diff --git a/cmd/crud.go b/cmd/crud.go
index 84307d4c..9db0e640 100644
--- a/cmd/crud.go
+++ b/cmd/crud.go
@@ -5,9 +5,9 @@ import (
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/pathutils"
+ v3 "github.com/unionj-cloud/toolkit/protobuf/v3"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
var dbDriver string
diff --git a/cmd/ddl.go b/cmd/ddl.go
index 8d96a714..944dc98c 100644
--- a/cmd/ddl.go
+++ b/cmd/ddl.go
@@ -5,10 +5,10 @@ import (
"github.com/spf13/cobra"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/config"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/dotenv"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/envconfig"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/yaml"
+ "github.com/unionj-cloud/toolkit/dotenv"
+ "github.com/unionj-cloud/toolkit/envconfig"
+ "github.com/unionj-cloud/toolkit/pathutils"
+ "github.com/unionj-cloud/toolkit/yaml"
)
var dir string
diff --git a/cmd/ddl_test.go b/cmd/ddl_test.go
index ef0346f4..4b4b37a3 100644
--- a/cmd/ddl_test.go
+++ b/cmd/ddl_test.go
@@ -7,7 +7,7 @@ import (
"github.com/spf13/cobra"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
+ "github.com/unionj-cloud/toolkit/pathutils"
)
var testDir string
diff --git a/cmd/grpc.go b/cmd/grpc.go
index 8efad690..3495556d 100644
--- a/cmd/grpc.go
+++ b/cmd/grpc.go
@@ -3,7 +3,7 @@ package cmd
import (
"github.com/iancoleman/strcase"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
+ v3 "github.com/unionj-cloud/toolkit/protobuf/v3"
"github.com/spf13/cobra"
)
diff --git a/cmd/init.go b/cmd/init.go
index b0476261..ca63bc1d 100644
--- a/cmd/init.go
+++ b/cmd/init.go
@@ -5,8 +5,8 @@ import (
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
+ "github.com/unionj-cloud/toolkit/pathutils"
+ v3 "github.com/unionj-cloud/toolkit/protobuf/v3"
)
var modName string
diff --git a/cmd/internal/ddl/codegen/dao.go b/cmd/internal/ddl/codegen/dao.go
index 72cf23e2..56acc85f 100644
--- a/cmd/internal/ddl/codegen/dao.go
+++ b/cmd/internal/ddl/codegen/dao.go
@@ -10,7 +10,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/table"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
@@ -22,12 +22,12 @@ import (
"fmt"
"github.com/pkg/errors"
"{{.EntityPackage}}"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sqlext/query"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sqlext/wrapper"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/reflectutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/templateutils"
+ "github.com/unionj-cloud/toolkit/caller"
+ "github.com/unionj-cloud/toolkit/sqlext/query"
+ "github.com/unionj-cloud/toolkit/sqlext/wrapper"
+ "github.com/unionj-cloud/toolkit/reflectutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/templateutils"
"strings"
"math"
"time"
diff --git a/cmd/internal/ddl/codegen/daosql.go b/cmd/internal/ddl/codegen/daosql.go
index eec34afb..9e8b4b9b 100644
--- a/cmd/internal/ddl/codegen/daosql.go
+++ b/cmd/internal/ddl/codegen/daosql.go
@@ -10,7 +10,7 @@ import (
"github.com/iancoleman/strcase"
log "github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/table"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/ddl/codegen/dto.go b/cmd/internal/ddl/codegen/dto.go
index fe5c07f2..370712c9 100644
--- a/cmd/internal/ddl/codegen/dto.go
+++ b/cmd/internal/ddl/codegen/dto.go
@@ -9,7 +9,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/ddl/codegen/entity.go b/cmd/internal/ddl/codegen/entity.go
index d9f3e43e..7761eaee 100644
--- a/cmd/internal/ddl/codegen/entity.go
+++ b/cmd/internal/ddl/codegen/entity.go
@@ -7,8 +7,8 @@ import (
log "github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/templateutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/templateutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/ddl/codegen/idao.go b/cmd/internal/ddl/codegen/idao.go
index 1fc539bb..a1a0cf9f 100644
--- a/cmd/internal/ddl/codegen/idao.go
+++ b/cmd/internal/ddl/codegen/idao.go
@@ -9,7 +9,7 @@ import (
log "github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/table"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
@@ -17,7 +17,7 @@ var idaoTmpl = templates.EditableHeaderTmpl + `package dao
import (
"context"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sqlext/query"
+ "github.com/unionj-cloud/toolkit/sqlext/query"
"{{.EntityPackage}}"
)
diff --git a/cmd/internal/ddl/ddlast/ast.go b/cmd/internal/ddl/ddlast/ast.go
index 9da5f88d..a6da612a 100644
--- a/cmd/internal/ddl/ddlast/ast.go
+++ b/cmd/internal/ddl/ddlast/ast.go
@@ -4,8 +4,8 @@ import (
"sort"
"strings"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/sliceutils"
)
type sortableFieldMeta []astutils.FieldMeta
diff --git a/cmd/internal/ddl/exec.go b/cmd/internal/ddl/exec.go
index ffabf2a7..da1cd30a 100644
--- a/cmd/internal/ddl/exec.go
+++ b/cmd/internal/ddl/exec.go
@@ -10,8 +10,8 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/caller"
// here must import mysql
_ "github.com/go-sql-driver/mysql"
diff --git a/cmd/internal/ddl/table/ddl.go b/cmd/internal/ddl/table/ddl.go
index c20533ec..f751f28e 100644
--- a/cmd/internal/ddl/table/ddl.go
+++ b/cmd/internal/ddl/table/ddl.go
@@ -24,14 +24,14 @@ import (
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/ddlast"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/extraenum"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/sortenum"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/envconfig"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sqlext/wrapper"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/caller"
+ "github.com/unionj-cloud/toolkit/envconfig"
+ "github.com/unionj-cloud/toolkit/pathutils"
+ "github.com/unionj-cloud/toolkit/sliceutils"
+ "github.com/unionj-cloud/toolkit/sqlext/wrapper"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/zlogger"
)
// CreateTable create table from Table
diff --git a/cmd/internal/ddl/table/ddl_test.go b/cmd/internal/ddl/table/ddl_test.go
index cb257ba9..04a5e218 100644
--- a/cmd/internal/ddl/table/ddl_test.go
+++ b/cmd/internal/ddl/table/ddl_test.go
@@ -12,7 +12,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/columnenum"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/sortenum"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sqlext/wrapper"
+ "github.com/unionj-cloud/toolkit/sqlext/wrapper"
)
func ExampleCreateTable() {
diff --git a/cmd/internal/ddl/table/table.go b/cmd/internal/ddl/table/table.go
index fe321935..64c75ffb 100644
--- a/cmd/internal/ddl/table/table.go
+++ b/cmd/internal/ddl/table/table.go
@@ -14,9 +14,9 @@ import (
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/keyenum"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/nullenum"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/sortenum"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/templateutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/templateutils"
)
const (
diff --git a/cmd/internal/ddl/table/table_test.go b/cmd/internal/ddl/table/table_test.go
index 036781ce..ee144eb6 100644
--- a/cmd/internal/ddl/table/table_test.go
+++ b/cmd/internal/ddl/table/table_test.go
@@ -15,8 +15,8 @@ import (
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/keyenum"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/nullenum"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/ddl/sortenum"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/pathutils"
)
func ExampleNewTableFromStruct() {
diff --git a/cmd/internal/enum/generator.go b/cmd/internal/enum/generator.go
index a38e122a..5187d07d 100644
--- a/cmd/internal/enum/generator.go
+++ b/cmd/internal/enum/generator.go
@@ -10,8 +10,8 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/modular/work.go b/cmd/internal/modular/work.go
index 926d251f..4c6fc9a0 100644
--- a/cmd/internal/modular/work.go
+++ b/cmd/internal/modular/work.go
@@ -7,9 +7,9 @@ import (
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/common"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/executils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/common"
+ "github.com/unionj-cloud/toolkit/executils"
+ "github.com/unionj-cloud/toolkit/stringutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/name/exec.go b/cmd/internal/name/exec.go
index a67e26a7..fe88110b 100644
--- a/cmd/internal/name/exec.go
+++ b/cmd/internal/name/exec.go
@@ -3,8 +3,8 @@ package name
import (
"github.com/iancoleman/strcase"
"github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
// Name wraps config properties for name command
diff --git a/cmd/internal/name/exec_test.go b/cmd/internal/name/exec_test.go
index 82efc583..3fc66a7a 100644
--- a/cmd/internal/name/exec_test.go
+++ b/cmd/internal/name/exec_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
+ "github.com/unionj-cloud/toolkit/pathutils"
)
const initCode = `package testdata
diff --git a/cmd/internal/openapi/v3/codegen/client/go.go b/cmd/internal/openapi/v3/codegen/client/go.go
index 3030a405..7d5ce16d 100644
--- a/cmd/internal/openapi/v3/codegen/client/go.go
+++ b/cmd/internal/openapi/v3/codegen/client/go.go
@@ -8,7 +8,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/openapi/v3/codegen"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
var dtoTmpl = templates.EditableHeaderTmpl + `package {{.Pkg}}
diff --git a/cmd/internal/openapi/v3/codegen/common.go b/cmd/internal/openapi/v3/codegen/common.go
index b4ee1ef8..1562779f 100644
--- a/cmd/internal/openapi/v3/codegen/common.go
+++ b/cmd/internal/openapi/v3/codegen/common.go
@@ -14,11 +14,11 @@ import (
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/copier"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
+ "github.com/unionj-cloud/toolkit/sliceutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
@@ -666,10 +666,10 @@ import (
"github.com/opentracing/opentracing-go"
"github.com/unionj-cloud/go-doudou/v2/framework/registry"
_querystring "github.com/google/go-querystring/query"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/fileutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/fileutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
"github.com/unionj-cloud/go-doudou/v2/framework/restclient"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
"io"
"mime/multipart"
"net/url"
diff --git a/cmd/internal/openapi/v3/codegen/common_test.go b/cmd/internal/openapi/v3/codegen/common_test.go
index bdb90740..310fbb8e 100644
--- a/cmd/internal/openapi/v3/codegen/common_test.go
+++ b/cmd/internal/openapi/v3/codegen/common_test.go
@@ -7,8 +7,8 @@ import (
"testing"
"github.com/stretchr/testify/assert"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
+ "github.com/unionj-cloud/toolkit/pathutils"
)
func TestPattern2Method(t *testing.T) {
diff --git a/cmd/internal/openapi/v3/codegen/server.go b/cmd/internal/openapi/v3/codegen/server.go
index c92cd8af..0d23d2b5 100644
--- a/cmd/internal/openapi/v3/codegen/server.go
+++ b/cmd/internal/openapi/v3/codegen/server.go
@@ -12,10 +12,10 @@ import (
"github.com/iancoleman/strcase"
"github.com/pkg/errors"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
+ "github.com/unionj-cloud/toolkit/sliceutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
"github.com/unionj-cloud/go-doudou/v2/version"
"golang.org/x/text/cases"
"golang.org/x/text/language"
@@ -183,7 +183,7 @@ var svcTmpl = templates.EditableHeaderTmpl + `package service
import (
"context"
"{{.DtoPackage}}"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
//go:generate go-doudou svc http
diff --git a/cmd/internal/openapi/v3/codegen/server/svc.go b/cmd/internal/openapi/v3/codegen/server/svc.go
index 0c64f872..220f8b60 100644
--- a/cmd/internal/openapi/v3/codegen/server/svc.go
+++ b/cmd/internal/openapi/v3/codegen/server/svc.go
@@ -9,8 +9,8 @@ import (
"github.com/iancoleman/strcase"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/openapi/v3/codegen"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/assert"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ "github.com/unionj-cloud/toolkit/assert"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
var dtoTmpl = templates.EditableHeaderTmpl + `package {{.Pkg}}
diff --git a/cmd/internal/openapi/v3/codegen/testdata/test/downloadclient.go b/cmd/internal/openapi/v3/codegen/testdata/test/downloadclient.go
index 43fe4b54..6e1d575e 100644
--- a/cmd/internal/openapi/v3/codegen/testdata/test/downloadclient.go
+++ b/cmd/internal/openapi/v3/codegen/testdata/test/downloadclient.go
@@ -20,8 +20,8 @@ import (
"github.com/pkg/errors"
"github.com/unionj-cloud/go-doudou/v2/framework/registry"
"github.com/unionj-cloud/go-doudou/v2/framework/restclient"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/fileutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/fileutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
type DownloadClient struct {
diff --git a/cmd/internal/openapi/v3/codegen/testdata/test/petclient.go b/cmd/internal/openapi/v3/codegen/testdata/test/petclient.go
index 8e95c8db..4c600426 100644
--- a/cmd/internal/openapi/v3/codegen/testdata/test/petclient.go
+++ b/cmd/internal/openapi/v3/codegen/testdata/test/petclient.go
@@ -17,7 +17,7 @@ import (
"github.com/pkg/errors"
"github.com/unionj-cloud/go-doudou/v2/framework/registry"
"github.com/unionj-cloud/go-doudou/v2/framework/restclient"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
type PetClient struct {
diff --git a/cmd/internal/openapi/v3/codegen/testdata/test/textclient.go b/cmd/internal/openapi/v3/codegen/testdata/test/textclient.go
index 73631596..3634760e 100644
--- a/cmd/internal/openapi/v3/codegen/testdata/test/textclient.go
+++ b/cmd/internal/openapi/v3/codegen/testdata/test/textclient.go
@@ -17,7 +17,7 @@ import (
"github.com/pkg/errors"
"github.com/unionj-cloud/go-doudou/v2/framework/registry"
"github.com/unionj-cloud/go-doudou/v2/framework/restclient"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
type TextClient struct {
diff --git a/cmd/internal/openapi/v3/codegen/testdata/test/uploadclient.go b/cmd/internal/openapi/v3/codegen/testdata/test/uploadclient.go
index 1f9e6ddf..44d12af4 100644
--- a/cmd/internal/openapi/v3/codegen/testdata/test/uploadclient.go
+++ b/cmd/internal/openapi/v3/codegen/testdata/test/uploadclient.go
@@ -17,7 +17,7 @@ import (
"github.com/pkg/errors"
"github.com/unionj-cloud/go-doudou/v2/framework/registry"
"github.com/unionj-cloud/go-doudou/v2/framework/restclient"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
type UploadClient struct {
diff --git a/cmd/internal/svc/codegen/config.go b/cmd/internal/svc/codegen/config.go
index aa01e095..7cb49abe 100644
--- a/cmd/internal/svc/codegen/config.go
+++ b/cmd/internal/svc/codegen/config.go
@@ -8,16 +8,16 @@ import (
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
var configTmpl = templates.EditableHeaderTmpl + `package config
import (
- "github.com/unionj-cloud/go-doudou/v2/toolkit/envconfig"
+ "github.com/unionj-cloud/toolkit/envconfig"
"github.com/unionj-cloud/go-doudou/v2/framework/config"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/zlogger"
)
var G_Config *Config
diff --git a/cmd/internal/svc/codegen/config_test.go b/cmd/internal/svc/codegen/config_test.go
index fdd6d688..579cc8f4 100644
--- a/cmd/internal/svc/codegen/config_test.go
+++ b/cmd/internal/svc/codegen/config_test.go
@@ -5,7 +5,7 @@ import (
"path/filepath"
"testing"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
)
const testDir = "testdata"
diff --git a/cmd/internal/svc/codegen/database/common.go b/cmd/internal/svc/codegen/database/common.go
index 4307c084..78a99ffc 100644
--- a/cmd/internal/svc/codegen/database/common.go
+++ b/cmd/internal/svc/codegen/database/common.go
@@ -4,10 +4,10 @@ import (
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/codegen"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/parser"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/validate"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/executils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/executils"
+ "github.com/wubin1989/gen"
+ v3 "github.com/unionj-cloud/toolkit/protobuf/v3"
"os"
"path/filepath"
)
diff --git a/cmd/internal/svc/codegen/database/gorm.go b/cmd/internal/svc/codegen/database/gorm.go
index 07cd8e15..df9df30d 100644
--- a/cmd/internal/svc/codegen/database/gorm.go
+++ b/cmd/internal/svc/codegen/database/gorm.go
@@ -1,17 +1,17 @@
package database
import (
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
"path/filepath"
"strings"
"github.com/gobwas/glob"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/errorx"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/executils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/errorx"
+ "github.com/unionj-cloud/toolkit/executils"
+ "github.com/wubin1989/gen"
+ "github.com/wubin1989/gen/field"
+ v3 "github.com/unionj-cloud/toolkit/protobuf/v3"
+ "github.com/unionj-cloud/toolkit/stringutils"
"github.com/wubin1989/gorm"
"github.com/wubin1989/mysql"
"github.com/wubin1989/postgres"
@@ -71,7 +71,7 @@ func (gg *GormGenerator) fix() {
// panic(err)
// }
// fileContent := string(source)
- // fileContent = strings.ReplaceAll(fileContent, "github.com/wubin1989/gen", "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen")
+ // fileContent = strings.ReplaceAll(fileContent, "github.com/wubin1989/gen", "github.com/wubin1989/gen")
// ioutil.WriteFile(file, []byte(fileContent), os.ModePerm)
//}
}
diff --git a/cmd/internal/svc/codegen/db.go b/cmd/internal/svc/codegen/db.go
index a3f58bfc..9ca81ff8 100644
--- a/cmd/internal/svc/codegen/db.go
+++ b/cmd/internal/svc/codegen/db.go
@@ -7,7 +7,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/svc/codegen/grpcannotation.go b/cmd/internal/svc/codegen/grpcannotation.go
index f3a4e161..538bb50f 100644
--- a/cmd/internal/svc/codegen/grpcannotation.go
+++ b/cmd/internal/svc/codegen/grpcannotation.go
@@ -10,7 +10,7 @@ import (
"github.com/unionj-cloud/go-doudou/v2/version"
"github.com/sirupsen/logrus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
)
var annotationTmpl = `/**
diff --git a/cmd/internal/svc/codegen/grpchttpmain.go b/cmd/internal/svc/codegen/grpchttpmain.go
index bdc827a1..66d52c89 100644
--- a/cmd/internal/svc/codegen/grpchttpmain.go
+++ b/cmd/internal/svc/codegen/grpchttpmain.go
@@ -8,8 +8,8 @@ import (
"text/template"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
+ "github.com/unionj-cloud/toolkit/astutils"
+ v3 "github.com/unionj-cloud/toolkit/protobuf/v3"
"github.com/unionj-cloud/go-doudou/v2/version"
)
@@ -24,7 +24,7 @@ import (
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/tags"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/zlogger"
"google.golang.org/grpc"
"github.com/unionj-cloud/go-doudou/v2/framework/grpcx"
{{.ServiceAlias}} "{{.ServicePackage}}"
diff --git a/cmd/internal/svc/codegen/grpcmain.go b/cmd/internal/svc/codegen/grpcmain.go
index 04c22c3f..5bc53c08 100644
--- a/cmd/internal/svc/codegen/grpcmain.go
+++ b/cmd/internal/svc/codegen/grpcmain.go
@@ -11,10 +11,10 @@ import (
"text/template"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/fileutils"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/constants"
+ "github.com/unionj-cloud/toolkit/fileutils"
+ v3 "github.com/unionj-cloud/toolkit/protobuf/v3"
"github.com/unionj-cloud/go-doudou/v2/version"
)
@@ -84,7 +84,7 @@ var gRPCImportBlock = `
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/tags"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/zlogger"
"google.golang.org/grpc"
"github.com/unionj-cloud/go-doudou/v2/framework/grpcx"
{{.ServiceAlias}} "{{.ServicePackage}}"
diff --git a/cmd/internal/svc/codegen/grpcmod.go b/cmd/internal/svc/codegen/grpcmod.go
index 8875cd7c..99963c5e 100644
--- a/cmd/internal/svc/codegen/grpcmod.go
+++ b/cmd/internal/svc/codegen/grpcmod.go
@@ -6,8 +6,8 @@ import (
"path/filepath"
"regexp"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/fileutils"
+ "github.com/unionj-cloud/toolkit/constants"
+ "github.com/unionj-cloud/toolkit/fileutils"
)
var goModFixGrpc = `
diff --git a/cmd/internal/svc/codegen/grpcproto.go b/cmd/internal/svc/codegen/grpcproto.go
index b712128a..00bfd013 100644
--- a/cmd/internal/svc/codegen/grpcproto.go
+++ b/cmd/internal/svc/codegen/grpcproto.go
@@ -2,6 +2,7 @@ package codegen
import (
"fmt"
+ "github.com/unionj-cloud/go-doudou/v2/version"
"os"
"path/filepath"
"sort"
@@ -9,9 +10,9 @@ import (
"text/template"
"github.com/sirupsen/logrus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- protov3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/templateutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ protov3 "github.com/unionj-cloud/toolkit/protobuf/v3"
+ "github.com/unionj-cloud/toolkit/templateutils"
)
var protoTmpl = `/**
@@ -103,7 +104,7 @@ func GenGrpcProto(dir string, ic astutils.InterfaceCollector, p protov3.ProtoGen
}
defer f.Close()
servicePkg := astutils.GetPkgPath(dir)
- service = p.NewService(svcname, servicePkg+"/transport/grpc")
+ service = p.NewService(svcname, servicePkg+"/transport/grpc", version.Release)
service.Comments = ic.Interfaces[0].Comments
for _, method := range ic.Interfaces[0].Methods {
rpc := p.NewRpc(method)
diff --git a/cmd/internal/svc/codegen/http.go b/cmd/internal/svc/codegen/http.go
index 9e11819f..d03884a1 100644
--- a/cmd/internal/svc/codegen/http.go
+++ b/cmd/internal/svc/codegen/http.go
@@ -2,7 +2,7 @@ package codegen
import (
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/parser"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
)
func genHttp(dir string, ic astutils.InterfaceCollector, caseConvertor func(string) string) {
diff --git a/cmd/internal/svc/codegen/http2grpc.go b/cmd/internal/svc/codegen/http2grpc.go
index 1190a88d..a9aa3a27 100644
--- a/cmd/internal/svc/codegen/http2grpc.go
+++ b/cmd/internal/svc/codegen/http2grpc.go
@@ -11,8 +11,8 @@ import (
"github.com/iancoleman/strcase"
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/copier"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/svc/codegen/httpclient.go b/cmd/internal/svc/codegen/httpclient.go
index 627c5406..3fbce0b1 100644
--- a/cmd/internal/svc/codegen/httpclient.go
+++ b/cmd/internal/svc/codegen/httpclient.go
@@ -10,9 +10,9 @@ import (
"github.com/iancoleman/strcase"
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/parser"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
- v3helper "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/copier"
+ v3helper "github.com/unionj-cloud/toolkit/openapi/v3"
"github.com/unionj-cloud/go-doudou/v2/version"
)
@@ -28,11 +28,11 @@ import (
"github.com/go-resty/resty/v2"
"github.com/pkg/errors"
"github.com/klauspost/compress/gzip"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/fileutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/fileutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
"github.com/unionj-cloud/go-doudou/v2/framework/registry"
"github.com/unionj-cloud/go-doudou/v2/framework/restclient"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/opentracing/opentracing-go"
"io"
diff --git a/cmd/internal/svc/codegen/httpclient_test.go b/cmd/internal/svc/codegen/httpclient_test.go
index b653d73a..284b50d1 100644
--- a/cmd/internal/svc/codegen/httpclient_test.go
+++ b/cmd/internal/svc/codegen/httpclient_test.go
@@ -8,7 +8,7 @@ import (
"github.com/iancoleman/strcase"
"github.com/pkg/errors"
. "github.com/smartystreets/goconvey/convey"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
)
func TestGenGoClient(t *testing.T) {
diff --git a/cmd/internal/svc/codegen/httpclientproxy.go b/cmd/internal/svc/codegen/httpclientproxy.go
index 6aa832eb..febae7cf 100644
--- a/cmd/internal/svc/codegen/httpclientproxy.go
+++ b/cmd/internal/svc/codegen/httpclientproxy.go
@@ -11,10 +11,10 @@ import (
"text/template"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
- v3helper "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/copier"
+ v3helper "github.com/unionj-cloud/toolkit/openapi/v3"
+ "github.com/unionj-cloud/toolkit/zlogger"
"github.com/unionj-cloud/go-doudou/v2/version"
)
@@ -23,7 +23,7 @@ var cpimportTmpl = `
"github.com/pkg/errors"
"github.com/prometheus/client_golang/prometheus"
"github.com/rs/zerolog"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/zlogger"
"github.com/slok/goresilience"
"github.com/go-resty/resty/v2"
"github.com/slok/goresilience/circuitbreaker"
@@ -31,7 +31,7 @@ var cpimportTmpl = `
"github.com/slok/goresilience/metrics"
"github.com/slok/goresilience/retry"
"github.com/slok/goresilience/timeout"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
"os"
"time"
`
diff --git a/cmd/internal/svc/codegen/httpclientproxy_test.go b/cmd/internal/svc/codegen/httpclientproxy_test.go
index 7410749f..50dfe3b7 100644
--- a/cmd/internal/svc/codegen/httpclientproxy_test.go
+++ b/cmd/internal/svc/codegen/httpclientproxy_test.go
@@ -5,7 +5,7 @@ import (
"path/filepath"
"testing"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
)
func TestGenGoClientProxy1(t *testing.T) {
diff --git a/cmd/internal/svc/codegen/httphandler.go b/cmd/internal/svc/codegen/httphandler.go
index 6e6df45f..bb4db8b8 100644
--- a/cmd/internal/svc/codegen/httphandler.go
+++ b/cmd/internal/svc/codegen/httphandler.go
@@ -11,7 +11,7 @@ import (
"github.com/unionj-cloud/go-doudou/v2/version"
"github.com/sirupsen/logrus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
)
var httpHandlerTmpl = `/**
diff --git a/cmd/internal/svc/codegen/httphandlerimpl.go b/cmd/internal/svc/codegen/httphandlerimpl.go
index 07086513..6eb669fa 100644
--- a/cmd/internal/svc/codegen/httphandlerimpl.go
+++ b/cmd/internal/svc/codegen/httphandlerimpl.go
@@ -11,9 +11,9 @@ import (
"github.com/iancoleman/strcase"
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
- v3helper "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/copier"
+ v3helper "github.com/unionj-cloud/toolkit/openapi/v3"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/svc/codegen/httphandlerimpl_test.go b/cmd/internal/svc/codegen/httphandlerimpl_test.go
index 018b976a..0ff95389 100644
--- a/cmd/internal/svc/codegen/httphandlerimpl_test.go
+++ b/cmd/internal/svc/codegen/httphandlerimpl_test.go
@@ -5,8 +5,8 @@ import (
"path/filepath"
"testing"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/copier"
)
func Test_unimplementedMethods(t *testing.T) {
diff --git a/cmd/internal/svc/codegen/httpiclient.go b/cmd/internal/svc/codegen/httpiclient.go
index db3f2120..031c88d2 100644
--- a/cmd/internal/svc/codegen/httpiclient.go
+++ b/cmd/internal/svc/codegen/httpiclient.go
@@ -8,8 +8,8 @@ import (
"text/template"
"github.com/sirupsen/logrus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/copier"
"github.com/unionj-cloud/go-doudou/v2/version"
)
@@ -22,7 +22,7 @@ package client
import (
"context"
"github.com/go-resty/resty/v2"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
"os"
)
diff --git a/cmd/internal/svc/codegen/httpiclient_test.go b/cmd/internal/svc/codegen/httpiclient_test.go
index 5c8807ac..947976de 100644
--- a/cmd/internal/svc/codegen/httpiclient_test.go
+++ b/cmd/internal/svc/codegen/httpiclient_test.go
@@ -4,7 +4,7 @@ import (
"path/filepath"
"testing"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
)
func TestGenGoIClient(t *testing.T) {
diff --git a/cmd/internal/svc/codegen/httpintegrationtesting.go b/cmd/internal/svc/codegen/httpintegrationtesting.go
index f29e53fc..ff8e17a5 100644
--- a/cmd/internal/svc/codegen/httpintegrationtesting.go
+++ b/cmd/internal/svc/codegen/httpintegrationtesting.go
@@ -14,8 +14,8 @@ import (
"github.com/rbretecher/go-postman-collection"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/zlogger"
"github.com/unionj-cloud/go-doudou/v2/version"
)
@@ -73,7 +73,7 @@ var integrationTestingImportTmpl = `
"github.com/gorilla/mux"
"github.com/joho/godotenv"
"github.com/rs/zerolog"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/zlogger"
"github.com/steinfletcher/apitest"
jsonpath "github.com/steinfletcher/apitest-jsonpath"
"net/http"
diff --git a/cmd/internal/svc/codegen/httpintegrationtesting_test.go b/cmd/internal/svc/codegen/httpintegrationtesting_test.go
index 646783b5..62d91709 100644
--- a/cmd/internal/svc/codegen/httpintegrationtesting_test.go
+++ b/cmd/internal/svc/codegen/httpintegrationtesting_test.go
@@ -5,7 +5,7 @@ import (
"path/filepath"
"testing"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/fileutils"
+ "github.com/unionj-cloud/toolkit/fileutils"
)
func Test_notGenerated(t *testing.T) {
diff --git a/cmd/internal/svc/codegen/init.go b/cmd/internal/svc/codegen/init.go
index bd08dd94..013437a3 100644
--- a/cmd/internal/svc/codegen/init.go
+++ b/cmd/internal/svc/codegen/init.go
@@ -13,11 +13,11 @@ import (
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/parser"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/common"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/executils"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/common"
+ "github.com/unionj-cloud/toolkit/executils"
+ v3 "github.com/unionj-cloud/toolkit/protobuf/v3"
+ "github.com/unionj-cloud/toolkit/stringutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/svc/codegen/init_test.go b/cmd/internal/svc/codegen/init_test.go
index fc9d8dea..397fabe4 100644
--- a/cmd/internal/svc/codegen/init_test.go
+++ b/cmd/internal/svc/codegen/init_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"github.com/stretchr/testify/assert"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/executils"
+ "github.com/unionj-cloud/toolkit/executils"
)
func TestInitProj(t *testing.T) {
diff --git a/cmd/internal/svc/codegen/k8sdeployment_test.go b/cmd/internal/svc/codegen/k8sdeployment_test.go
index efc12dc8..713f0c72 100644
--- a/cmd/internal/svc/codegen/k8sdeployment_test.go
+++ b/cmd/internal/svc/codegen/k8sdeployment_test.go
@@ -6,7 +6,7 @@ import (
"path/filepath"
"testing"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
+ "github.com/unionj-cloud/toolkit/pathutils"
)
func TestModifyVersion(t *testing.T) {
diff --git a/cmd/internal/svc/codegen/k8sstatefulset_test.go b/cmd/internal/svc/codegen/k8sstatefulset_test.go
index e5c48fc2..b64f5b9f 100644
--- a/cmd/internal/svc/codegen/k8sstatefulset_test.go
+++ b/cmd/internal/svc/codegen/k8sstatefulset_test.go
@@ -5,7 +5,7 @@ import (
"path/filepath"
"testing"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
+ "github.com/unionj-cloud/toolkit/pathutils"
)
func TestGenK8sStatefulset(t *testing.T) {
diff --git a/cmd/internal/svc/codegen/main.go b/cmd/internal/svc/codegen/main.go
index dca306f7..3b501dd9 100644
--- a/cmd/internal/svc/codegen/main.go
+++ b/cmd/internal/svc/codegen/main.go
@@ -7,7 +7,7 @@ import (
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/svc/codegen/main_test.go b/cmd/internal/svc/codegen/main_test.go
index d9b79be5..0a3ba302 100644
--- a/cmd/internal/svc/codegen/main_test.go
+++ b/cmd/internal/svc/codegen/main_test.go
@@ -7,7 +7,7 @@ import (
"github.com/pkg/errors"
. "github.com/smartystreets/goconvey/convey"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
)
func TestGenMainPanic_Stat(t *testing.T) {
diff --git a/cmd/internal/svc/codegen/mainmodule.go b/cmd/internal/svc/codegen/mainmodule.go
index c37e0977..063ad52d 100644
--- a/cmd/internal/svc/codegen/mainmodule.go
+++ b/cmd/internal/svc/codegen/mainmodule.go
@@ -8,7 +8,7 @@ import (
"text/template"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/svc/codegen/plugin.go b/cmd/internal/svc/codegen/plugin.go
index f8198abb..bb4fa61e 100644
--- a/cmd/internal/svc/codegen/plugin.go
+++ b/cmd/internal/svc/codegen/plugin.go
@@ -8,7 +8,7 @@ import (
"text/template"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/svc/codegen/svcimpl.go b/cmd/internal/svc/codegen/svcimpl.go
index 691d993d..29959046 100644
--- a/cmd/internal/svc/codegen/svcimpl.go
+++ b/cmd/internal/svc/codegen/svcimpl.go
@@ -10,9 +10,9 @@ import (
"github.com/iancoleman/strcase"
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/templates"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/copier"
+ v3 "github.com/unionj-cloud/toolkit/protobuf/v3"
"github.com/unionj-cloud/go-doudou/v2/version"
)
diff --git a/cmd/internal/svc/codegen/testdata/client/clientproxy.go b/cmd/internal/svc/codegen/testdata/client/clientproxy.go
index 58049d4b..6281f93a 100644
--- a/cmd/internal/svc/codegen/testdata/client/clientproxy.go
+++ b/cmd/internal/svc/codegen/testdata/client/clientproxy.go
@@ -17,7 +17,7 @@ import (
"github.com/slok/goresilience/metrics"
"github.com/slok/goresilience/retry"
"github.com/slok/goresilience/timeout"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
type UsersvcClientProxy struct {
diff --git a/cmd/internal/svc/codegen/testdata/client/iclient.go b/cmd/internal/svc/codegen/testdata/client/iclient.go
index e07e5447..d1af808c 100644
--- a/cmd/internal/svc/codegen/testdata/client/iclient.go
+++ b/cmd/internal/svc/codegen/testdata/client/iclient.go
@@ -9,7 +9,7 @@ import (
"github.com/go-resty/resty/v2"
"testdata/vo"
"testdata/dto"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
"os"
)
diff --git a/cmd/internal/svc/codegen/testdata/config/config.go b/cmd/internal/svc/codegen/testdata/config/config.go
index 1c75585e..cefc3354 100644
--- a/cmd/internal/svc/codegen/testdata/config/config.go
+++ b/cmd/internal/svc/codegen/testdata/config/config.go
@@ -2,7 +2,7 @@ package config
import (
"github.com/sirupsen/logrus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/envconfig"
+ "github.com/unionj-cloud/toolkit/envconfig"
)
type Config struct {
diff --git a/cmd/internal/svc/codegen/testdata/svc.go b/cmd/internal/svc/codegen/testdata/svc.go
index 32238697..8fa9e421 100644
--- a/cmd/internal/svc/codegen/testdata/svc.go
+++ b/cmd/internal/svc/codegen/testdata/svc.go
@@ -7,7 +7,7 @@ import (
"testdata/vo"
"github.com/shopspring/decimal"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
// 用户服务接口
diff --git a/cmd/internal/svc/codegen/testdata/svcimpl.go b/cmd/internal/svc/codegen/testdata/svcimpl.go
index 8cff2178..c80cee3f 100644
--- a/cmd/internal/svc/codegen/testdata/svcimpl.go
+++ b/cmd/internal/svc/codegen/testdata/svcimpl.go
@@ -9,7 +9,7 @@ import (
"github.com/brianvoe/gofakeit/v6"
"github.com/jmoiron/sqlx"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
type UsersvcImpl struct {
diff --git a/cmd/internal/svc/codegen/testdata/svcp.go b/cmd/internal/svc/codegen/testdata/svcp.go
index 11332882..a8c244c3 100644
--- a/cmd/internal/svc/codegen/testdata/svcp.go
+++ b/cmd/internal/svc/codegen/testdata/svcp.go
@@ -5,7 +5,7 @@ import (
"os"
"testdata/vo"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
// 用户服务接口
diff --git a/cmd/internal/svc/codegen/testdata/transport/httpsrv/handlerimpl.go b/cmd/internal/svc/codegen/testdata/transport/httpsrv/handlerimpl.go
index 78fcd45d..f2cc39ab 100644
--- a/cmd/internal/svc/codegen/testdata/transport/httpsrv/handlerimpl.go
+++ b/cmd/internal/svc/codegen/testdata/transport/httpsrv/handlerimpl.go
@@ -17,8 +17,8 @@ import (
"github.com/pkg/errors"
"github.com/unionj-cloud/go-doudou/v2/framework/rest"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ "github.com/unionj-cloud/toolkit/cast"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
type UsersvcHandlerImpl struct {
diff --git a/cmd/internal/svc/parser/parser.go b/cmd/internal/svc/parser/parser.go
index 5fc56446..210c4cf2 100644
--- a/cmd/internal/svc/parser/parser.go
+++ b/cmd/internal/svc/parser/parser.go
@@ -19,11 +19,11 @@ import (
"github.com/iancoleman/strcase"
"github.com/samber/lo"
"github.com/sirupsen/logrus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
- protov3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/constants"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
+ protov3 "github.com/unionj-cloud/toolkit/protobuf/v3"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
var DEFAULT_DTO_PKGS = []string{"vo", "dto", "model"}
diff --git a/cmd/internal/svc/svc.go b/cmd/internal/svc/svc.go
index 6a317248..aa7c9e96 100644
--- a/cmd/internal/svc/svc.go
+++ b/cmd/internal/svc/svc.go
@@ -18,11 +18,11 @@ import (
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/codegen/database"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/parser"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/validate"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/assert"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/executils"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/assert"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/executils"
+ v3 "github.com/unionj-cloud/toolkit/protobuf/v3"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
//go:generate mockgen -destination ../../mock/mock_svc.go -package mock -source=./svc.go
diff --git a/cmd/internal/svc/svc_test.go b/cmd/internal/svc/svc_test.go
index 3f425ac7..79110816 100644
--- a/cmd/internal/svc/svc_test.go
+++ b/cmd/internal/svc/svc_test.go
@@ -5,7 +5,7 @@ import (
"github.com/iancoleman/strcase"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/codegen"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/parser"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
+ v3 "github.com/unionj-cloud/toolkit/protobuf/v3"
"os"
"os/exec"
"path/filepath"
@@ -16,9 +16,9 @@ import (
"github.com/stretchr/testify/assert"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/validate"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/executils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/executils"
+ "github.com/unionj-cloud/toolkit/pathutils"
)
var testDir string
diff --git a/cmd/internal/svc/testdata/checkIc2/svc.go b/cmd/internal/svc/testdata/checkIc2/svc.go
index 2b22d294..7df77e4a 100644
--- a/cmd/internal/svc/testdata/checkIc2/svc.go
+++ b/cmd/internal/svc/testdata/checkIc2/svc.go
@@ -4,7 +4,7 @@ import (
"context"
"mime/multipart"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
// 用户服务接口
diff --git a/cmd/internal/svc/testdata/novo/svc.go b/cmd/internal/svc/testdata/novo/svc.go
index 372516ba..b61d9144 100644
--- a/cmd/internal/svc/testdata/novo/svc.go
+++ b/cmd/internal/svc/testdata/novo/svc.go
@@ -6,7 +6,7 @@ import (
"os"
"github.com/unionj-cloud/go-doudou/v2/framework/testdata/vo"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
// 用户服务接口
diff --git a/cmd/internal/svc/testdata/svc.go b/cmd/internal/svc/testdata/svc.go
index 7ea67f8f..fda7138e 100644
--- a/cmd/internal/svc/testdata/svc.go
+++ b/cmd/internal/svc/testdata/svc.go
@@ -6,7 +6,7 @@ import (
"os"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/testdata/vo"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
// 用户服务接口
diff --git a/cmd/internal/svc/testdata/svcp.go b/cmd/internal/svc/testdata/svcp.go
index 12475a5e..8894d8bf 100644
--- a/cmd/internal/svc/testdata/svcp.go
+++ b/cmd/internal/svc/testdata/svcp.go
@@ -5,7 +5,7 @@ import (
"os"
"github.com/unionj-cloud/go-doudou/v2/framework/testdata/vo"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
// 用户服务接口
diff --git a/cmd/internal/svc/validate/validate.go b/cmd/internal/svc/validate/validate.go
index 4d7c9126..84398bd3 100644
--- a/cmd/internal/svc/validate/validate.go
+++ b/cmd/internal/svc/validate/validate.go
@@ -9,9 +9,9 @@ import (
"github.com/pkg/errors"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/svc/parser"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- v3helper "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
+ "github.com/unionj-cloud/toolkit/astutils"
+ v3helper "github.com/unionj-cloud/toolkit/openapi/v3"
+ "github.com/unionj-cloud/toolkit/sliceutils"
)
func DataType(dir string, dtoDirs ...string) {
diff --git a/cmd/internal/templates/mainmain.go b/cmd/internal/templates/mainmain.go
index c02aa54b..25bf2f44 100644
--- a/cmd/internal/templates/mainmain.go
+++ b/cmd/internal/templates/mainmain.go
@@ -14,8 +14,8 @@ import (
"github.com/unionj-cloud/go-doudou/v2/framework/grpcx"
"github.com/unionj-cloud/go-doudou/v2/framework/plugin"
"github.com/unionj-cloud/go-doudou/v2/framework/rest"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pipeconn"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/pipeconn"
+ "github.com/unionj-cloud/toolkit/zlogger"
"google.golang.org/grpc"
)
diff --git a/cmd/internal/templates/mainmodule.go b/cmd/internal/templates/mainmodule.go
index e923f29b..9298a838 100644
--- a/cmd/internal/templates/mainmodule.go
+++ b/cmd/internal/templates/mainmodule.go
@@ -8,7 +8,7 @@ import (
{{- end }}
"github.com/unionj-cloud/go-doudou/v2/framework/plugin"
"github.com/unionj-cloud/go-doudou/v2/framework/rest"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/zlogger"
_ "{{.PluginPackage}}"
)
diff --git a/cmd/internal/templates/plugin.go b/cmd/internal/templates/plugin.go
index 69b3cf03..edf5f162 100644
--- a/cmd/internal/templates/plugin.go
+++ b/cmd/internal/templates/plugin.go
@@ -6,8 +6,8 @@ import (
"github.com/unionj-cloud/go-doudou/v2/framework/grpcx"
"github.com/unionj-cloud/go-doudou/v2/framework/plugin"
"github.com/unionj-cloud/go-doudou/v2/framework/rest"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pipeconn"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/pipeconn"
+ "github.com/unionj-cloud/toolkit/stringutils"
{{.ServiceAlias}} "{{.ServicePackage}}"
"{{.ConfigPackage}}"
"{{.TransportHttpPackage}}"
@@ -23,7 +23,7 @@ import (
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging"
"github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/tags"
grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/zlogger"
{{- end }}
)
diff --git a/cmd/modular/init.go b/cmd/modular/init.go
index d0046c09..0dd3035c 100644
--- a/cmd/modular/init.go
+++ b/cmd/modular/init.go
@@ -4,8 +4,8 @@ import (
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
"github.com/unionj-cloud/go-doudou/v2/cmd/internal/modular"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/executils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
+ "github.com/unionj-cloud/toolkit/executils"
+ "github.com/unionj-cloud/toolkit/pathutils"
)
// initCmd initializes the service
diff --git a/cmd/name_test.go b/cmd/name_test.go
index 5f2bad00..e32958e3 100644
--- a/cmd/name_test.go
+++ b/cmd/name_test.go
@@ -6,7 +6,7 @@ import (
"testing"
"github.com/unionj-cloud/go-doudou/v2/cmd"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
+ "github.com/unionj-cloud/toolkit/astutils"
)
func TestNameCmd(t *testing.T) {
diff --git a/cmd/testdata/testsvc/config/config.go b/cmd/testdata/testsvc/config/config.go
index 1c75585e..cefc3354 100644
--- a/cmd/testdata/testsvc/config/config.go
+++ b/cmd/testdata/testsvc/config/config.go
@@ -2,7 +2,7 @@ package config
import (
"github.com/sirupsen/logrus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/envconfig"
+ "github.com/unionj-cloud/toolkit/envconfig"
)
type Config struct {
diff --git a/framework/cache/2qcache.go b/framework/cache/2qcache.go
index 725266a2..c7684236 100644
--- a/framework/cache/2qcache.go
+++ b/framework/cache/2qcache.go
@@ -5,7 +5,7 @@ import (
lru "github.com/hashicorp/golang-lru"
"github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
+ "github.com/unionj-cloud/toolkit/caller"
)
type TwoQueueCache struct {
diff --git a/framework/cache/arccache.go b/framework/cache/arccache.go
index 65dddbeb..c6c52573 100644
--- a/framework/cache/arccache.go
+++ b/framework/cache/arccache.go
@@ -5,7 +5,7 @@ import (
lru "github.com/hashicorp/golang-lru"
"github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
+ "github.com/unionj-cloud/toolkit/caller"
)
type ARCCache struct {
diff --git a/framework/cache/cachemanager.go b/framework/cache/cachemanager.go
index 5902ea4c..d1ca78cf 100644
--- a/framework/cache/cachemanager.go
+++ b/framework/cache/cachemanager.go
@@ -8,17 +8,17 @@ import (
"github.com/dgraph-io/ristretto"
go_cache "github.com/patrickmn/go-cache"
"github.com/redis/go-redis/v9"
- gocache "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/cache"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/metrics"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- go_cache_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/store/go_cache"
- redis_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/store/redis"
- ristretto_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/store/ristretto"
+ gocache "github.com/unionj-cloud/toolkit/gocache/lib/cache"
+ "github.com/unionj-cloud/toolkit/gocache/lib/metrics"
+ "github.com/unionj-cloud/toolkit/gocache/lib/store"
+ go_cache_store "github.com/unionj-cloud/toolkit/gocache/store/go_cache"
+ redis_store "github.com/unionj-cloud/toolkit/gocache/store/redis"
+ ristretto_store "github.com/unionj-cloud/toolkit/gocache/store/ristretto"
"github.com/unionj-cloud/go-doudou/v2/framework/config"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/cast"
+ "github.com/unionj-cloud/toolkit/sliceutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
var CacheManager gocache.CacheInterface[any]
diff --git a/framework/cache/lrucache.go b/framework/cache/lrucache.go
index a5eecc2f..419bc064 100644
--- a/framework/cache/lrucache.go
+++ b/framework/cache/lrucache.go
@@ -5,7 +5,7 @@ import (
lru "github.com/hashicorp/golang-lru"
"github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
+ "github.com/unionj-cloud/toolkit/caller"
)
type LruCache struct {
diff --git a/framework/config/config.go b/framework/config/config.go
index 01bb08fe..5b5f628d 100644
--- a/framework/config/config.go
+++ b/framework/config/config.go
@@ -12,8 +12,8 @@ import (
"sync"
"time"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/fileutils"
+ "github.com/unionj-cloud/toolkit/constants"
+ "github.com/unionj-cloud/toolkit/fileutils"
"github.com/apolloconfig/agollo/v4"
"github.com/apolloconfig/agollo/v4/env/config"
@@ -25,12 +25,12 @@ import (
_ "go.uber.org/automaxprocs"
"github.com/unionj-cloud/go-doudou/v2/framework/configmgr"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/dotenv"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/envconfig"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/yaml"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/cast"
+ "github.com/unionj-cloud/toolkit/dotenv"
+ "github.com/unionj-cloud/toolkit/envconfig"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/yaml"
+ "github.com/unionj-cloud/toolkit/zlogger"
)
var GddConfig = &gddConfig{}
diff --git a/framework/configmgr/apollo.go b/framework/configmgr/apollo.go
index 265b9284..efeaea93 100644
--- a/framework/configmgr/apollo.go
+++ b/framework/configmgr/apollo.go
@@ -10,7 +10,7 @@ import (
"github.com/apolloconfig/agollo/v4/env/config"
"github.com/apolloconfig/agollo/v4/storage"
"github.com/pkg/errors"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
)
var onceApollo sync.Once
diff --git a/framework/configmgr/nacos.go b/framework/configmgr/nacos.go
index 483373c6..7b873b9f 100644
--- a/framework/configmgr/nacos.go
+++ b/framework/configmgr/nacos.go
@@ -9,10 +9,10 @@ import (
"github.com/joho/godotenv"
"github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/dotenv"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/maputils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/yaml"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/dotenv"
+ "github.com/unionj-cloud/toolkit/maputils"
+ "github.com/unionj-cloud/toolkit/yaml"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
"github.com/wubin1989/nacos-sdk-go/v2/clients"
"github.com/wubin1989/nacos-sdk-go/v2/clients/cache"
"github.com/wubin1989/nacos-sdk-go/v2/clients/config_client"
diff --git a/framework/configmgr/nacos_test.go b/framework/configmgr/nacos_test.go
index 0fae5d24..5f824d1c 100644
--- a/framework/configmgr/nacos_test.go
+++ b/framework/configmgr/nacos_test.go
@@ -12,7 +12,7 @@ import (
"github.com/unionj-cloud/go-doudou/v2/framework/config"
"github.com/unionj-cloud/go-doudou/v2/framework/configmgr"
"github.com/unionj-cloud/go-doudou/v2/framework/configmgr/mock"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
"github.com/wubin1989/nacos-sdk-go/v2/clients/cache"
"github.com/wubin1989/nacos-sdk-go/v2/clients/config_client"
"github.com/wubin1989/nacos-sdk-go/v2/util"
diff --git a/framework/database/cacheradapter.go b/framework/database/cacheradapter.go
index a2152c8e..d5a0a0ab 100644
--- a/framework/database/cacheradapter.go
+++ b/framework/database/cacheradapter.go
@@ -4,10 +4,10 @@ import (
"context"
"github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caches"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/cache"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/caches"
+ "github.com/unionj-cloud/toolkit/gocache/lib/cache"
+ "github.com/unionj-cloud/toolkit/gocache/lib/store"
+ "github.com/unionj-cloud/toolkit/zlogger"
)
var _ caches.Cacher = (*CacherAdapter)(nil)
diff --git a/framework/database/database.go b/framework/database/database.go
index f45de6d5..d2500e11 100644
--- a/framework/database/database.go
+++ b/framework/database/database.go
@@ -6,7 +6,7 @@ import (
"strings"
"time"
- gocache "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/cache"
+ gocache "github.com/unionj-cloud/toolkit/gocache/lib/cache"
"github.com/wubin1989/clickhouse"
"github.com/wubin1989/sqlite"
"github.com/wubin1989/sqlserver"
@@ -20,11 +20,11 @@ import (
"github.com/unionj-cloud/go-doudou/v2/framework/cache"
"github.com/unionj-cloud/go-doudou/v2/framework/config"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caches"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/errorx"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/caches"
+ "github.com/unionj-cloud/toolkit/cast"
+ "github.com/unionj-cloud/toolkit/errorx"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/zlogger"
)
const (
diff --git a/framework/database/marshaler.go b/framework/database/marshaler.go
index 3bb8b323..d85ede36 100644
--- a/framework/database/marshaler.go
+++ b/framework/database/marshaler.go
@@ -12,10 +12,10 @@ import (
"github.com/lithammer/shortuuid/v4"
"github.com/samber/lo"
"github.com/spf13/cast"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caches"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/cache"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/reflectutils"
+ "github.com/unionj-cloud/toolkit/caches"
+ "github.com/unionj-cloud/toolkit/gocache/lib/cache"
+ "github.com/unionj-cloud/toolkit/gocache/lib/store"
+ "github.com/unionj-cloud/toolkit/reflectutils"
)
var json = sonic.ConfigDefault
diff --git a/framework/grpcx/grpc_resolver_nacos/balancer.go b/framework/grpcx/grpc_resolver_nacos/balancer.go
index f9270c85..da908a0c 100644
--- a/framework/grpcx/grpc_resolver_nacos/balancer.go
+++ b/framework/grpcx/grpc_resolver_nacos/balancer.go
@@ -5,7 +5,7 @@ import (
"sort"
"sync"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/zlogger"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/base"
diff --git a/framework/grpcx/grpc_resolver_zk/balancer.go b/framework/grpcx/grpc_resolver_zk/balancer.go
index 21c6da62..b301633d 100644
--- a/framework/grpcx/grpc_resolver_zk/balancer.go
+++ b/framework/grpcx/grpc_resolver_zk/balancer.go
@@ -5,7 +5,7 @@ import (
"sort"
"sync"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/zlogger"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/base"
diff --git a/framework/grpcx/grpc_resolver_zk/zk.go b/framework/grpcx/grpc_resolver_zk/zk.go
index cdf3b77d..30f38467 100644
--- a/framework/grpcx/grpc_resolver_zk/zk.go
+++ b/framework/grpcx/grpc_resolver_zk/zk.go
@@ -5,7 +5,7 @@ import (
"strings"
"github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
+ "github.com/unionj-cloud/toolkit/cast"
"google.golang.org/grpc/attributes"
"google.golang.org/grpc/resolver"
)
diff --git a/framework/grpcx/server.go b/framework/grpcx/server.go
index 82f69b2d..be82d90c 100644
--- a/framework/grpcx/server.go
+++ b/framework/grpcx/server.go
@@ -14,8 +14,8 @@ import (
"github.com/unionj-cloud/go-doudou/v2/framework"
"github.com/unionj-cloud/go-doudou/v2/framework/config"
register "github.com/unionj-cloud/go-doudou/v2/framework/registry"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/timeutils"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/timeutils"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
"google.golang.org/grpc"
"google.golang.org/grpc/reflection"
)
diff --git a/framework/logger/configure.go b/framework/logger/configure.go
index 66c2934e..0e45fad4 100644
--- a/framework/logger/configure.go
+++ b/framework/logger/configure.go
@@ -6,7 +6,7 @@ import (
"github.com/mattn/go-colorable"
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/framework/config"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
type LoggerOption func(*logrus.Logger)
diff --git a/framework/logger/entry.go b/framework/logger/entry.go
index 6053e01b..db57e451 100644
--- a/framework/logger/entry.go
+++ b/framework/logger/entry.go
@@ -9,8 +9,8 @@ import (
"github.com/sirupsen/logrus"
"github.com/unionj-cloud/go-doudou/v2/framework/buildinfo"
"github.com/unionj-cloud/go-doudou/v2/framework/config"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/constants"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
var (
diff --git a/framework/plugin/plugin.go b/framework/plugin/plugin.go
index b51905d5..1a700e57 100644
--- a/framework/plugin/plugin.go
+++ b/framework/plugin/plugin.go
@@ -4,7 +4,7 @@ import (
"github.com/elliotchance/orderedmap/v2"
"github.com/unionj-cloud/go-doudou/v2/framework/grpcx"
"github.com/unionj-cloud/go-doudou/v2/framework/rest"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pipeconn"
+ "github.com/unionj-cloud/toolkit/pipeconn"
)
var servicePlugins = orderedmap.NewOrderedMap[string, ServicePlugin]()
diff --git a/framework/ratelimit/memrate/memorystore.go b/framework/ratelimit/memrate/memorystore.go
index 754af957..e8231feb 100644
--- a/framework/ratelimit/memrate/memorystore.go
+++ b/framework/ratelimit/memrate/memorystore.go
@@ -7,7 +7,7 @@ import (
lru "github.com/hashicorp/golang-lru"
"github.com/hashicorp/golang-lru/simplelru"
"github.com/unionj-cloud/go-doudou/v2/framework/ratelimit"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
)
const defaultMaxKeys = 256
diff --git a/framework/ratelimit/memrate/rate.go b/framework/ratelimit/memrate/rate.go
index c23dd3e4..eb1bb78d 100644
--- a/framework/ratelimit/memrate/rate.go
+++ b/framework/ratelimit/memrate/rate.go
@@ -13,7 +13,7 @@ import (
"time"
"github.com/unionj-cloud/go-doudou/v2/framework/ratelimit"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
)
// Limit defines the maximum frequency of some events.
diff --git a/framework/ratelimit/redisrate/gcralimiter.go b/framework/ratelimit/redisrate/gcralimiter.go
index 8fbcd5bd..61a83f5a 100644
--- a/framework/ratelimit/redisrate/gcralimiter.go
+++ b/framework/ratelimit/redisrate/gcralimiter.go
@@ -6,7 +6,7 @@ import (
"time"
"github.com/unionj-cloud/go-doudou/v2/framework/ratelimit"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
"github.com/go-redis/redis/v8"
)
diff --git a/framework/registry/etcd/balancer.go b/framework/registry/etcd/balancer.go
index 76532e80..1ad7abd9 100644
--- a/framework/registry/etcd/balancer.go
+++ b/framework/registry/etcd/balancer.go
@@ -3,7 +3,7 @@ package etcd
import (
"sync"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/zlogger"
"google.golang.org/grpc/balancer"
"google.golang.org/grpc/balancer/base"
diff --git a/framework/registry/etcd/node.go b/framework/registry/etcd/node.go
index 004a9f32..41c09be4 100644
--- a/framework/registry/etcd/node.go
+++ b/framework/registry/etcd/node.go
@@ -16,10 +16,10 @@ import (
cons "github.com/unionj-cloud/go-doudou/v2/framework/registry/constants"
"github.com/unionj-cloud/go-doudou/v2/framework/registry/interfaces"
"github.com/unionj-cloud/go-doudou/v2/framework/registry/utils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/cast"
+ "github.com/unionj-cloud/toolkit/constants"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/zlogger"
clientv3 "go.etcd.io/etcd/client/v3"
"go.etcd.io/etcd/client/v3/naming/endpoints"
"go.etcd.io/etcd/client/v3/naming/resolver"
diff --git a/framework/registry/memberlist/balancer.go b/framework/registry/memberlist/balancer.go
index c800ed39..dca79271 100644
--- a/framework/registry/memberlist/balancer.go
+++ b/framework/registry/memberlist/balancer.go
@@ -3,7 +3,7 @@ package memberlist
import (
"sync"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/zlogger"
"google.golang.org/grpc/balancer"
balancerbase "google.golang.org/grpc/balancer/base"
diff --git a/framework/registry/memberlist/config.go b/framework/registry/memberlist/config.go
index fb21f8cb..2d7c0ede 100644
--- a/framework/registry/memberlist/config.go
+++ b/framework/registry/memberlist/config.go
@@ -5,9 +5,9 @@ import (
"time"
"github.com/unionj-cloud/go-doudou/v2/framework/config"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/cast"
+ "github.com/unionj-cloud/toolkit/memberlist"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
func setGddMemDeadTimeout(conf *memberlist.Config) {
diff --git a/framework/registry/memberlist/config_test.go b/framework/registry/memberlist/config_test.go
index 052476d3..b1219424 100644
--- a/framework/registry/memberlist/config_test.go
+++ b/framework/registry/memberlist/config_test.go
@@ -5,7 +5,7 @@ import (
"testing"
"github.com/unionj-cloud/go-doudou/v2/framework/config"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
+ "github.com/unionj-cloud/toolkit/memberlist"
)
func TestMain(m *testing.M) {
diff --git a/framework/registry/memberlist/delegate.go b/framework/registry/memberlist/delegate.go
index cedffc5e..883ad229 100644
--- a/framework/registry/memberlist/delegate.go
+++ b/framework/registry/memberlist/delegate.go
@@ -8,8 +8,8 @@ import (
"github.com/hashicorp/go-msgpack/codec"
"github.com/unionj-cloud/go-doudou/v2/framework/registry/constants"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/memberlist"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
)
type Service struct {
diff --git a/framework/registry/memberlist/delegate_test.go b/framework/registry/memberlist/delegate_test.go
index f10d9d34..de36362e 100644
--- a/framework/registry/memberlist/delegate_test.go
+++ b/framework/registry/memberlist/delegate_test.go
@@ -6,7 +6,7 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
+ "github.com/unionj-cloud/toolkit/memberlist"
)
func Test_delegate_NodeMeta(t *testing.T) {
diff --git a/framework/registry/memberlist/eventdelegate.go b/framework/registry/memberlist/eventdelegate.go
index 4cecbf0e..516c4ef3 100644
--- a/framework/registry/memberlist/eventdelegate.go
+++ b/framework/registry/memberlist/eventdelegate.go
@@ -1,7 +1,7 @@
package memberlist
import (
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
+ "github.com/unionj-cloud/toolkit/memberlist"
)
type eventDelegate struct {
diff --git a/framework/registry/memberlist/eventdelegate_test.go b/framework/registry/memberlist/eventdelegate_test.go
index 5c67f5dc..0fde0d24 100644
--- a/framework/registry/memberlist/eventdelegate_test.go
+++ b/framework/registry/memberlist/eventdelegate_test.go
@@ -4,7 +4,7 @@ import (
"testing"
"github.com/bytedance/sonic"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
+ "github.com/unionj-cloud/toolkit/memberlist"
)
var json = sonic.ConfigDefault
diff --git a/framework/registry/memberlist/node.go b/framework/registry/memberlist/node.go
index 1cd96b57..ad4e61f2 100644
--- a/framework/registry/memberlist/node.go
+++ b/framework/registry/memberlist/node.go
@@ -19,11 +19,11 @@ import (
"github.com/unionj-cloud/go-doudou/v2/framework/config"
"github.com/unionj-cloud/go-doudou/v2/framework/configmgr"
cons "github.com/unionj-cloud/go-doudou/v2/framework/registry/constants"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/cast"
+ "github.com/unionj-cloud/toolkit/constants"
+ "github.com/unionj-cloud/toolkit/memberlist"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
)
var mlist memberlist.IMemberlist
diff --git a/framework/registry/memberlist/resolver.go b/framework/registry/memberlist/resolver.go
index c3c562cb..73e9dc7b 100644
--- a/framework/registry/memberlist/resolver.go
+++ b/framework/registry/memberlist/resolver.go
@@ -6,7 +6,7 @@ import (
"sync"
"github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
+ "github.com/unionj-cloud/toolkit/memberlist"
"google.golang.org/grpc/attributes"
gresolver "google.golang.org/grpc/resolver"
)
diff --git a/framework/registry/memberlist/serviceprovider.go b/framework/registry/memberlist/serviceprovider.go
index c9bb9ef7..05cc575a 100644
--- a/framework/registry/memberlist/serviceprovider.go
+++ b/framework/registry/memberlist/serviceprovider.go
@@ -7,9 +7,9 @@ import (
"sync/atomic"
"time"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/memberlist"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
"google.golang.org/grpc"
)
diff --git a/framework/registry/memberlist/serviceprovider_test.go b/framework/registry/memberlist/serviceprovider_test.go
index 226bace4..d05340b9 100644
--- a/framework/registry/memberlist/serviceprovider_test.go
+++ b/framework/registry/memberlist/serviceprovider_test.go
@@ -3,7 +3,7 @@ package memberlist
import (
"sync"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
+ "github.com/unionj-cloud/toolkit/memberlist"
)
type mockServiceProvider struct {
diff --git a/framework/registry/nacos/node.go b/framework/registry/nacos/node.go
index bc5cab94..93e06750 100644
--- a/framework/registry/nacos/node.go
+++ b/framework/registry/nacos/node.go
@@ -16,10 +16,10 @@ import (
"github.com/unionj-cloud/go-doudou/v2/framework/grpcx/grpc_resolver_nacos"
cons "github.com/unionj-cloud/go-doudou/v2/framework/registry/constants"
"github.com/unionj-cloud/go-doudou/v2/framework/registry/utils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/cast"
+ "github.com/unionj-cloud/toolkit/constants"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
"github.com/wubin1989/nacos-sdk-go/v2/clients"
"github.com/wubin1989/nacos-sdk-go/v2/clients/naming_client"
"github.com/wubin1989/nacos-sdk-go/v2/model"
diff --git a/framework/registry/node.go b/framework/registry/node.go
index db11873f..6645d518 100644
--- a/framework/registry/node.go
+++ b/framework/registry/node.go
@@ -7,7 +7,7 @@ import (
"github.com/unionj-cloud/go-doudou/v2/framework/registry/memberlist"
"github.com/unionj-cloud/go-doudou/v2/framework/registry/nacos"
"github.com/unionj-cloud/go-doudou/v2/framework/registry/zk"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
)
type IServiceProvider interface {
diff --git a/framework/registry/pipe/node.go b/framework/registry/pipe/node.go
index 2a01f181..1e92ec32 100644
--- a/framework/registry/pipe/node.go
+++ b/framework/registry/pipe/node.go
@@ -5,8 +5,8 @@ import (
"net"
"time"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pipeconn"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/pipeconn"
+ "github.com/unionj-cloud/toolkit/zlogger"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
diff --git a/framework/registry/serversets/endpoint.go b/framework/registry/serversets/endpoint.go
index 52abcb44..fdf10026 100644
--- a/framework/registry/serversets/endpoint.go
+++ b/framework/registry/serversets/endpoint.go
@@ -9,7 +9,7 @@ import (
"github.com/bytedance/sonic"
"github.com/unionj-cloud/go-doudou/v2/framework/config"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
+ "github.com/unionj-cloud/toolkit/cast"
"github.com/go-zookeeper/zk"
)
diff --git a/framework/registry/serversets/watch.go b/framework/registry/serversets/watch.go
index 4be63036..2ebb9806 100644
--- a/framework/registry/serversets/watch.go
+++ b/framework/registry/serversets/watch.go
@@ -7,8 +7,8 @@ import (
"time"
"github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/caller"
+ "github.com/unionj-cloud/toolkit/zlogger"
"github.com/go-zookeeper/zk"
)
diff --git a/framework/registry/utils/utils.go b/framework/registry/utils/utils.go
index a01afb41..40239ce4 100644
--- a/framework/registry/utils/utils.go
+++ b/framework/registry/utils/utils.go
@@ -3,8 +3,8 @@ package utils
import (
"github.com/hashicorp/go-sockaddr"
"github.com/unionj-cloud/go-doudou/v2/framework/config"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/zlogger"
)
var GetPrivateIP = sockaddr.GetPrivateIP
diff --git a/framework/registry/zk/node.go b/framework/registry/zk/node.go
index d82f6a12..b45088cc 100644
--- a/framework/registry/zk/node.go
+++ b/framework/registry/zk/node.go
@@ -20,11 +20,11 @@ import (
"github.com/unionj-cloud/go-doudou/v2/framework/registry/interfaces"
"github.com/unionj-cloud/go-doudou/v2/framework/registry/serversets"
"github.com/unionj-cloud/go-doudou/v2/framework/registry/utils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/errorx"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/cast"
+ "github.com/unionj-cloud/toolkit/constants"
+ "github.com/unionj-cloud/toolkit/errorx"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/zlogger"
"google.golang.org/grpc"
)
diff --git a/framework/rest/confighandler.go b/framework/rest/confighandler.go
index 981ad01b..e8d85642 100644
--- a/framework/rest/confighandler.go
+++ b/framework/rest/confighandler.go
@@ -6,7 +6,7 @@ import (
"os"
"strings"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
var ConfigRoutes = configRoutes
diff --git a/framework/rest/dochandler.go b/framework/rest/dochandler.go
index 2b83937e..fca417ff 100644
--- a/framework/rest/dochandler.go
+++ b/framework/rest/dochandler.go
@@ -8,7 +8,7 @@ import (
"text/template"
"github.com/rs/cors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
// window.docs = [
diff --git a/framework/rest/form.go b/framework/rest/form.go
index 3bb4dc85..959aadef 100644
--- a/framework/rest/form.go
+++ b/framework/rest/form.go
@@ -2,8 +2,8 @@ package rest
import (
"github.com/goccy/go-reflect"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/form"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/form"
+ "github.com/unionj-cloud/toolkit/stringutils"
"net/url"
)
diff --git a/framework/rest/httprouter/routegroup.go b/framework/rest/httprouter/routegroup.go
index 14b4125d..ac5ee802 100644
--- a/framework/rest/httprouter/routegroup.go
+++ b/framework/rest/httprouter/routegroup.go
@@ -3,7 +3,7 @@ package httprouter
import (
"net/http"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
type RouteGroup struct {
diff --git a/framework/rest/memberlistuihandler.go b/framework/rest/memberlistuihandler.go
index 4883d46c..01d25dc2 100644
--- a/framework/rest/memberlistuihandler.go
+++ b/framework/rest/memberlistuihandler.go
@@ -8,7 +8,7 @@ import (
"github.com/hako/durafmt"
registry "github.com/unionj-cloud/go-doudou/v2/framework/registry/memberlist"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
+ "github.com/unionj-cloud/toolkit/memberlist"
)
type Row struct {
diff --git a/framework/rest/middleware.go b/framework/rest/middleware.go
index 48f70f71..019f5ba4 100644
--- a/framework/rest/middleware.go
+++ b/framework/rest/middleware.go
@@ -26,8 +26,8 @@ import (
"github.com/uber/jaeger-client-go"
"github.com/unionj-cloud/go-doudou/v2/framework/config"
"github.com/unionj-cloud/go-doudou/v2/framework/configmgr"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
)
var json = sonic.ConfigDefault
diff --git a/framework/rest/middleware_test.go b/framework/rest/middleware_test.go
index 285916a6..ae0983ec 100644
--- a/framework/rest/middleware_test.go
+++ b/framework/rest/middleware_test.go
@@ -26,7 +26,7 @@ import (
"github.com/unionj-cloud/go-doudou/v2/framework/rest"
httpMock "github.com/unionj-cloud/go-doudou/v2/framework/rest/mock"
"github.com/unionj-cloud/go-doudou/v2/framework/restclient"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/maputils"
+ "github.com/unionj-cloud/toolkit/maputils"
"github.com/wubin1989/nacos-sdk-go/v2/clients/cache"
"github.com/wubin1989/nacos-sdk-go/v2/clients/config_client"
"github.com/wubin1989/nacos-sdk-go/v2/vo"
diff --git a/framework/rest/model.go b/framework/rest/model.go
index 651af5a2..84328d66 100644
--- a/framework/rest/model.go
+++ b/framework/rest/model.go
@@ -10,7 +10,7 @@ import (
"strings"
"github.com/pkg/errors"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
)
// Route wraps config for route
diff --git a/framework/rest/prommiddleware.go b/framework/rest/prommiddleware.go
index f3ac88ce..ac505a48 100644
--- a/framework/rest/prommiddleware.go
+++ b/framework/rest/prommiddleware.go
@@ -11,8 +11,8 @@ import (
"github.com/prometheus/client_golang/prometheus"
"github.com/unionj-cloud/go-doudou/v2/framework/buildinfo"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/constants"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
type responseWriter struct {
diff --git a/framework/rest/server.go b/framework/rest/server.go
index 22f7a8e3..9af09b84 100644
--- a/framework/rest/server.go
+++ b/framework/rest/server.go
@@ -22,8 +22,8 @@ import (
register "github.com/unionj-cloud/go-doudou/v2/framework/registry"
"github.com/unionj-cloud/go-doudou/v2/framework/registry/constants"
"github.com/unionj-cloud/go-doudou/v2/framework/rest/httprouter"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
)
type MiddlewareFunc func(http.Handler) http.Handler
diff --git a/framework/rest/validate.go b/framework/rest/validate.go
index f9723e0d..332422fd 100644
--- a/framework/rest/validate.go
+++ b/framework/rest/validate.go
@@ -6,7 +6,7 @@ import (
ut "github.com/go-playground/universal-translator"
"github.com/go-playground/validator/v10"
"github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
+ "github.com/unionj-cloud/toolkit/stringutils"
)
var validate = validator.New()
diff --git a/framework/restclient/restclient.go b/framework/restclient/restclient.go
index 29603d5c..3f167dfb 100644
--- a/framework/restclient/restclient.go
+++ b/framework/restclient/restclient.go
@@ -12,7 +12,7 @@ import (
"github.com/opentracing-contrib/go-stdlib/nethttp"
"github.com/unionj-cloud/go-doudou/v2/framework/config"
"github.com/unionj-cloud/go-doudou/v2/framework/registry"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
+ "github.com/unionj-cloud/toolkit/cast"
)
// RestClient defines service client interface
diff --git a/framework/testdata/checkIc2/svc.go b/framework/testdata/checkIc2/svc.go
index 2b22d294..7df77e4a 100644
--- a/framework/testdata/checkIc2/svc.go
+++ b/framework/testdata/checkIc2/svc.go
@@ -4,7 +4,7 @@ import (
"context"
"mime/multipart"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
// 用户服务接口
diff --git a/framework/testdata/novo/svc.go b/framework/testdata/novo/svc.go
index 372516ba..b61d9144 100644
--- a/framework/testdata/novo/svc.go
+++ b/framework/testdata/novo/svc.go
@@ -6,7 +6,7 @@ import (
"os"
"github.com/unionj-cloud/go-doudou/v2/framework/testdata/vo"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
// 用户服务接口
diff --git a/framework/testdata/svc.go b/framework/testdata/svc.go
index 372516ba..b61d9144 100644
--- a/framework/testdata/svc.go
+++ b/framework/testdata/svc.go
@@ -6,7 +6,7 @@ import (
"os"
"github.com/unionj-cloud/go-doudou/v2/framework/testdata/vo"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
// 用户服务接口
diff --git a/framework/testdata/svcp.go b/framework/testdata/svcp.go
index 12475a5e..8894d8bf 100644
--- a/framework/testdata/svcp.go
+++ b/framework/testdata/svcp.go
@@ -5,7 +5,7 @@ import (
"os"
"github.com/unionj-cloud/go-doudou/v2/framework/testdata/vo"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
+ v3 "github.com/unionj-cloud/toolkit/openapi/v3"
)
// 用户服务接口
diff --git a/framework/tracing/tracer.go b/framework/tracing/tracer.go
index 8449f139..467ec650 100644
--- a/framework/tracing/tracer.go
+++ b/framework/tracing/tracer.go
@@ -11,8 +11,8 @@ import (
"github.com/uber/jaeger-lib/metrics"
jprom "github.com/uber/jaeger-lib/metrics/prometheus"
ddconfig "github.com/unionj-cloud/go-doudou/v2/framework/config"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- logger "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
+ "github.com/unionj-cloud/toolkit/stringutils"
+ logger "github.com/unionj-cloud/toolkit/zlogger"
)
// Init returns an instance of Jaeger Tracer.
diff --git a/go.mod b/go.mod
index 1f6b03b6..33be212e 100644
--- a/go.mod
+++ b/go.mod
@@ -1,8 +1,6 @@
module github.com/unionj-cloud/go-doudou/v2
-go 1.21
-
-toolchain go1.22.2
+go 1.22.2
require (
github.com/Jeffail/gabs/v2 v2.6.1
@@ -10,17 +8,16 @@ require (
github.com/common-nighthawk/go-figure v0.0.0-20200609044655-c4b36f998cf2
github.com/deckarep/golang-set v1.8.0
github.com/felixge/httpsnoop v1.0.4
- github.com/go-git/go-billy/v5 v5.4.1
- github.com/go-git/go-git/v5 v5.6.1
+ github.com/go-git/go-billy/v5 v5.4.1 // indirect
+ github.com/go-git/go-git/v5 v5.6.1 // indirect
github.com/go-resty/resty/v2 v2.7.0
- github.com/go-sql-driver/mysql v1.7.1
+ github.com/go-sql-driver/mysql v1.8.1
github.com/goccy/go-yaml v1.11.0
- github.com/hyperjumptech/jiffy v1.0.0
- github.com/iancoleman/strcase v0.2.0
- github.com/jeremywohl/flatten v1.0.1
+ github.com/hyperjumptech/jiffy v1.0.0 // indirect
+ github.com/iancoleman/strcase v0.3.0
+ github.com/jeremywohl/flatten v1.0.1 // indirect
github.com/jmoiron/sqlx v1.3.5
github.com/joho/godotenv v1.5.1
- github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646
github.com/olekukonko/tablewriter v0.0.5
github.com/opentracing-contrib/go-stdlib v1.0.0
github.com/opentracing/opentracing-go v1.2.0
@@ -30,24 +27,19 @@ require (
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.7.0
github.com/stretchr/testify v1.9.0
- github.com/testcontainers/testcontainers-go v0.31.0
+ github.com/testcontainers/testcontainers-go v0.33.0
github.com/uber/jaeger-client-go v2.30.0+incompatible
github.com/uber/jaeger-lib v2.4.1+incompatible
- golang.org/x/tools v0.16.1
+ github.com/unionj-cloud/toolkit v0.0.1
+ github.com/wubin1989/gen v0.0.1
+ golang.org/x/tools v0.26.0 // indirect
)
require (
- github.com/DATA-DOG/go-sqlmock v1.5.2
- github.com/XiaoMi/pegasus-go-client v0.0.0-20220519103347-ba0e68465cd5
- github.com/allegro/bigcache/v3 v3.1.0
- github.com/auxten/postgresql-parser v1.0.1
- github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874
github.com/bytedance/sonic v1.12.1
- github.com/coocood/freecache v1.2.4
github.com/deckarep/golang-set/v2 v2.6.0
github.com/dgraph-io/ristretto v0.1.1
github.com/elliotchance/orderedmap/v2 v2.2.0
- github.com/go-playground/assert/v2 v2.2.0
github.com/go-redis/redis/v8 v8.11.5
github.com/go-zookeeper/zk v1.0.3
github.com/gobwas/glob v0.2.3
@@ -56,35 +48,25 @@ require (
github.com/gorilla/handlers v1.5.1
github.com/hashicorp/go-sockaddr v1.0.2
github.com/hashicorp/golang-lru v0.5.4
- github.com/hazelcast/hazelcast-go-client v1.4.1
github.com/manifoldco/promptui v0.9.0
- github.com/mholt/archiver/v3 v3.5.1
- github.com/morkid/gocache v1.0.0
github.com/patrickmn/go-cache v2.1.0+incompatible
- github.com/redis/rueidis v1.0.38
- github.com/redis/rueidis/mock v1.0.38
- github.com/redis/rueidis/rueidiscompat v1.0.38
github.com/rs/cors v1.9.0
github.com/samber/lo v1.39.0
github.com/slok/goresilience v0.2.0
- github.com/wubin1989/clickhouse v0.0.3-0.20240611142627-b655b8690cda
- github.com/wubin1989/datatypes v0.0.2-0.20240611143143-56e1066d0d46
- github.com/wubin1989/dbresolver v0.0.2-0.20240611143237-5d31d83b04ba
- github.com/wubin1989/gorm v0.0.4
- github.com/wubin1989/hints v0.0.2-0.20240611143337-989d3cd7fd24
- github.com/wubin1989/mysql v0.0.2-0.20240611142742-11722462c1f3
+ github.com/wubin1989/clickhouse v0.0.3
+ github.com/wubin1989/gorm v0.0.5
+ github.com/wubin1989/mysql v0.0.2
github.com/wubin1989/prometheus v0.0.2-0.20240611143457-960b4a499cef
- github.com/wubin1989/sqlite v0.0.3-0.20240611143051-c35482f79d92
- github.com/wubin1989/sqlserver v0.0.2-0.20240611142955-b9bee6e252c3
- github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2
- go.uber.org/mock v0.4.0
- gopkg.in/yaml.v3 v3.0.1
+ github.com/wubin1989/sqlite v0.0.3
+ github.com/wubin1989/sqlserver v0.0.2
)
require (
dario.cat/mergo v1.0.0 // indirect
+ filippo.io/edwards25519 v1.1.0 // indirect
github.com/ClickHouse/ch-go v0.61.5 // indirect
- github.com/ClickHouse/clickhouse-go/v2 v2.23.2 // indirect
+ github.com/ClickHouse/clickhouse-go/v2 v2.29.0 // indirect
+ github.com/auxten/postgresql-parser v1.0.1 // indirect
github.com/bytedance/sonic/loader v0.2.0 // indirect
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 // indirect
@@ -96,49 +78,53 @@ require (
github.com/cockroachdb/redact v1.0.8 // indirect
github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2 // indirect
github.com/containerd/log v0.1.0 // indirect
+ github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.1 // indirect
- github.com/distribution/reference v0.5.0 // indirect
+ github.com/distribution/reference v0.6.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/getsentry/raven-go v0.2.0 // indirect
github.com/go-faster/city v1.0.1 // indirect
github.com/go-faster/errors v0.7.1 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
- github.com/golang/glog v1.1.2 // indirect
- github.com/google/go-cmp v0.6.0 // indirect
+ github.com/golang/glog v1.2.0 // indirect
github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/klauspost/cpuid/v2 v2.0.9 // indirect
github.com/kr/pretty v0.3.1 // indirect
github.com/kr/text v0.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
+ github.com/mholt/archiver/v3 v3.5.1 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.5.0 // indirect
github.com/moby/sys/user v0.1.0 // indirect
- github.com/nxadm/tail v1.4.8 // indirect
+ github.com/moby/sys/userns v0.1.0 // indirect
github.com/paulmach/orb v0.11.1 // indirect
- github.com/pegasus-kv/thrift v0.13.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/rogpeppe/go-internal v1.11.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/shirou/gopsutil/v3 v3.23.12 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
+ github.com/wubin1989/datatypes v0.0.2 // indirect
+ github.com/wubin1989/dbresolver v0.0.2 // indirect
+ github.com/wubin1989/hints v0.0.2 // indirect
+ github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect
+ go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 // indirect
go.opentelemetry.io/otel/metric v1.26.0 // indirect
+ go.opentelemetry.io/proto/otlp v1.0.0 // indirect
+ go.uber.org/mock v0.4.0 // indirect
golang.org/x/arch v0.1.0 // indirect
- google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
- gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
- gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect
- k8s.io/apimachinery v0.26.2 // indirect
+ google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 // indirect
+ gopkg.in/yaml.v3 v3.0.1 // indirect
)
require (
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
- github.com/Microsoft/go-winio v0.6.1 // indirect
- github.com/Microsoft/hcsshim v0.11.4 // indirect
+ github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 // indirect
github.com/acomagu/bufpipe v1.0.4 // indirect
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 // indirect
@@ -151,7 +137,7 @@ require (
github.com/cloudflare/circl v1.1.0 // indirect
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
- github.com/davecgh/go-spew v1.1.1
+ github.com/davecgh/go-spew v1.1.1 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect
@@ -166,7 +152,7 @@ require (
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
- github.com/golang/protobuf v1.5.4
+ github.com/golang/protobuf v1.5.4 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 // indirect
@@ -196,8 +182,6 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/nwaples/rardecode v1.1.3 // indirect
- github.com/onsi/ginkgo v1.16.5
- github.com/onsi/gomega v1.31.1
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pelletier/go-toml v1.9.5 // indirect
@@ -211,7 +195,7 @@ require (
github.com/redis/go-redis/v9 v9.1.0
github.com/rivo/uniseg v0.1.0 // indirect
github.com/skeema/knownhosts v1.1.0 // indirect
- github.com/smartystreets/assertions v1.2.0
+ github.com/smartystreets/assertions v1.2.0 // indirect
github.com/spf13/afero v1.9.2 // indirect
github.com/spf13/cast v1.3.0
github.com/spf13/jwalterweatherman v1.0.0 // indirect
@@ -221,8 +205,8 @@ require (
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
- github.com/vmihailenco/go-tinylfu v0.2.2
- github.com/vmihailenco/msgpack/v5 v5.3.5
+ github.com/vmihailenco/go-tinylfu v0.2.2 // indirect
+ github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
@@ -234,10 +218,10 @@ require (
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
- golang.org/x/mod v0.16.0 // indirect
- golang.org/x/net v0.24.0 // indirect
- golang.org/x/sync v0.6.0
- golang.org/x/sys v0.19.0 // indirect
+ golang.org/x/mod v0.21.0 // indirect
+ golang.org/x/net v0.30.0 // indirect
+ golang.org/x/sync v0.8.0 // indirect
+ golang.org/x/sys v0.26.0 // indirect
golang.org/x/time v0.3.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/protobuf v1.33.0 // indirect
@@ -248,27 +232,24 @@ require (
)
require (
- github.com/HdrHistogram/hdrhistogram-go v1.1.2 // indirect
github.com/apolloconfig/agollo/v4 v4.1.1-0.20220323095621-60ed86180f24
github.com/arl/statsviz v0.6.0
- github.com/armon/go-metrics v0.4.1
- github.com/containerd/containerd v1.7.15 // indirect
- github.com/docker/docker v26.0.1+incompatible // indirect
+ github.com/armon/go-metrics v0.4.1 // indirect
+ github.com/docker/docker v27.3.0+incompatible // indirect
github.com/fatih/color v1.12.0 // indirect
- github.com/getkin/kin-openapi v0.115.0
- github.com/ghodss/yaml v1.0.0
- github.com/go-playground/form/v4 v4.2.0
+ github.com/getkin/kin-openapi v0.115.0 // indirect
+ github.com/ghodss/yaml v1.0.0 // indirect
github.com/go-playground/universal-translator v0.18.1
github.com/go-playground/validator/v10 v10.11.2
github.com/golang/mock v1.6.0
- github.com/google/btree v1.1.2
- github.com/google/uuid v1.6.0
+ github.com/google/btree v1.1.2 // indirect
+ github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0
github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-immutable-radix v1.3.1 // indirect
github.com/hashicorp/go-msgpack v1.1.5
- github.com/hashicorp/go-multierror v1.1.1
+ github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-version v1.6.0
github.com/hashicorp/logutils v1.0.0
github.com/jackc/pgx/v5 v5.5.5 // indirect
@@ -276,26 +257,26 @@ require (
github.com/lithammer/shortuuid/v4 v4.0.0
github.com/mattn/go-colorable v0.1.13
github.com/mattn/go-runewidth v0.0.10 // indirect
- github.com/mattn/go-sqlite3 v1.14.17 // indirect
- github.com/microsoft/go-mssqldb v1.7.0 // indirect
- github.com/miekg/dns v1.1.54
+ github.com/mattn/go-sqlite3 v1.14.22 // indirect
+ github.com/microsoft/go-mssqldb v1.7.2 // indirect
+ github.com/miekg/dns v1.1.54 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/rbretecher/go-postman-collection v0.9.0
github.com/rs/zerolog v1.28.0
- github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529
+ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
github.com/sergi/go-diff v1.2.0 // indirect
- github.com/shirou/gopsutil v3.21.11+incompatible
- github.com/shopspring/decimal v1.4.0
+ github.com/shirou/gopsutil v3.21.11+incompatible // indirect
+ github.com/shopspring/decimal v1.4.0 // indirect
github.com/smartystreets/goconvey v1.7.2
github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb
github.com/wubin1989/nacos-sdk-go/v2 v2.1.2-0.20221024120645-0288f53fdaa8
- github.com/wubin1989/postgres v0.0.2-0.20240611142853-f89b1682d931
+ github.com/wubin1989/postgres v0.0.2
go.etcd.io/etcd/client/v3 v3.5.14
go.uber.org/automaxprocs v1.5.2
- golang.org/x/crypto v0.22.0 // indirect
+ golang.org/x/crypto v0.28.0 // indirect
golang.org/x/exp v0.0.0-20230510235704-dd950f8aeaea
- golang.org/x/text v0.14.0
- google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d // indirect
- google.golang.org/grpc v1.59.0
+ golang.org/x/text v0.19.0
+ google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13 // indirect
+ google.golang.org/grpc v1.64.1
)
diff --git a/go.sum b/go.sum
index 667d645c..a3d6743e 100644
--- a/go.sum
+++ b/go.sum
@@ -598,6 +598,8 @@ cloud.google.com/go/workflows v1.10.0/go.mod h1:fZ8LmRmZQWacon9UCX1r/g/DfAXx5VcP
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
+filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
+filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8=
git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU=
@@ -627,12 +629,10 @@ github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/ClickHouse/ch-go v0.61.5 h1:zwR8QbYI0tsMiEcze/uIMK+Tz1D3XZXLdNrlaOpeEI4=
github.com/ClickHouse/ch-go v0.61.5/go.mod h1:s1LJW/F/LcFs5HJnuogFMta50kKDO0lf9zzfrbl0RQg=
-github.com/ClickHouse/clickhouse-go/v2 v2.23.2 h1:+DAKPMnxLS7pduQZsrJc8OhdLS2L9MfDEJ2TS+hpYDM=
-github.com/ClickHouse/clickhouse-go/v2 v2.23.2/go.mod h1:aNap51J1OM3yxQJRgM+AlP/MPkGBCL8A74uQThoQhR0=
+github.com/ClickHouse/clickhouse-go/v2 v2.29.0 h1:Dj1w59RssRyLgGHXtYaWU0eIM1pJsu9nGPi/btmvAqw=
+github.com/ClickHouse/clickhouse-go/v2 v2.29.0/go.mod h1:bLookq6qZJ4Ush/6tOAnJGh1Sf3Sa/nQoMn71p7ZCUE=
github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
-github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU=
-github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU=
github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
@@ -642,23 +642,14 @@ github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c/go.mod h1:X0
github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
-github.com/Microsoft/go-winio v0.6.1 h1:9/kr64B9VUZrLm5YYwbGtUJnMgqWVOdUAXu6Migciow=
-github.com/Microsoft/go-winio v0.6.1/go.mod h1:LRdKpFKfdobln8UmuiYcKPot9D2v6svN5+sAH+4kjUM=
-github.com/Microsoft/hcsshim v0.11.4 h1:68vKo2VN8DE9AdN4tnkWnmdhqdbpUFM8OF3Airm7fz8=
-github.com/Microsoft/hcsshim v0.11.4/go.mod h1:smjE4dvqPX9Zldna+t5FG3rnoHhaB7QYxPRqGcpAD9w=
-github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
+github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
+github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8 h1:wPbRQzjjwFc0ih8puEVAOFGELsn1zoIIYdxvML7mDxA=
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
-github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
-github.com/XiaoMi/pegasus-go-client v0.0.0-20220519103347-ba0e68465cd5 h1:mePTwVjIZ65Ko7gK9MWh/xV/ubEe9yns69Slzr4B1aI=
-github.com/XiaoMi/pegasus-go-client v0.0.0-20220519103347-ba0e68465cd5/go.mod h1:VrfgKISflRhFm32m3e0SXLccvNJTyG8PRywWbUuGEfY=
github.com/acomagu/bufpipe v1.0.4 h1:e3H4WUzM3npvo5uv95QuJM3cQspFNtFBzvJ2oNjKIDQ=
github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
-github.com/agiledragon/gomonkey v2.0.2+incompatible h1:eXKi9/piiC3cjJD1658mEE2o3NjkJ5vDLgYjCQu0Xlw=
-github.com/agiledragon/gomonkey v2.0.2+incompatible/go.mod h1:2NGfXu1a80LLr2cmWXGBDaHEjb1idR6+FVlX5T3D9hw=
github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY=
github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk=
@@ -675,8 +666,6 @@ github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk5
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704 h1:PpfENOj/vPfhhy9N2OFRjpue0hjM5XqAp2thFmkXXIk=
github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
-github.com/allegro/bigcache/v3 v3.1.0 h1:H2Vp8VOvxcrB91o86fUSVJFqeuz8kpyyB02eH3bSzwk=
-github.com/allegro/bigcache/v3 v3.1.0/go.mod h1:aPyh7jEvrog9zAwx5N7+JUQX5dZTSGpxF1LAR4dr35I=
github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M=
@@ -688,7 +677,6 @@ github.com/antlr/antlr4 v0.0.0-20200124162019-2d7f727a00b7 h1:4IkFZAFQ87SeXXF6n+
github.com/antlr/antlr4 v0.0.0-20200124162019-2d7f727a00b7/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
github.com/apache/arrow/go/v10 v10.0.1/go.mod h1:YvhnlEePVnBS4+0z3fhPfUy7W1Ikj0Ih0vcRo/gZ1M0=
github.com/apache/arrow/go/v11 v11.0.0/go.mod h1:Eg5OsL5H+e299f7u5ssuXsuHQVEGC4xei5aX110hRiI=
-github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY=
github.com/apache/thrift v0.16.0/go.mod h1:PHK3hniurgQaNMZYaCLEqXKsYK8upmhPbmdP2FXSqgU=
github.com/apolloconfig/agollo/v4 v4.1.1-0.20220323095621-60ed86180f24 h1:ZMK8YXD82znWnBrn9XF7wlPFDXPdBrPphL6ZS2pIKqQ=
github.com/apolloconfig/agollo/v4 v4.1.1-0.20220323095621-60ed86180f24/go.mod h1:SuvTjtg0p4UlSzSbik+ibLRr6oR1xRsfy65QzP3GEAs=
@@ -716,8 +704,6 @@ github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kB
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
-github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874 h1:N7oVaKyGp8bttX0bfZGmcGkjz7DLQXhAn3DNd3T0ous=
-github.com/bradfitz/gomemcache v0.0.0-20230905024940-24af94b03874/go.mod h1:r5xuitiExdLAJ09PR7vBVENGvp4ZuTBeWTGtxuX3K+c=
github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0=
github.com/bsm/ginkgo/v2 v2.9.5/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
@@ -730,7 +716,6 @@ github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKz
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
github.com/bytedance/sonic/loader v0.2.0 h1:zNprn+lsIP06C/IqCHs3gPQIvnvpKbbxyXQP1iU4kWM=
github.com/bytedance/sonic/loader v0.2.0/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
-github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
@@ -786,12 +771,10 @@ github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk
github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
github.com/common-nighthawk/go-figure v0.0.0-20200609044655-c4b36f998cf2 h1:tjT4Jp4gxECvsJcYpAMtW2I3YqzBTPuB67OejxXs86s=
github.com/common-nighthawk/go-figure v0.0.0-20200609044655-c4b36f998cf2/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w=
-github.com/containerd/containerd v1.7.15 h1:afEHXdil9iAm03BmhjzKyXnnEBtjaLJefdU7DV0IFes=
-github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K302C21/+cr3kUnY=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
-github.com/coocood/freecache v1.2.4 h1:UdR6Yz/X1HW4fZOuH0Z94KwG851GWOSknua5VUbb/5M=
-github.com/coocood/freecache v1.2.4/go.mod h1:RBUWa/Cy+OHdfTGFEhEuE1pMCMX51Ncizj7rthiQ3vk=
+github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
+github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
@@ -811,7 +794,6 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
-github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
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=
@@ -828,17 +810,16 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUn
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
-github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
+github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
+github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
-github.com/docker/docker v26.0.1+incompatible h1:t39Hm6lpXuXtgkF0dm1t9a5HkbUfdGy6XbWexmGr+hA=
-github.com/docker/docker v26.0.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v27.3.0+incompatible h1:BNb1QY6o4JdKpqwi9IB+HUYcRRrVN4aGFUTvDmWYK1A=
+github.com/docker/docker v27.3.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5 h1:iFaUwBSo5Svw6L7HYpRu/0lE3e0BaElwnNO1qkNQxBY=
github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
@@ -847,10 +828,8 @@ github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25Kn
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
-github.com/elazarl/goproxy v0.0.0-20170405201442-c4fc26588b6e/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
github.com/elliotchance/orderedmap/v2 v2.2.0 h1:7/2iwO98kYT4XkOjA9mBEIwvi4KpGB4cyHeOFOnj4Vk=
github.com/elliotchance/orderedmap/v2 v2.2.0/go.mod h1:85lZyVbpGaGvHvnKa7Qhx7zncAdBIBq6u56Hb1PRU5Q=
-github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
@@ -869,7 +848,6 @@ github.com/envoyproxy/protoc-gen-validate v0.6.7/go.mod h1:dyJXwwfPK2VSqiB9Klm1J
github.com/envoyproxy/protoc-gen-validate v0.9.1/go.mod h1:OKNgG7TCp5pF4d6XftA0++PMirau2/yoOwVac3AbF2w=
github.com/envoyproxy/protoc-gen-validate v0.10.0/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
-github.com/evanphx/json-patch v4.2.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.12.0 h1:mRhaKNwANqRgUBGKmnI5ZxEk7QXmjQeCcuYFMX2bfcc=
@@ -881,8 +859,6 @@ github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw
github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
-github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
-github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
@@ -892,7 +868,6 @@ github.com/getkin/kin-openapi v0.115.0 h1:c8WHRLVY3G8m9jQTy0/DnIuljgRwTCB5twZytQ
github.com/getkin/kin-openapi v0.115.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
-github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
@@ -934,7 +909,6 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
-github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -943,22 +917,15 @@ github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre
github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
-github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0=
github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=
github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg=
-github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc=
-github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I=
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng=
github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M=
-github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
-github.com/go-playground/form/v4 v4.2.0 h1:N1wh+Goz61e6w66vo8vJkQt+uwZSoLz50kZPJWR8eic=
-github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
@@ -970,13 +937,9 @@ github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
-github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
-github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
-github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
+github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
+github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
-github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-zookeeper/zk v1.0.3 h1:7M2kwOsc//9VeeFiPtf+uSJlVpU66x9Ba5+8XK7/TDg=
@@ -996,7 +959,6 @@ github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q8
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
@@ -1013,9 +975,8 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
-github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo=
-github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ=
-github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
+github.com/golang/glog v1.2.0 h1:uCdmnmatrKCgMBlM4rMuJZWOkPDqdbZPnrMXDY4gI68=
+github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
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=
@@ -1031,7 +992,6 @@ github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71
github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
-github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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=
@@ -1085,7 +1045,6 @@ github.com/google/go-github/v42 v42.0.0/go.mod h1:jgg/jvyI0YlDOM1/ps6XYh04HNQ3vK
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
-github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
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=
@@ -1107,13 +1066,10 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe
github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20230207041349-798e818bf904 h1:4/hN5RUoecvl+RmJRE2YxKWtnnQls6rQjjW5oV7qg2U=
-github.com/google/pprof v0.0.0-20230207041349-798e818bf904/go.mod h1:uglQLonpP8qtYCYyzA+8c/9qtqgA3qsXGYqCPKARAFg=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/s2a-go v0.1.0/go.mod h1:OJpEgntRZo8ugHpF9hkoLJbS5dSI20XZeXJ9JVywLlM=
github.com/google/s2a-go v0.1.3/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
github.com/google/s2a-go v0.1.4/go.mod h1:Ej+mSEMGRnqRzjc7VtF+jdBwYG5fuJfiZ8ELkjEwM0A=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@@ -1138,7 +1094,6 @@ github.com/googleapis/gax-go/v2 v2.7.1/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38
github.com/googleapis/gax-go/v2 v2.8.0/go.mod h1:4orTrqY6hXxxaUL4LHIPl6lGo8vAE38/qKbhSAKP6QI=
github.com/googleapis/gax-go/v2 v2.10.0/go.mod h1:4UOEnMCrxsSqQ940WnTiD6qJ63le2ev3xfyagutxiPw=
github.com/googleapis/gax-go/v2 v2.11.0/go.mod h1:DxmR61SGKkGLa2xigwuZIQpkCI2S5iydzRfb3peWZJI=
-github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY=
github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
@@ -1207,14 +1162,13 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
-github.com/hazelcast/hazelcast-go-client v1.4.1 h1:BSpJqqjbACI4MugfWXGxk+JdZR3JRELx0n769pa85kA=
-github.com/hazelcast/hazelcast-go-client v1.4.1/go.mod h1:PJ38lqXJ18S0YpkrRznPDlUH8GnnMAQCx3jpQtBPZ6Q=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
github.com/hyperjumptech/jiffy v1.0.0 h1:hLfjgh4YQPYFanSmh06nfN2Es7BZ1WF2sQwmZIQ5tHQ=
github.com/hyperjumptech/jiffy v1.0.0/go.mod h1:iFHHUap4onOTcvqBBU0iF33snPmqz4DSA/KgnBHG7dU=
-github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0=
github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
+github.com/iancoleman/strcase v0.3.0 h1:nTXanmYxhfFAMjZL34Ov6gkzEsSJZ5DbhxWjvSASxEI=
+github.com/iancoleman/strcase v0.3.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
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/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk=
@@ -1265,10 +1219,8 @@ github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
-github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
@@ -1297,7 +1249,6 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/kisielk/sqlstruct v0.0.0-20201105191214-5f3e10d3ab46/go.mod h1:yyMNCyc/Ib3bDTKd379tNMpB/7/H5TjM2Y9QJ5THLbE=
github.com/klauspost/asmfmt v1.3.2/go.mod h1:AG8TuvYojzulgDAMCnYn50l/5QV3Bs/tp6j0HLHbNSE=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
@@ -1350,7 +1301,6 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
-github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
@@ -1380,8 +1330,8 @@ github.com/mattn/go-runewidth v0.0.10 h1:CoZ3S2P7pvtP45xOtBw+/mDL2z0RKI576gSkzRR
github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
github.com/mattn/go-sqlite3 v1.14.14/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
-github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
+github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
+github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
@@ -1391,8 +1341,8 @@ github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQ
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
-github.com/microsoft/go-mssqldb v1.7.0 h1:sgMPW0HA6Ihd37Yx0MzHyKD726C2kY/8KJsQtXHNaAs=
-github.com/microsoft/go-mssqldb v1.7.0/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
+github.com/microsoft/go-mssqldb v1.7.2 h1:CHkFJiObW7ItKTJfHo1QX7QBBD1iV+mn1eOyRP3b/PA=
+github.com/microsoft/go-mssqldb v1.7.2/go.mod h1:kOvZKUdrhhFQmxLZqbwUV0rHkNkZpthMITIb2Ko1IoA=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
github.com/miekg/dns v1.1.54 h1:5jon9mWcb0sFJGpnI99tOMhCPyJ+RPVz5b63MQG0VWI=
github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
@@ -1417,12 +1367,13 @@ github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg=
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
+github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
+github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
@@ -1434,18 +1385,12 @@ github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJ
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
-github.com/morkid/gocache v1.0.0 h1:hTnU78Dqp2vs9al5vJC2TmmMF+Hm3nDH1AgRBjSXE+0=
-github.com/morkid/gocache v1.0.0/go.mod h1:xK+hmoEMjYffIBvjn7DE8WfSd/rF5Kz/G9f20OliMJY=
github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
-github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
-github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646 h1:zYyBkD/k9seD2A7fsi6Oo2LfFZAehjjQMERAvZLEDnQ=
-github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
github.com/nwaples/rardecode v1.1.3 h1:cWCaZwfM5H7nAD6PyEdcVnczzV8i/JtotnyW/dD9lEc=
@@ -1456,17 +1401,11 @@ github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
-github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=
github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/ginkgo/v2 v2.15.0 h1:79HwNRBAZHOEwrczrgSOPy+eFTTlIGELKy5as+ClttY=
-github.com/onsi/ginkgo/v2 v2.15.0/go.mod h1:HlxMHtYF57y6Dpf+mc5529KKmSq9h2FpCF+/ZkwUxKM=
-github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.31.1 h1:KYppCUK+bUgAZwHOu7EXVBKyQA6ILvOESHkn/tgoqvo=
@@ -1488,8 +1427,6 @@ github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTK
github.com/paulmach/orb v0.11.1 h1:3koVegMC4X/WeiXYz9iswopaTwMem53NzTJuTF20JzU=
github.com/paulmach/orb v0.11.1/go.mod h1:5mULz1xQfs3bmQm63QEJA6lNGujuRafwA5S/EnuLaLU=
github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
-github.com/pegasus-kv/thrift v0.13.0 h1:4ESwaNoHImfbHa9RUGJiJZ4hrxorihZHk5aarYwY8d4=
-github.com/pegasus-kv/thrift v0.13.0/go.mod h1:Gl9NT/WHG6ABm6NsrbfE8LiJN0sAyneCrvB4qN4NPqQ=
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
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=
@@ -1518,7 +1455,6 @@ 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.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
-github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
@@ -1584,12 +1520,6 @@ github.com/rbretecher/go-postman-collection v0.9.0 h1:vXw6KBhASpz0L0igH3OsJCx5pj
github.com/rbretecher/go-postman-collection v0.9.0/go.mod h1:pptkyjdB/sqPycH+CCa1zrA6Wpj2Kc8Nz846qRstVVs=
github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY=
github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
-github.com/redis/rueidis v1.0.38 h1:ZlEBumHM+ECCMgf/zQZImLfmxb/sxGKnBP0R0AxoH/Y=
-github.com/redis/rueidis v1.0.38/go.mod h1:bnbkk4+CkXZgDPEbUtSos/o55i4RhFYYesJ4DS2zmq0=
-github.com/redis/rueidis/mock v1.0.38 h1:E49z0pptMAs3xbWSW9ZgSp+cAmAiE4SvF7Q5mhDU9hw=
-github.com/redis/rueidis/mock v1.0.38/go.mod h1:oOKSGb4CKjvUzGCQ7PupeCEp6W+TdEfzR8YYLejU5Sk=
-github.com/redis/rueidis/rueidiscompat v1.0.38 h1:JREqnP+y4Gte4p66ZwBB2jT1m9W0RHFr+lRavpaGno4=
-github.com/redis/rueidis/rueidiscompat v1.0.38/go.mod h1:pQaGNQkzKsyxSY0BP/r5lrzP4kUdfWXb/gsquXhbDtA=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.1.0 h1:+2KBaVoUmb9XzDsrx/Ct0W/EYOSFf/nWTauy++DprtY=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
@@ -1663,7 +1593,6 @@ github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
@@ -1676,7 +1605,6 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
-github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
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=
@@ -1692,8 +1620,8 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
-github.com/testcontainers/testcontainers-go v0.31.0 h1:W0VwIhcEVhRflwL9as3dhY6jXjVCA27AkmbnZ+UTh3U=
-github.com/testcontainers/testcontainers-go v0.31.0/go.mod h1:D2lAoA0zUFiSY+eAflqK5mcUx/A5hrrORaEQrd0SefI=
+github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw=
+github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8=
github.com/tevid/gohamcrest v1.1.1 h1:ou+xSqlIw1xfGTg1uq1nif/htZ2S3EzRqLm2BP+tYU0=
github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
@@ -1721,6 +1649,8 @@ github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oW
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
+github.com/unionj-cloud/toolkit v0.0.1 h1:Q//MiUFDPZGBBnYucbHtJ5oEJ1df59wG6iTrhxQgHDs=
+github.com/unionj-cloud/toolkit v0.0.1/go.mod h1:/7fOlIX1aUs3MWYhyKDBNwUo2KI7eDw8M7x7CdZiViA=
github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
@@ -1732,29 +1662,31 @@ github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
-github.com/wubin1989/clickhouse v0.0.3-0.20240611142627-b655b8690cda h1:K8aGeEwIwMVGam4klbiXdxvDVAzh2I9P38QDr1Xubio=
-github.com/wubin1989/clickhouse v0.0.3-0.20240611142627-b655b8690cda/go.mod h1:pIVh0MTLUDKIGbkJGtAd3GuWuDXqJKFQtAYyrXftNcg=
-github.com/wubin1989/datatypes v0.0.2-0.20240611143143-56e1066d0d46 h1:2PtAVdFagYjrBlGJrtWqd3gHwWUwn6EHMIw9mflEP18=
-github.com/wubin1989/datatypes v0.0.2-0.20240611143143-56e1066d0d46/go.mod h1:zf0H8F3yk6l0WxQTtuf6c5cQ1KyMniqPGVi+wsP+Pwo=
-github.com/wubin1989/dbresolver v0.0.2-0.20240611143237-5d31d83b04ba h1:t8iGZmi0DDV3fFXQLTbvJo3oi6FIrcW+QoIq1JyaR40=
-github.com/wubin1989/dbresolver v0.0.2-0.20240611143237-5d31d83b04ba/go.mod h1:fdp9eyI05mcyy42qGZzxmxX9JMCMjeId9pzIb7ETN2M=
+github.com/wubin1989/clickhouse v0.0.3 h1:lHxjVsqgtf9091K45j2jul3Z52XLmyyR8CFby3kwKvw=
+github.com/wubin1989/clickhouse v0.0.3/go.mod h1:n6xglwUs3YRK4AaLWsytJio3+07UwpPhYy4lNvsWw1Q=
+github.com/wubin1989/datatypes v0.0.2 h1:a2ZiuWjqiYHRS+lkvan8QymxYtB1yvwJHf0kE7mqAnk=
+github.com/wubin1989/datatypes v0.0.2/go.mod h1:ifZrSL9ZeEfOqWxCvAx6ugbiMYcMPRyY2mSgN8qRdKE=
+github.com/wubin1989/dbresolver v0.0.2 h1:nxZJa7TSPRWvrHyshbBhoiHFt3/cagHi1qOySrCyqjE=
+github.com/wubin1989/dbresolver v0.0.2/go.mod h1:QCzD3CTzNXQ8Vk/V0WRdCQw2Mu8d/UGa3AyRIbbe9hc=
+github.com/wubin1989/gen v0.0.1 h1:KB5vgZwkGm2dDYjfUpBo2Q3VHaqQCTnw9VEzP8jhP9U=
+github.com/wubin1989/gen v0.0.1/go.mod h1:8nHzm1o5Rc3dGRTTCBoqIJisfevFiSzeGQX5IH7eP+I=
github.com/wubin1989/gorm v0.0.3/go.mod h1:sv3afa8/Q6XTgEbTTWTjJd0NIlBMH4bMq5VpEnXr/Cc=
-github.com/wubin1989/gorm v0.0.4 h1:ItRawZeMeGsGFw9H2biEV2o8Bg3qv+07MWwZrnZfLOE=
-github.com/wubin1989/gorm v0.0.4/go.mod h1:sv3afa8/Q6XTgEbTTWTjJd0NIlBMH4bMq5VpEnXr/Cc=
-github.com/wubin1989/hints v0.0.2-0.20240611143337-989d3cd7fd24 h1:ZCC6dSYWh4251NNDrQ8LnR8vGVTqJmUJQ6OvGyjj86c=
-github.com/wubin1989/hints v0.0.2-0.20240611143337-989d3cd7fd24/go.mod h1:D2RyfIt/EQDPauzJoc21JHaEPRDtkJXjpCyJii6Qowo=
-github.com/wubin1989/mysql v0.0.2-0.20240611142742-11722462c1f3 h1:23Whb/nEWyGXOrXs41cvzqZOKrtxE6iq4+9K5yhoRC0=
-github.com/wubin1989/mysql v0.0.2-0.20240611142742-11722462c1f3/go.mod h1:feHpEjnhL/oGHaiOwD2wUGhkWFHnTFrgk2/gRqIoit0=
+github.com/wubin1989/gorm v0.0.5 h1:eI+zjoQ2GEURnPBEh+H4axSD4sM0wWpJCAQZw+FfZX8=
+github.com/wubin1989/gorm v0.0.5/go.mod h1:q/wDV7AJMsbDdtv+K3eNX+VMpC0k1uxCqPBPXzHO90k=
+github.com/wubin1989/hints v0.0.2 h1:hPLqrRV4O15zS93mHhcCtQlpAfpG/YByVAo2KU7WdFk=
+github.com/wubin1989/hints v0.0.2/go.mod h1:XF4CQtyVTXmGtkHTYrHba/zcz761+71XqB3BDQPYd9s=
+github.com/wubin1989/mysql v0.0.2 h1:OgJ3LS6LfCBtWboQ2ghw6UXETMWyNb2fe2MLBELIpDg=
+github.com/wubin1989/mysql v0.0.2/go.mod h1:X6kei/oCZkghaulJzYjIEnFQLe59WYFMczj4hDObDN8=
github.com/wubin1989/nacos-sdk-go/v2 v2.1.2-0.20221024120645-0288f53fdaa8 h1:lFIaajFUmboXJ/sL/HUyg+9jakQVq5ohPToycWyt9ms=
github.com/wubin1989/nacos-sdk-go/v2 v2.1.2-0.20221024120645-0288f53fdaa8/go.mod h1:Z30xHaEyVwGKmbXHGM5uuh7pQkV66p/wiC3mGHCyhWQ=
-github.com/wubin1989/postgres v0.0.2-0.20240611142853-f89b1682d931 h1:QTaIX4r0bnus4AkfSbxSN2Qc0shwnkZGxXptx0icccQ=
-github.com/wubin1989/postgres v0.0.2-0.20240611142853-f89b1682d931/go.mod h1:v2djFEEBRl6SaR2UI7wRtRVwGXfarCQRU8cWHZKaKJE=
+github.com/wubin1989/postgres v0.0.2 h1:skgTBzvhXouB8sk8QW3CIL12IHc4yjxp0HFlmQWk+w0=
+github.com/wubin1989/postgres v0.0.2/go.mod h1:ak4xecaBWCvjryICTXlDtHwkK8QV1ibtBF/d2YzI+lw=
github.com/wubin1989/prometheus v0.0.2-0.20240611143457-960b4a499cef h1:jzuCPuY2BW5ia5xYfr0u1UL4K4jmB/spzPHZYcihcJA=
github.com/wubin1989/prometheus v0.0.2-0.20240611143457-960b4a499cef/go.mod h1:yK1GqsgQank3HXNrHDxpYnmjEu+K5MI4iIxjIhzWrFE=
-github.com/wubin1989/sqlite v0.0.3-0.20240611143051-c35482f79d92 h1:NIr2PCLZf2N1YTMkDRKOkGsGf3zNImp75Mq3/711WXU=
-github.com/wubin1989/sqlite v0.0.3-0.20240611143051-c35482f79d92/go.mod h1:DMWHq2F64sr/bnVXmbe5VX/6Y4orGPjX/rgcyOW7+K0=
-github.com/wubin1989/sqlserver v0.0.2-0.20240611142955-b9bee6e252c3 h1:6RF+iXbNg9iaSchnEsy0QTQld2HFiXlNvo+g5WfbnLs=
-github.com/wubin1989/sqlserver v0.0.2-0.20240611142955-b9bee6e252c3/go.mod h1:zoRF5ZmmvErOaz2im1Ef7fAR+KrUORC6LZbqdUeaOLQ=
+github.com/wubin1989/sqlite v0.0.3 h1:HpeLMcNQ0pFHCk3QNAYjYUt6OpZiWykBe1o5FC6NxTg=
+github.com/wubin1989/sqlite v0.0.3/go.mod h1:sJwi+4OqdP6KL56LUuJsqeubNREEpNpj+/Hp3YZ9zQI=
+github.com/wubin1989/sqlserver v0.0.2 h1:9KHzJ2/OC7ORsLO+7Lx/PAVabw9fG5AE6jw3kLDqI/c=
+github.com/wubin1989/sqlserver v0.0.2/go.mod h1:INhW70C5Zax3s+KDrU/6R6/2yueRe7gZ2+MUDGcxKUA=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
@@ -1873,8 +1805,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
-golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
-golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
+golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
+golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
@@ -1935,9 +1867,8 @@ golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.16.0 h1:QX4fJ0Rr5cPQCF7O9lh9Se4pmwfwskqZfq5moyldzic=
-golang.org/x/mod v0.16.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
-golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
+golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
+golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1958,8 +1889,6 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
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-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191105084925-a882066a44e0/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=
@@ -2018,8 +1947,8 @@ golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
-golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
-golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
+golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
+golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
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=
@@ -2070,9 +1999,8 @@ golang.org/x/sync v0.0.0-20220929204114-8fcdb60fdcc0/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.2.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
-golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
-golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
+golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
+golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -2094,12 +2022,10 @@ golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191010194322-b09406accb47/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -2129,7 +2055,6 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210112080510-489259a85091/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-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -2195,8 +2120,8 @@ golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
-golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
-golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
+golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -2214,9 +2139,8 @@ golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
-golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
-golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
-golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
+golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
+golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
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=
@@ -2236,8 +2160,9 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
+golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
+golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
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=
@@ -2249,7 +2174,6 @@ golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181221001348-537d06c36207/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -2304,7 +2228,6 @@ golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4f
golang.org/x/tools v0.0.0-20201124115921-2c860bdd6e78/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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
@@ -2320,8 +2243,8 @@ golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.7.0/go.mod h1:4pg6aUX35JBAogB10C9AtvVL+qowtN4pT3CGSQex14s=
-golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA=
-golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0=
+golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
+golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
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=
@@ -2545,21 +2468,21 @@ google.golang.org/genproto v0.0.0-20230403163135-c38d8f061ccd/go.mod h1:UUQDJDOl
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1/go.mod h1:nKE/iIaLqn2bQwXBg8f1g2Ylh6r5MN5CmZvuzZCgsCU=
google.golang.org/genproto v0.0.0-20230525234025-438c736192d0/go.mod h1:9ExIQyXL5hZrHzQceCwuSYwZZ5QZBazOcprJ5rgs3lY=
google.golang.org/genproto v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:xZnkP7mREFX5MORlOPEzLMr+90PPZQ2QWzrVTWfAq64=
-google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY=
-google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
+google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13 h1:vlzZttNJGVqTsRFU9AmdnrcO1Znh8Ew9kCD//yjigk0=
+google.golang.org/genproto v0.0.0-20230920204549-e6e6cdab5c13/go.mod h1:CCviP9RmpZ1mxVr8MUjCnSiY09IbAXZxhLE6EhHIdPU=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234020-1aefcd67740a/go.mod h1:ts19tUU+Z0ZShN1y3aPyq2+O3d5FUNNgT6FtOzmrNn8=
google.golang.org/genproto/googleapis/api v0.0.0-20230525234035-dd9d682886f9/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/api v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
google.golang.org/genproto/googleapis/api v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:vHYtlOoi6TsQ3Uk2yxR7NI5z8uoV+3pZtR4jmHIkRig=
-google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q=
-google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
+google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4=
+google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE=
google.golang.org/genproto/googleapis/bytestream v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:ylj+BE99M198VPbBh6A8d9n3w8fChvyLK3wwBOjXBFA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234015-3fc162c6f38a/go.mod h1:xURIpW9ES5+/GZhnV6beoEtxQrnkRGIfP5VQG2tCBLc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230525234030-28d5490b6b19/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230526203410-71b5a4ffd15e/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
google.golang.org/genproto/googleapis/rpc v0.0.0-20230530153820-e85fd2cbaebc/go.mod h1:66JfowdXAEgad5O9NnYcsNPLCPZJD++2L9X0PCMODrA=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY=
google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@@ -2602,8 +2525,8 @@ google.golang.org/grpc v1.52.0/go.mod h1:pu6fVzoFb+NBYNAvQL08ic+lvB2IojljRYuun5v
google.golang.org/grpc v1.53.0/go.mod h1:OnIrk0ipVdj4N5d9IUoFUx72/VlD7+jUsHwZgwSMQpw=
google.golang.org/grpc v1.54.0/go.mod h1:PUSEXI6iWghWaB6lXM4knEgpJNu2qUcKfDtNci3EC2g=
google.golang.org/grpc v1.55.0/go.mod h1:iYEXKGkEBhg1PjZQvoYEVPTDkHo1/bjTnfwTeGONTY8=
-google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
-google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
+google.golang.org/grpc v1.64.1 h1:LKtvyfbX3UGVPFcGqJ9ItpVWW6oN/2XqTxfAnwRRXiA=
+google.golang.org/grpc v1.64.1/go.mod h1:hiQF4LFZelK2WKaP6W0L92zGHtiQdZxk8CrSdvyjeP0=
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
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=
@@ -2637,7 +2560,6 @@ gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
-gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI=
gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -2647,8 +2569,6 @@ gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 h1:yiW+nvdHb9LVqSHQBXfZCieqV4fzYhNBql77zY0ykqs=
-gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637/go.mod h1:BHsqpu/nsuzkT5BpiH1EMZPLyqSMM8JbIavyFACoFNk=
gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME=
gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
@@ -2667,8 +2587,8 @@ gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C
gopkg.in/yaml.v3 v3.0.0/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=
-gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY=
-gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
+gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
+gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
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=
@@ -2677,13 +2597,6 @@ honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt
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=
honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las=
-k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3/go.mod h1:b9qmWdKlLuU9EBh+06BtLcSf/Mu89rWL33naRxs1uZg=
-k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ=
-k8s.io/apimachinery v0.26.2/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I=
-k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
-k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I=
-k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E=
lukechampine.com/uint128 v1.1.1/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.36.0/go.mod h1:NFUHyPn4ekoC/JHeZFfZurN6ixxawE1BnVonP/oahEI=
@@ -2723,5 +2636,3 @@ rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI=
-sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
diff --git a/toolkit/assert/assert.go b/toolkit/assert/assert.go
deleted file mode 100644
index f4ee0960..00000000
--- a/toolkit/assert/assert.go
+++ /dev/null
@@ -1,36 +0,0 @@
-package assert
-
-import (
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
-)
-
-func NotNil(input interface{}, format string, v ...interface{}) {
- if input == nil {
- zlogger.Panic().Msgf(format, v...)
- }
-}
-
-func NotTrue(input bool, format string, v ...interface{}) {
- if input {
- zlogger.Panic().Msgf(format, v...)
- }
-}
-
-func True(input bool, format string, v ...interface{}) {
- if !input {
- zlogger.Panic().Msgf(format, v...)
- }
-}
-
-func NotEmpty(input string, format string, v ...interface{}) {
- if stringutils.IsEmpty(input) {
- zlogger.Panic().Msgf(format, v...)
- }
-}
-
-func Empty(input string, format string, v ...interface{}) {
- if stringutils.IsNotEmpty(input) {
- zlogger.Panic().Msgf(format, v...)
- }
-}
diff --git a/toolkit/astutils/enumcollector.go b/toolkit/astutils/enumcollector.go
deleted file mode 100644
index bb1df43b..00000000
--- a/toolkit/astutils/enumcollector.go
+++ /dev/null
@@ -1,107 +0,0 @@
-package astutils
-
-import (
- "go/ast"
- "go/parser"
- "go/token"
- "strings"
-)
-
-type EnumCollector struct {
- Methods map[string][]MethodMeta
- Package PackageMeta
- exprString func(ast.Expr) string
- Consts map[string][]string
-}
-
-// Visit traverse each node from source code
-func (sc *EnumCollector) Visit(n ast.Node) ast.Visitor {
- return sc.Collect(n)
-}
-
-// Collect collects all structs from source code
-func (sc *EnumCollector) Collect(n ast.Node) ast.Visitor {
- switch spec := n.(type) {
- case *ast.Package:
- return sc
- case *ast.File: // actually it is package name
- sc.Package = PackageMeta{
- Name: spec.Name.Name,
- }
- return sc
- case *ast.FuncDecl:
- if spec.Recv != nil {
- typeName := strings.TrimPrefix(sc.exprString(spec.Recv.List[0].Type), "*")
- methods, _ := sc.Methods[typeName]
- methods = append(methods, GetMethodMeta(spec))
- if sc.Methods == nil {
- sc.Methods = make(map[string][]MethodMeta)
- }
- sc.Methods[typeName] = methods
- }
- case *ast.GenDecl:
- if spec.Tok == token.CONST {
- var comments []string
- if spec.Doc != nil {
- for _, comment := range spec.Doc.List {
- comments = append(comments, strings.TrimSpace(strings.TrimPrefix(comment.Text, "//")))
- }
- }
- var typeName string
- for _, item := range spec.Specs {
- valueSpec := item.(*ast.ValueSpec)
- if len(valueSpec.Names) == 0 {
- continue
- }
- switch specType := valueSpec.Type.(type) {
- case *ast.Ident:
- typeName = specType.Name
- case nil:
- // useless
- //if len(valueSpec.Values) > 0 {
- // switch valueExpr := valueSpec.Values[0].(type) {
- // case *ast.BasicLit:
- // switch valueExpr.Kind {
- // case token.INT:
- // typeName = "int"
- // case token.FLOAT:
- // typeName = "float64"
- // case token.IMAG:
- // typeName = "complex128"
- // case token.CHAR:
- // typeName = "rune"
- // case token.STRING:
- // typeName = "string"
- // default:
- // continue
- // }
- // }
- //}
- }
- sc.Consts[typeName] = append(sc.Consts[typeName], valueSpec.Names[0].Name)
- }
- }
- }
- return nil
-}
-
-// NewEnumCollector initializes an EnumCollector
-func NewEnumCollector(exprString func(ast.Expr) string) *EnumCollector {
- return &EnumCollector{
- Methods: make(map[string][]MethodMeta),
- Package: PackageMeta{},
- exprString: exprString,
- Consts: make(map[string][]string),
- }
-}
-
-func EnumsOf(file string, exprString func(ast.Expr) string) *EnumCollector {
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := NewEnumCollector(exprString)
- ast.Walk(sc, root)
- return sc
-}
diff --git a/toolkit/astutils/enumcollector_test.go b/toolkit/astutils/enumcollector_test.go
deleted file mode 100644
index 86409747..00000000
--- a/toolkit/astutils/enumcollector_test.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package astutils
-
-import (
- "go/ast"
- "go/parser"
- "go/token"
- "testing"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
-)
-
-func TestEnum(t *testing.T) {
- file := pathutils.Abs("testdata/enum.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := NewEnumCollector(ExprString)
- ast.Walk(sc, root)
-}
diff --git a/toolkit/astutils/funcs.go b/toolkit/astutils/funcs.go
deleted file mode 100644
index 34418119..00000000
--- a/toolkit/astutils/funcs.go
+++ /dev/null
@@ -1,492 +0,0 @@
-package astutils
-
-import (
- "bufio"
- "bytes"
- "encoding/json"
- "fmt"
- "github.com/goccy/go-reflect"
- "go/ast"
- "go/format"
- "io/ioutil"
- "os"
- "path/filepath"
- "regexp"
- "strings"
- "text/template"
- "unicode"
-
- "github.com/samber/lo"
- "github.com/sirupsen/logrus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-)
-
-func GetImportStatements(input []byte) []byte {
- reg := regexp.MustCompile("(?s)import \\((.*?)\\)")
- if !reg.Match(input) {
- return nil
- }
- matches := reg.FindSubmatch(input)
- return matches[1]
-}
-
-func AppendImportStatements(src []byte, appendImports []byte) []byte {
- reg := regexp.MustCompile("(?s)import \\((.*?)\\)")
- if !reg.Match(src) {
- return src
- }
- matches := reg.FindSubmatch(src)
- old := matches[1]
- re := regexp.MustCompile(`[\r\n]+`)
- splits := re.Split(string(old), -1)
- oldmap := make(map[string]struct{})
- for _, item := range splits {
- oldmap[strings.TrimSpace(item)] = struct{}{}
- }
- splits = re.Split(string(appendImports), -1)
- var newimps []string
- for _, item := range splits {
- key := strings.TrimSpace(item)
- if _, ok := oldmap[key]; !ok {
- newimps = append(newimps, "\t"+key)
- }
- }
- if len(newimps) == 0 {
- return src
- }
- appendImports = []byte(constants.LineBreak + strings.Join(newimps, constants.LineBreak) + constants.LineBreak)
- return reg.ReplaceAllFunc(src, func(i []byte) []byte {
- old = append([]byte("import ("), old...)
- old = append(old, appendImports...)
- old = append(old, []byte(")")...)
- return old
- })
-}
-
-func GrpcRelatedModify(src []byte, metaName string, grpcSvcName string) []byte {
- expr := fmt.Sprintf(`type %sImpl struct {`, metaName)
- reg := regexp.MustCompile(expr)
- unimpl := fmt.Sprintf("pb.Unimplemented%sServer", grpcSvcName)
- if !strings.Contains(string(src), unimpl) {
- appendUnimpl := []byte(constants.LineBreak + unimpl + constants.LineBreak)
- src = reg.ReplaceAllFunc(src, func(i []byte) []byte {
- return append([]byte(expr), appendUnimpl...)
- })
- }
- var_pb := fmt.Sprintf("var _ pb.%sServer = (*%sImpl)(nil)", grpcSvcName, metaName)
- if !strings.Contains(string(src), var_pb) {
- appendVarPb := []byte(constants.LineBreak + var_pb + constants.LineBreak)
- src = reg.ReplaceAllFunc(src, func(i []byte) []byte {
- return append(appendVarPb, []byte(expr)...)
- })
- }
- return src
-}
-
-func RestRelatedModify(src []byte, metaName string) []byte {
- expr := fmt.Sprintf(`type %sImpl struct {`, metaName)
- reg := regexp.MustCompile(expr)
- var_ := fmt.Sprintf("var _ %s = (*%sImpl)(nil)", metaName, metaName)
- if !strings.Contains(string(src), var_) {
- appendVarPb := []byte(constants.LineBreak + var_ + constants.LineBreak)
- src = reg.ReplaceAllFunc(src, func(i []byte) []byte {
- return append(appendVarPb, []byte(expr)...)
- })
- }
- return src
-}
-
-// FixImport format source code and add missing import syntax automatically
-func FixImport(src []byte, file string) {
- //defer utils.TimeTrack(time.Now(), fmt.Sprintf("FixImport: %s", file), logrus.StandardLogger())
- // here import format cause performance issue on some computer
- //var (
- // res []byte
- // err error
- //)
- //if res, err = imports.Process(file, src, &imports.Options{
- // TabWidth: 8,
- // TabIndent: true,
- // Comments: true,
- // Fragment: true,
- //}); err != nil {
- // lines := strings.Split(string(src), "\n")
- // errLine, _ := strconv.Atoi(strings.Split(err.Error(), ":")[1])
- // startLine, endLine := errLine-5, errLine+5
- // fmt.Println("Format fail:", errLine, err)
- // if startLine < 0 {
- // startLine = 0
- // }
- // if endLine > len(lines)-1 {
- // endLine = len(lines) - 1
- // }
- // for i := startLine; i <= endLine; i++ {
- // fmt.Println(i, lines[i])
- // }
- // errors.WithStack(fmt.Errorf("cannot format file: %w", err))
- //} else {
- // _ = ioutil.WriteFile(file, res, os.ModePerm)
- // return
- //}
- _ = ioutil.WriteFile(file, src, os.ModePerm)
-}
-
-// GetMethodMeta get method name then new MethodMeta struct from *ast.FuncDecl
-func GetMethodMeta(spec *ast.FuncDecl) MethodMeta {
- methodName := ExprString(spec.Name)
- mm := NewMethodMeta(spec.Type, ExprString)
- mm.Name = methodName
- return mm
-}
-
-// NewMethodMeta new MethodMeta struct from *ast.FuncDecl
-func NewMethodMeta(ft *ast.FuncType, exprString func(ast.Expr) string) MethodMeta {
- var params, results []FieldMeta
- for _, param := range ft.Params.List {
- pt := exprString(param.Type)
- if len(param.Names) > 0 {
- for _, name := range param.Names {
- params = append(params, FieldMeta{
- Name: name.Name,
- Type: pt,
- Tag: "",
- })
- }
- continue
- }
- params = append(params, FieldMeta{
- Name: "",
- Type: pt,
- Tag: "",
- })
- }
- if ft.Results != nil {
- for _, result := range ft.Results.List {
- rt := exprString(result.Type)
- if len(result.Names) > 0 {
- for _, name := range result.Names {
- results = append(results, FieldMeta{
- Name: name.Name,
- Type: rt,
- Tag: "",
- })
- }
- continue
- }
- results = append(results, FieldMeta{
- Name: "",
- Type: rt,
- Tag: "",
- })
- }
- }
- return MethodMeta{
- Params: params,
- Results: results,
- }
-}
-
-// NewStructMeta new StructMeta from *ast.StructType
-func NewStructMeta(structType *ast.StructType, exprString func(ast.Expr) string) StructMeta {
- var fields []FieldMeta
- re := regexp.MustCompile(`json:"(.*?)"`)
- for _, field := range structType.Fields.List {
- var fieldComments []string
- if field.Doc != nil {
- for _, comment := range field.Doc.List {
- fieldComments = append(fieldComments, strings.TrimSpace(strings.TrimPrefix(comment.Text, "//")))
- }
- }
-
- fieldType := exprString(field.Type)
-
- var tag string
- var docName string
- if field.Tag != nil {
- tag = strings.Trim(field.Tag.Value, "`")
- if re.MatchString(tag) {
- jsonTag := re.FindStringSubmatch(tag)[1]
- docName = strings.Split(jsonTag, ",")[0]
- }
- }
-
- if len(field.Names) > 0 {
- for _, name := range field.Names {
- _docName := docName
- if stringutils.IsEmpty(_docName) {
- _docName = name.Name
- }
- fields = append(fields, FieldMeta{
- Name: name.Name,
- Type: fieldType,
- Tag: tag,
- Comments: fieldComments,
- IsExport: unicode.IsUpper(rune(name.Name[0])),
- DocName: _docName,
- })
- }
- } else {
- splits := strings.Split(fieldType, ".")
- name := splits[len(splits)-1]
- fieldType = "embed:" + fieldType
- _docName := docName
- if stringutils.IsEmpty(_docName) {
- _docName = name
- }
- fields = append(fields, FieldMeta{
- Name: name,
- Type: fieldType,
- Tag: tag,
- Comments: fieldComments,
- IsExport: unicode.IsUpper(rune(name[0])),
- DocName: _docName,
- })
- }
- }
- return StructMeta{
- Fields: fields,
- }
-}
-
-// PackageMeta wraps package info
-type PackageMeta struct {
- Name string
-}
-
-// FieldMeta wraps field info
-type FieldMeta struct {
- Name string
- Type string
- Tag string
- Comments []string
- IsExport bool
- // used in OpenAPI 3.0 spec as property name
- DocName string
- // Annotations of the field
- Annotations []Annotation
- // ValidateTag based on https://github.com/go-playground/validator
- // please refer to its documentation https://pkg.go.dev/github.com/go-playground/validator/v10
- ValidateTag string
- IsPathVariable bool
-}
-
-// StructMeta wraps struct info
-type StructMeta struct {
- Name string
- Fields []FieldMeta
- Comments []string
- Methods []MethodMeta
- IsExport bool
- // go-doudou version
- Version string
-}
-
-// EnumMeta wraps struct info
-type EnumMeta struct {
- Name string
- Values []string
-}
-
-// ExprString return string representation from ast.Expr
-func ExprString(expr ast.Expr) string {
- switch _expr := expr.(type) {
- case *ast.Ident:
- return _expr.Name
- case *ast.StarExpr:
- return "*" + ExprString(_expr.X)
- case *ast.SelectorExpr:
- return ExprString(_expr.X) + "." + _expr.Sel.Name
- case *ast.InterfaceType:
- return "interface{}"
- case *ast.ArrayType:
- if _expr.Len == nil {
- return "[]" + ExprString(_expr.Elt)
- }
- return "[" + ExprString(_expr.Len) + "]" + ExprString(_expr.Elt)
- case *ast.BasicLit:
- return _expr.Value
- case *ast.MapType:
- return "map[" + ExprString(_expr.Key) + "]" + ExprString(_expr.Value)
- case *ast.StructType:
- structmeta := NewStructMeta(_expr, ExprString)
- b, _ := json.Marshal(structmeta)
- return "anonystruct«" + string(b) + "»"
- case *ast.FuncType:
- return NewMethodMeta(_expr, ExprString).String()
- case *ast.ChanType:
- var result string
- if _expr.Dir == ast.SEND {
- result += "chan<- "
- } else if _expr.Dir == ast.RECV {
- result += "<-chan "
- } else {
- result += "chan "
- }
- return result + ExprString(_expr.Value)
- case *ast.Ellipsis:
- if _expr.Ellipsis.IsValid() {
- return "..." + ExprString(_expr.Elt)
- }
- panic(fmt.Sprintf("invalid ellipsis expression: %+v\n", expr))
- case *ast.IndexExpr:
- return ExprString(_expr.X) + "[" + ExprString(_expr.Index) + "]"
- case *ast.IndexListExpr:
- typeParams := lo.Map[ast.Expr, string](_expr.Indices, func(item ast.Expr, index int) string {
- return ExprString(item)
- })
- return ExprString(_expr.X) + "[" + strings.Join(typeParams, ", ") + "]"
- default:
- logrus.Infof("not support expression: %+v\n", expr)
- logrus.Infof("not support expression: %+v\n", reflect.TypeOf(expr))
- logrus.Infof("not support expression: %#v\n", reflect.TypeOf(expr))
- logrus.Infof("not support expression: %v\n", reflect.TypeOf(expr).String())
- return ""
- //panic(fmt.Sprintf("not support expression: %+v\n", expr))
- }
-}
-
-type Annotation struct {
- Name string
- Params []string
-}
-
-var reAnno = regexp.MustCompile(`@(\S+?)\((.*?)\)`)
-
-func GetAnnotations(text string) []Annotation {
- if !reAnno.MatchString(text) {
- return nil
- }
- var annotations []Annotation
- matches := reAnno.FindAllStringSubmatch(text, -1)
- for _, item := range matches {
- name := fmt.Sprintf(`@%s`, item[1])
- var params []string
- if stringutils.IsNotEmpty(item[2]) {
- params = strings.Split(strings.TrimSpace(item[2]), ",")
- }
- annotations = append(annotations, Annotation{
- Name: name,
- Params: params,
- })
- }
- return annotations
-}
-
-// MethodMeta represents an api
-type MethodMeta struct {
- // Recv method receiver
- Recv string
- // Name method name
- Name string
- // Params when generate client code from openapi3 spec json file, Params holds all method input parameters.
- // when generate client code from service interface in svc.go file, if there is struct type param, this struct type param will put into request body,
- // then others will be put into url as query string. if there is no struct type param and the api is a get request, all will be put into url as query string.
- // if there is no struct type param and the api is Not a get request, all will be put into request body as application/x-www-form-urlencoded data.
- // specially, if there is one or more v3.FileModel or []v3.FileModel params,
- // all will be put into request body as multipart/form-data data.
- Params []FieldMeta
- // Results response
- Results []FieldMeta
- QueryVars []FieldMeta
- // PathVars not support when generate client code from service interface in svc.go file
- // when generate client code from openapi3 spec json file, PathVars is parameters in url as path variable.
- PathVars []FieldMeta
- // HeaderVars not support when generate client code from service interface in svc.go file
- // when generate client code from openapi3 spec json file, HeaderVars is parameters in header.
- HeaderVars []FieldMeta
- // BodyParams not support when generate client code from service interface in svc.go file
- // when generate client code from openapi3 spec json file, BodyParams is parameters in request body as query string.
- BodyParams *FieldMeta
- // BodyJSON not support when generate client code from service interface in svc.go file
- // when generate client code from openapi3 spec json file, BodyJSON is parameters in request body as json.
- BodyJSON *FieldMeta
- // Files not support when generate client code from service interface in svc.go file
- // when generate client code from openapi3 spec json file, Files is parameters in request body as multipart file.
- Files []FieldMeta
- // Comments of the method
- Comments []string
- // Path api path
- // not support when generate client code from service interface in svc.go file
- Path string
- // QueryParams not support when generate client code from service interface in svc.go file
- // when generate client code from openapi3 spec json file, QueryParams is parameters in url as query string.
- QueryParams *FieldMeta
- // Annotations of the method
- Annotations []Annotation
- HasPathVariable bool
- // HttpMethod only accepts GET, PUT, POST, DELETE
- HttpMethod string
-}
-
-const methodTmpl = `func {{ if .Recv }}(receiver {{.Recv}}){{ end }} {{.Name}}({{- range $i, $p := .Params}}
- {{- if $i}},{{end}}
- {{- $p.Name}} {{$p.Type}}
- {{- end }}) ({{- range $i, $r := .Results}}
- {{- if $i}},{{end}}
- {{- $r.Name}} {{$r.Type}}
- {{- end }})`
-
-func (mm MethodMeta) String() string {
- if stringutils.IsNotEmpty(mm.Recv) && stringutils.IsEmpty(mm.Name) {
- panic("not valid code")
- }
- var isAnony bool
- if stringutils.IsEmpty(mm.Name) {
- isAnony = true
- mm.Name = "placeholder"
- }
- t, _ := template.New("method.tmpl").Parse(methodTmpl)
- var buf bytes.Buffer
- _ = t.Execute(&buf, mm)
- var res []byte
- res, _ = format.Source(buf.Bytes())
- result := string(res)
- if isAnony {
- return strings.Replace(result, "func placeholder(", "func(", 1)
- }
- return result
-}
-
-// InterfaceMeta wraps interface info
-type InterfaceMeta struct {
- Name string
- Methods []MethodMeta
- Comments []string
-}
-
-// Visit visit each files
-func Visit(files *[]string) filepath.WalkFunc {
- return func(path string, info os.FileInfo, err error) error {
- if err != nil {
- logrus.Panicln(err)
- }
- if !info.IsDir() {
- *files = append(*files, path)
- }
- return nil
- }
-}
-
-// GetMod get module name from go.mod file
-func GetMod() string {
- var (
- f *os.File
- err error
- firstLine string
- )
- dir, _ := os.Getwd()
- mod := filepath.Join(dir, "go.mod")
- if f, err = os.Open(mod); err != nil {
- panic(err)
- }
- reader := bufio.NewReader(f)
- firstLine, _ = reader.ReadString('\n')
- return strings.TrimSpace(strings.TrimPrefix(firstLine, "module"))
-}
-
-// GetImportPath get import path of pkg from dir
-func GetImportPath(dir string) string {
- wd, _ := os.Getwd()
- return GetMod() + strings.ReplaceAll(strings.TrimPrefix(dir, wd), `\`, `/`)
-}
diff --git a/toolkit/astutils/funcs_test.go b/toolkit/astutils/funcs_test.go
deleted file mode 100644
index 8ccc8f41..00000000
--- a/toolkit/astutils/funcs_test.go
+++ /dev/null
@@ -1,463 +0,0 @@
-package astutils
-
-import (
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "io/ioutil"
- "os"
- "path/filepath"
- "testing"
-
- "github.com/davecgh/go-spew/spew"
- "github.com/sirupsen/logrus"
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-)
-
-func TestFixImport(t *testing.T) {
- code := `package main
-
-import (
- "encoding/json"
- "fmt"
-)
-
-type UserVo struct {
- Id int
- Name string
- Phone string
- Dept string
-}
-
-type Page struct {
- PageNo int
-Size int
-Items []UserVo
-}
-
-func main() {
- page := Page{
- PageNo: 10,
- Size: 30,
-}
-b, _ := json.Marshal(page)
-fmt.Println(string(b))
-}
-`
- expect := `package main
-
-import (
- "encoding/json"
- "fmt"
-)
-
-type UserVo struct {
- Id int
- Name string
- Phone string
- Dept string
-}
-
-type Page struct {
- PageNo int
- Size int
- Items []UserVo
-}
-
-func main() {
- page := Page{
- PageNo: 10,
- Size: 30,
- }
- b, _ := json.Marshal(page)
- fmt.Println(string(b))
-}
-`
- file := pathutils.Abs("testdata/output.go")
- FixImport([]byte(code), file)
- f, err := os.Open(file)
- if err != nil {
- t.Fatal(err)
- }
- got, err := ioutil.ReadAll(f)
- if err != nil {
- t.Fatal(err)
- }
- assert.Equal(t, expect, string(got))
-}
-
-func TestMethodMeta_String(t *testing.T) {
- type fields struct {
- Recv string
- Name string
- Params []FieldMeta
- Results []FieldMeta
- Comments []string
- }
- tests := []struct {
- name string
- fields fields
- want string
- panic bool
- }{
- {
- name: "",
- fields: fields{
- Recv: "handler",
- Name: "HandleEvent",
- Params: []FieldMeta{
- {
- Name: "ctx",
- Type: "context.Context",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- {
- Name: "etype",
- Type: "int",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- {
- Name: "uid",
- Type: "string",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- },
- Results: []FieldMeta{
- {
- Name: "",
- Type: "bool",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- {
- Name: "",
- Type: "error",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- },
- Comments: nil,
- },
- want: "func (receiver handler) HandleEvent(ctx context.Context, etype int, uid string) (bool, error)",
- },
- {
- name: "",
- fields: fields{
- Recv: "",
- Name: "HandleEvent",
- Params: []FieldMeta{
- {
- Name: "ctx",
- Type: "context.Context",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- {
- Name: "etype",
- Type: "int",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- {
- Name: "uid",
- Type: "string",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- },
- Results: []FieldMeta{
- {
- Name: "",
- Type: "error",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- },
- Comments: nil,
- },
- want: "func HandleEvent(ctx context.Context, etype int, uid string) error",
- },
- {
- name: "",
- fields: fields{
- Recv: "",
- Name: "",
- Params: []FieldMeta{
- {
- Name: "etype",
- Type: "int",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- {
- Name: "uid",
- Type: "string",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- },
- Results: []FieldMeta{
- {
- Name: "",
- Type: "error",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- },
- Comments: nil,
- },
- want: "func(etype int, uid string) error",
- },
- {
- name: "",
- fields: fields{
- Recv: "PlaceHolder",
- Name: "",
- Params: []FieldMeta{
- {
- Name: "etype",
- Type: "int",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- {
- Name: "uid",
- Type: "string",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- },
- Results: []FieldMeta{
- {
- Name: "",
- Type: "error",
- Tag: "",
- Comments: nil,
- IsExport: false,
- DocName: "",
- },
- },
- Comments: nil,
- },
- want: "",
- panic: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- mm := MethodMeta{
- Recv: tt.fields.Recv,
- Name: tt.fields.Name,
- Params: tt.fields.Params,
- Results: tt.fields.Results,
- Comments: tt.fields.Comments,
- }
- if tt.panic {
- assert.Panics(t, func() {
- mm.String()
- })
- } else {
- if got := mm.String(); stringutils.IsNotEmpty(tt.want) && got != tt.want {
- t.Errorf("String() = %v, want %v", got, tt.want)
- }
- }
- })
- }
-}
-
-func TestVisit(t *testing.T) {
- testDir := pathutils.Abs("./testdata")
- vodir := filepath.Join(testDir, "vo")
- var files []string
- err := filepath.Walk(vodir, Visit(&files))
- if err != nil {
- logrus.Panicln(err)
- }
- assert.Len(t, files, 1)
-}
-
-func TestNewMethodMeta(t *testing.T) {
- file := pathutils.Abs("testdata/cat.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := NewStructCollector(ExprString)
- ast.Walk(sc, root)
- spew.Dump(root)
-}
-
-func TestGetImportStatements(t *testing.T) {
- input := `import (
- "context"
- "encoding/json"
- "fmt"
- "github.com/sirupsen/logrus"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
- "github.com/unionj-cloud/go-doudou/v2/framework/rest"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
- {{.ServiceAlias}} "{{.ServicePackage}}"
- "net/http"
- "github.com/pkg/errors"
-)`
- ret := GetImportStatements([]byte(input))
- fmt.Println(string(ret))
-}
-
-func TestAppendImportStatements(t *testing.T) {
- input := `import (
- "context"
- "encoding/json"
- "fmt"
-
- "github.com/sirupsen/logrus"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
- "github.com/unionj-cloud/go-doudou/v2/framework/rest"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
-
- {{.ServiceAlias}} "{{.ServicePackage}}"
- "net/http"
-)
-
-type UsersvcHandlerImpl struct {
- usersvc service.Usersvc
-}
-`
- ret := AppendImportStatements([]byte(input), []byte(`
- "context"
- "encoding/json"
- "fmt"
- "github.com/pkg/errors"
-`))
-
- ret = AppendImportStatements(ret, []byte(`
- "context"
- "encoding/json"
- "fmt"
- "github.com/pkg/errors"
-`))
-
- ret = AppendImportStatements(ret, []byte(`
- "context"
- "encoding/json"
- "fmt"
- "github.com/pkg/errors"
-`))
-
- expected := `import (
- "context"
- "encoding/json"
- "fmt"
-
- "github.com/sirupsen/logrus"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/openapi/v3"
- "github.com/unionj-cloud/go-doudou/v2/framework/rest"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
-
- {{.ServiceAlias}} "{{.ServicePackage}}"
- "net/http"
-
- "github.com/pkg/errors"
-)
-
-type UsersvcHandlerImpl struct {
- usersvc service.Usersvc
-}
-`
-
- fmt.Println(string(ret))
- require.Equal(t, expected, string(ret))
-}
-
-func TestAppendImportStatements1(t *testing.T) {
- input := `import ()`
- ret := AppendImportStatements([]byte(input), []byte(`
- "context"
- "encoding/json"
- "fmt"
- "github.com/pkg/errors"
-`))
- expected := `import (
- "context"
- "encoding/json"
- "fmt"
- "github.com/pkg/errors"
-)`
-
- fmt.Println(string(ret))
- require.Equal(t, expected, string(ret))
-}
-
-func TestAppendImportStatements2(t *testing.T) {
- input := `import ()`
- ret := AppendImportStatements([]byte(input), []byte(`
-
-`))
- expected := input
-
- fmt.Println(string(ret))
- require.Equal(t, expected, string(ret))
-}
-
-func ExampleGetAnnotations() {
- ret := GetAnnotations(`// NEW 删除数据接口(不删数据文件)@role(SUPER_ADMIN)@permission(create,update)这是几个注解@sss()`)
- fmt.Println(ret)
- // Output:
- // [{@role [SUPER_ADMIN]} {@permission [create update]} {@sss []}]
-}
-
-func TestGrpcRelatedModify(t *testing.T) {
- input := `
-var _ Helloworld = (*HelloworldImpl)(nil)
-
-type HelloworldImpl struct {
- conf *config.Config
-}
-
-func (receiver *HelloworldImpl) Greeting(ctx context.Context, greeting string) (data string, err error) {
- var _result struct {
- Data string
- }
- _ = gofakeit.Struct(&_result)
- return _result.Data, nil
-}
-`
- ret := GrpcRelatedModify([]byte(input), "Helloworld", "HelloworldRpc")
- fmt.Println(string(ret))
-}
diff --git a/toolkit/astutils/helper.go b/toolkit/astutils/helper.go
deleted file mode 100644
index cf0b28ff..00000000
--- a/toolkit/astutils/helper.go
+++ /dev/null
@@ -1,99 +0,0 @@
-package astutils
-
-import (
- "bufio"
- "github.com/pkg/errors"
- "go/ast"
- "go/parser"
- "go/token"
- "os"
- "path/filepath"
- "strings"
-)
-
-func IsSlice(t string) bool {
- return strings.Contains(t, "[") || strings.HasPrefix(t, "...")
-}
-
-func IsVarargs(t string) bool {
- return strings.HasPrefix(t, "...")
-}
-
-func ToSlice(t string) string {
- return "[]" + strings.TrimPrefix(t, "...")
-}
-
-// ElementType get element type string from slice
-func ElementType(t string) string {
- if IsVarargs(t) {
- return strings.TrimPrefix(t, "...")
- }
- return t[strings.Index(t, "]")+1:]
-}
-
-func CollectStructsInFolder(dir string, sc *StructCollector) {
- dir, _ = filepath.Abs(dir)
- var files []string
- err := filepath.Walk(dir, Visit(&files))
- if err != nil {
- panic(err)
- }
- for _, file := range files {
- if filepath.Ext(file) != ".go" {
- continue
- }
- root, err := parser.ParseFile(token.NewFileSet(), file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- ast.Walk(sc, root)
- }
-}
-
-//func GetPkgPath1(filePath string) string {
-// pkgs, err := packages.Load(&packages.Config{
-// Mode: packages.NeedName,
-// Dir: filePath,
-// })
-// if err != nil {
-// panic(err)
-// }
-// if len(pkgs) == 0 {
-// panic(errors.New("no package found"))
-// }
-// if len(pkgs[0].Errors) > 0 {
-// for _, err = range pkgs[0].Errors {
-// panic(err)
-// }
-// }
-// return pkgs[0].PkgPath
-//}
-
-func GetPkgPath(filePath string) string {
- modf, err := FindGoMod(filePath)
- if err != nil {
- panic(err)
- }
- defer modf.Close()
- reader := bufio.NewReader(modf)
- firstLine, _ := reader.ReadString('\n')
- modName := strings.TrimSpace(strings.TrimPrefix(firstLine, "module"))
- filePath, _ = filepath.Abs(filePath)
- filePath = filepath.ToSlash(filePath)
- result := filePath[strings.Index(filePath, modName):]
- return result
-}
-
-func FindGoMod(filePath string) (*os.File, error) {
- path, _ := filepath.Abs(filePath)
- if os.IsPathSeparator(path[0]) {
- return nil, errors.New("Can not find go.mod")
- }
- var err error
- mf := filepath.Join(path, "go.mod")
- var f *os.File
- if f, err = os.Open(mf); err != nil {
- return FindGoMod(filepath.Dir(path))
- }
- return f, nil
-}
diff --git a/toolkit/astutils/helper_test.go b/toolkit/astutils/helper_test.go
deleted file mode 100644
index 6eaddc7c..00000000
--- a/toolkit/astutils/helper_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package astutils
-
-import (
- "os"
- "path/filepath"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestGetPkgPath(t *testing.T) {
- workDir, _ := filepath.Abs("./testdata")
- oldDir, _ := os.Getwd()
- os.Chdir(workDir)
- defer func() {
- os.Chdir(oldDir)
- }()
- type args struct {
- filePath string
- }
- tests := []struct {
- name string
- args args
- want string
- }{
- {
- name: "",
- args: args{
- filePath: filepath.Join(workDir, "demo", "config"),
- },
- want: "testdata/demo/config",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- assert.Equalf(t, tt.want, GetPkgPath(tt.args.filePath), "GetPkgPath(%v)", tt.args.filePath)
- })
- }
-}
diff --git a/toolkit/astutils/interfacecollector.go b/toolkit/astutils/interfacecollector.go
deleted file mode 100644
index 80c18bae..00000000
--- a/toolkit/astutils/interfacecollector.go
+++ /dev/null
@@ -1,303 +0,0 @@
-package astutils
-
-import (
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "net/http"
- "regexp"
- "strings"
-
- "github.com/iancoleman/strcase"
- "github.com/sirupsen/logrus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-)
-
-// InterfaceCollector collect interfaces by parsing source code
-type InterfaceCollector struct {
- Interfaces []InterfaceMeta
- Package PackageMeta
- exprString func(ast.Expr) string
- cmap ast.CommentMap
-}
-
-// Visit traverse each node from source code
-func (ic *InterfaceCollector) Visit(n ast.Node) ast.Visitor {
- return ic.Collect(n)
-}
-
-// Collect collects all interfaces from source code
-func (ic *InterfaceCollector) Collect(n ast.Node) ast.Visitor {
- switch spec := n.(type) {
- case *ast.Package:
- return ic
- case *ast.File: // actually it is package name
- ic.Package = PackageMeta{
- Name: spec.Name.Name,
- }
- return ic
- case *ast.GenDecl:
- if spec.Tok == token.TYPE {
- comments := doc2Comments(spec.Doc)
- for _, item := range spec.Specs {
- typeSpec := item.(*ast.TypeSpec)
- typeName := typeSpec.Name.Name
- switch specType := typeSpec.Type.(type) {
- case *ast.InterfaceType:
- ic.Interfaces = append(ic.Interfaces, InterfaceMeta{
- Name: typeName,
- Methods: ic.field2Methods(specType.Methods.List),
- Comments: comments,
- })
- }
- }
- }
- }
- return nil
-}
-
-// GetShelves_ShelfBooks_Book
-// shelves/:shelf/books/:book
-func Pattern(method string) (httpMethod string, endpoint string) {
- httpMethods := []string{http.MethodGet, http.MethodPost, http.MethodPut, http.MethodDelete}
- re1, err := regexp.Compile("_?[A-Z]")
- if err != nil {
- panic(err)
- }
- method = re1.ReplaceAllStringFunc(method, func(s string) string {
- if strings.HasPrefix(s, "_") {
- return "/:" + strings.ToLower(strings.TrimPrefix(s, "_"))
- } else {
- return "/" + strings.ToLower(s)
- }
- })
- splits := strings.Split(method, "/")[1:]
- httpMethod = httpMethods[1]
- head := strings.ToUpper(splits[0])
- if sliceutils.StringContains(httpMethods, head) {
- httpMethod = head
- splits = splits[1:]
- }
- return httpMethod, strings.Join(splits, "/")
-}
-
-func pathVariables(endpoint string) (ret []string) {
- splits := strings.Split(endpoint, "/")
- pvs := sliceutils.StringFilter(splits, func(item string) bool {
- return stringutils.IsNotEmpty(item) && strings.HasPrefix(item, ":")
- })
- for _, v := range pvs {
- ret = append(ret, strings.TrimPrefix(v, ":"))
- }
- return
-}
-
-func (ic *InterfaceCollector) field2Methods(list []*ast.Field) []MethodMeta {
- var methods []MethodMeta
- for _, method := range list {
- if len(method.Names) == 0 {
- panic("no method name")
- }
- mn := method.Names[0].Name
-
- var mComments []string
- var annotations []Annotation
- if method.Doc != nil {
- for _, comment := range method.Doc.List {
- mComments = append(mComments, strings.TrimSpace(strings.TrimPrefix(comment.Text, "//")))
- annotations = append(annotations, GetAnnotations(comment.Text)...)
- }
- }
-
- ft, _ := method.Type.(*ast.FuncType)
- var params []FieldMeta
- if ft.Params != nil {
- params = ic.field2Params(ft.Params.List)
- }
-
- queryVars := make([]FieldMeta, 0)
- httpMethod, endpoint := Pattern(mn)
- pvs := pathVariables(endpoint)
- for i := range params {
- if sliceutils.StringContains(pvs, params[i].Name) {
- params[i].IsPathVariable = true
- }
- if httpMethod == http.MethodGet && !params[i].IsPathVariable && params[i].Type != "context.Context" {
- queryVars = append(queryVars, params[i])
- }
- }
-
- var results []FieldMeta
- if ft.Results != nil {
- results = ic.field2Results(ft.Results.List)
- }
- methods = append(methods, MethodMeta{
- Name: mn,
- Params: params,
- Results: results,
- Comments: mComments,
- Annotations: annotations,
- HasPathVariable: len(pvs) > 0,
- HttpMethod: httpMethod,
- QueryVars: queryVars,
- })
- }
- return methods
-}
-
-func (ic *InterfaceCollector) field2Params(list []*ast.Field) []FieldMeta {
- var params []FieldMeta
- pkeymap := make(map[string]int)
- for _, param := range list {
- pt := ic.exprString(param.Type)
- if len(param.Names) > 0 {
- for i, name := range param.Names {
- field := FieldMeta{
- Name: name.Name,
- Type: pt,
- }
- var cnode ast.Node
- if i == 0 {
- cnode = param
- } else {
- cnode = name
- }
- if cmts, exists := ic.cmap[cnode]; exists {
- for _, comment := range cmts[0].List {
- field.Comments = append(field.Comments, strings.TrimSpace(strings.TrimPrefix(comment.Text, "//")))
- field.Annotations = append(field.Annotations, GetAnnotations(comment.Text)...)
- }
- }
- var validateTags []string
- for _, item := range field.Annotations {
- if item.Name == "@validate" {
- validateTags = append(validateTags, item.Params...)
- }
- }
- field.ValidateTag = strings.Join(validateTags, ",")
- params = append(params, field)
- }
- continue
- }
- var pComments []string
- var annotations []Annotation
- if cmts, exists := ic.cmap[param]; exists {
- for _, comment := range cmts[0].List {
- pComments = append(pComments, strings.TrimSpace(strings.TrimPrefix(comment.Text, "//")))
- annotations = append(annotations, GetAnnotations(comment.Text)...)
- }
- }
- var pn string
- elemt := strings.TrimPrefix(pt, "*")
- if stringutils.IsNotEmpty(elemt) {
- if strings.Contains(elemt, "[") {
- elemt = elemt[strings.Index(elemt, "]")+1:]
- elemt = strings.TrimPrefix(elemt, "*")
- }
- splits := strings.Split(elemt, ".")
- _key := "p" + strcase.ToLowerCamel(splits[len(splits)-1][0:1])
- if _, exists := pkeymap[_key]; exists {
- pkeymap[_key]++
- pn = _key + fmt.Sprintf("%d", pkeymap[_key])
- } else {
- pkeymap[_key]++
- pn = _key
- }
- }
- var validateTags []string
- for _, item := range annotations {
- if item.Name == "@validate" {
- validateTags = append(validateTags, item.Params...)
- }
- }
- params = append(params, FieldMeta{
- Name: pn,
- Type: pt,
- Comments: pComments,
- Annotations: annotations,
- ValidateTag: strings.Join(validateTags, ","),
- })
- }
- return params
-}
-
-func (ic *InterfaceCollector) field2Results(list []*ast.Field) []FieldMeta {
- var results []FieldMeta
- rkeymap := make(map[string]int)
- for _, result := range list {
- var rComments []string
- if cmts, exists := ic.cmap[result]; exists {
- for _, comment := range cmts[0].List {
- rComments = append(rComments, strings.TrimSpace(strings.TrimPrefix(comment.Text, "//")))
- }
- }
- rt := ic.exprString(result.Type)
- if len(result.Names) > 0 {
- for _, name := range result.Names {
- results = append(results, FieldMeta{
- Name: name.Name,
- Type: rt,
- Tag: "",
- Comments: rComments,
- })
- }
- continue
- }
- var rn string
- elemt := strings.TrimPrefix(rt, "*")
- if stringutils.IsNotEmpty(elemt) {
- if strings.Contains(elemt, "[") {
- elemt = elemt[strings.Index(elemt, "]")+1:]
- elemt = strings.TrimPrefix(elemt, "*")
- }
- splits := strings.Split(elemt, ".")
- _key := "r" + strcase.ToLowerCamel(splits[len(splits)-1][0:1])
- if _, exists := rkeymap[_key]; exists {
- rkeymap[_key]++
- rn = _key + fmt.Sprintf("%d", rkeymap[_key])
- } else {
- rkeymap[_key]++
- rn = _key
- }
- }
- results = append(results, FieldMeta{
- Name: rn,
- Type: rt,
- Tag: "",
- Comments: rComments,
- })
- }
- return results
-}
-
-func doc2Comments(doc *ast.CommentGroup) []string {
- var comments []string
- if doc != nil {
- for _, comment := range doc.List {
- comments = append(comments, strings.TrimSpace(strings.TrimPrefix(comment.Text, "//")))
- }
- }
- return comments
-}
-
-// NewInterfaceCollector initializes an InterfaceCollector
-func NewInterfaceCollector(exprString func(ast.Expr) string) *InterfaceCollector {
- return &InterfaceCollector{
- exprString: exprString,
- }
-}
-
-// BuildInterfaceCollector initializes an InterfaceCollector and collects interfaces
-func BuildInterfaceCollector(file string, exprString func(ast.Expr) string) InterfaceCollector {
- ic := NewInterfaceCollector(exprString)
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- logrus.Panicln(err)
- }
- ic.cmap = ast.NewCommentMap(fset, root, root.Comments)
- ast.Walk(ic, root)
- return *ic
-}
diff --git a/toolkit/astutils/interfacecollector_test.go b/toolkit/astutils/interfacecollector_test.go
deleted file mode 100644
index fc1fb68e..00000000
--- a/toolkit/astutils/interfacecollector_test.go
+++ /dev/null
@@ -1,106 +0,0 @@
-package astutils
-
-import (
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
-)
-
-func TestInterfaceCollector(t *testing.T) {
- file := pathutils.Abs("testdata/svc.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- //spew.Dump(root)
- sc := NewInterfaceCollector(ExprString)
- sc.cmap = ast.NewCommentMap(fset, root, root.Comments)
- ast.Walk(sc, root)
- fmt.Println(sc)
-}
-
-func TestBuildInterfaceCollector(t *testing.T) {
- file := pathutils.Abs("testdata/svc.go")
- ic := BuildInterfaceCollector(file, ExprString)
- assert.NotNil(t, ic)
-}
-
-func Test_pattern(t *testing.T) {
- type args struct {
- method string
- }
- tests := []struct {
- name string
- args args
- want string
- }{
- {
- name: "1",
- args: args{
- method: "GetBooks",
- },
- want: "books",
- },
- {
- name: "2",
- args: args{
- method: "PageUsers",
- },
- want: "page/users",
- },
- {
- name: "",
- args: args{
- method: "Get",
- },
- want: "",
- },
- {
- name: "",
- args: args{
- method: "GetShelves_ShelfBooks_Book",
- },
- want: "shelves/:shelf/books/:book",
- },
- {
- name: "",
- args: args{
- method: "Goodfood_BigappleBooks_Mybird",
- },
- want: "goodfood/:bigapple/books/:mybird",
- },
- {
- name: "",
- args: args{
- method: "ApiV1Query_range",
- },
- want: "api/v1/query_range",
- },
- {
- name: "",
- args: args{
- method: "GetQuery_range",
- },
- want: "query_range",
- },
- {
- name: "",
- args: args{
- method: "GetQuery",
- },
- want: "query",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- _, endpoint := Pattern(tt.args.method)
- assert.Equalf(t, tt.want, endpoint, "pattern(%v)", tt.args.method)
- })
- }
-}
diff --git a/toolkit/astutils/rewritejsontag.go b/toolkit/astutils/rewritejsontag.go
deleted file mode 100644
index 407e7b50..00000000
--- a/toolkit/astutils/rewritejsontag.go
+++ /dev/null
@@ -1,133 +0,0 @@
-package astutils
-
-import (
- "bytes"
- "fmt"
- "go/ast"
- "go/format"
- "go/parser"
- "go/token"
- "regexp"
- "strings"
- "unicode"
-
- "github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
- "golang.org/x/tools/go/ast/astutil"
-)
-
-func isExport(field string) bool {
- return unicode.IsUpper([]rune(field)[0])
-}
-
-func extractJsonPropName(tag string) string {
- re := regexp.MustCompile(`json:"(.*?)"`)
- if re.MatchString(tag) {
- subs := re.FindAllStringSubmatch(tag, -1)
- return strings.TrimSpace(strings.Split(subs[0][1], ",")[0])
- }
- return ""
-}
-
-func extractFormPropName(tag string) string {
- re := regexp.MustCompile(`form:"(.*?)"`)
- if re.MatchString(tag) {
- subs := re.FindAllStringSubmatch(tag, -1)
- return strings.TrimSpace(strings.Split(subs[0][1], ",")[0])
- }
- return ""
-}
-
-type RewriteTagConfig struct {
- File string
- Omitempty bool
- ConvertFunc func(old string) string
- Form bool
-}
-
-// RewriteTag overwrites json tag by convert function and return formatted source code
-func RewriteTag(config RewriteTagConfig) (string, error) {
- file, convert, omitempty, form := config.File, config.ConvertFunc, config.Omitempty, config.Form
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- re := regexp.MustCompile(`json:"(.*?)"`)
- reForm := regexp.MustCompile(`form:"(.*?)"`)
- astutil.Apply(root, func(cursor *astutil.Cursor) bool {
- return true
- }, func(cursor *astutil.Cursor) bool {
- structSpec, ok := cursor.Node().(*ast.StructType)
- if !ok {
- return true
- }
- for _, field := range structSpec.Fields.List {
- if field.Names == nil {
- continue
- }
- fname := field.Names[0].Name
- if !isExport(fname) {
- continue
- }
- tagValue := convert(field.Names[0].Name)
- jsonTagValue := tagValue
- if omitempty {
- jsonTagValue += ",omitempty"
- }
- jsonTag := fmt.Sprintf(`json:"%s"`, jsonTagValue)
-
- formTagValue := tagValue
- if omitempty {
- formTagValue += ",omitempty"
- }
- formTag := fmt.Sprintf(`form:"%s"`, formTagValue)
-
- if field.Tag != nil {
- if re.MatchString(field.Tag.Value) {
- if extractJsonPropName(field.Tag.Value) != "-" {
- field.Tag.Value = re.ReplaceAllLiteralString(field.Tag.Value, jsonTag)
- }
- } else {
- lastindex := strings.LastIndex(field.Tag.Value, "`")
- if lastindex < 0 {
- panic(errors.New("call LastIndex() error"))
- }
- field.Tag.Value = field.Tag.Value[:lastindex] + fmt.Sprintf(" %s`", jsonTag)
- }
- if form {
- if reForm.MatchString(field.Tag.Value) {
- if extractFormPropName(field.Tag.Value) != "-" {
- field.Tag.Value = reForm.ReplaceAllLiteralString(field.Tag.Value, formTag)
- }
- } else {
- lastindex := strings.LastIndex(field.Tag.Value, "`")
- if lastindex < 0 {
- panic(errors.New("call LastIndex() error"))
- }
- field.Tag.Value = field.Tag.Value[:lastindex] + fmt.Sprintf(" %s`", formTag)
- }
- }
- } else {
- if form {
- field.Tag = &ast.BasicLit{
- Kind: token.STRING,
- Value: fmt.Sprintf("`%s %s`", jsonTag, formTag),
- }
- } else {
- field.Tag = &ast.BasicLit{
- Kind: token.STRING,
- Value: fmt.Sprintf("`%s`", jsonTag),
- }
- }
- }
- }
- return true
- })
- buf := &bytes.Buffer{}
- err = format.Node(buf, fset, root)
- if err != nil {
- return "", fmt.Errorf("error formatting new code: %w", err)
- }
- return buf.String(), nil
-}
diff --git a/toolkit/astutils/rewritejsontag_test.go b/toolkit/astutils/rewritejsontag_test.go
deleted file mode 100644
index 071c3cb6..00000000
--- a/toolkit/astutils/rewritejsontag_test.go
+++ /dev/null
@@ -1,149 +0,0 @@
-package astutils
-
-import (
- "fmt"
- "testing"
-
- "github.com/iancoleman/strcase"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
-)
-
-func ExampleRewriteTag() {
- file := pathutils.Abs("testdata/rewritejsontag.go")
- config := RewriteTagConfig{
- File: file,
- Omitempty: true,
- ConvertFunc: strcase.ToLowerCamel,
- Form: false,
- }
- result, err := RewriteTag(config)
- if err != nil {
- panic(err)
- }
- fmt.Println(result)
- // Output:
- //package main
- //
- //type base struct {
- // Index string `json:"index,omitempty"`
- // Type string `json:"type,omitempty"`
- //}
- //
- //type struct1 struct {
- // base
- // Name string `json:"name,omitempty"`
- // StructType int `json:"structType,omitempty" dd:"awesomtag"`
- // Format string `dd:"anothertag" json:"format,omitempty"`
- // Pos int `json:"pos,omitempty"`
- //}
-}
-
-func Test_isExport(t *testing.T) {
- type args struct {
- field string
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {
- name: "",
- args: args{
- field: "unExportField",
- },
- want: false,
- },
- {
- name: "",
- args: args{
- field: "ExportField",
- },
- want: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := isExport(tt.args.field); got != tt.want {
- t.Errorf("isExport() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func Test_extractJsonPropName(t *testing.T) {
- type args struct {
- tag string
- }
- tests := []struct {
- name string
- args args
- want string
- }{
- {
- name: "",
- args: args{
- tag: `json:"name, omitempty"`,
- },
- want: "name",
- },
- {
- name: "",
- args: args{
- tag: `json:"name"`,
- },
- want: "name",
- },
- {
- name: "",
- args: args{
- tag: `json:"-, omitempty"`,
- },
- want: "-",
- },
- {
- name: "",
- args: args{
- tag: `json:"-"`,
- },
- want: "-",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := extractJsonPropName(tt.args.tag); got != tt.want {
- t.Errorf("extractJsonPropName() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func ExampleRewriteTagForm() {
- file := pathutils.Abs("testdata/rewritejsontag.go")
- config := RewriteTagConfig{
- File: file,
- Omitempty: true,
- ConvertFunc: strcase.ToLowerCamel,
- Form: true,
- }
- result, err := RewriteTag(config)
- if err != nil {
- panic(err)
- }
- fmt.Println(result)
- // Output:
- //package main
- //
- //type base struct {
- // Index string `json:"index,omitempty" form:"index,omitempty"`
- // Type string `json:"type,omitempty" form:"type,omitempty"`
- //}
- //
- //type struct1 struct {
- // base
- // Name string `json:"name,omitempty" form:"name,omitempty"`
- // StructType int `json:"structType,omitempty" dd:"awesomtag" form:"structType,omitempty"`
- // Format string `dd:"anothertag" json:"format,omitempty" form:"format,omitempty"`
- // Pos int `json:"pos,omitempty" form:"pos,omitempty"`
- //}
-}
diff --git a/toolkit/astutils/staticmethodcollector.go b/toolkit/astutils/staticmethodcollector.go
deleted file mode 100644
index 34686c7e..00000000
--- a/toolkit/astutils/staticmethodcollector.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package astutils
-
-import (
- "go/ast"
- "go/parser"
- "go/token"
-
- "github.com/sirupsen/logrus"
-)
-
-// StaticMethodCollector collect methods by parsing source code
-type StaticMethodCollector struct {
- Methods []MethodMeta
- Package PackageMeta
- exprString func(ast.Expr) string
-}
-
-// Visit traverse each node from source code
-func (sc *StaticMethodCollector) Visit(n ast.Node) ast.Visitor {
- return sc.Collect(n)
-}
-
-// Collect collects all static methods from source code
-func (sc *StaticMethodCollector) Collect(n ast.Node) ast.Visitor {
- switch spec := n.(type) {
- case *ast.Package:
- return sc
- case *ast.File: // actually it is package name
- sc.Package = PackageMeta{
- Name: spec.Name.Name,
- }
- return sc
- case *ast.FuncDecl:
- if spec.Recv == nil {
- sc.Methods = append(sc.Methods, GetMethodMeta(spec))
- }
- case *ast.GenDecl:
- }
- return nil
-}
-
-type StaticMethodCollectorOption func(collector *StaticMethodCollector)
-
-// NewStaticMethodCollector initializes an StaticMethodCollector
-func NewStaticMethodCollector(exprString func(ast.Expr) string, opts ...StaticMethodCollectorOption) *StaticMethodCollector {
- sc := &StaticMethodCollector{
- Methods: make([]MethodMeta, 0),
- Package: PackageMeta{},
- exprString: exprString,
- }
- for _, opt := range opts {
- opt(sc)
- }
- return sc
-}
-
-// BuildStaticMethodCollector initializes an StaticMethodCollector and collects static methods
-func BuildStaticMethodCollector(file string, exprString func(ast.Expr) string) StaticMethodCollector {
- sc := NewStaticMethodCollector(exprString)
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- logrus.Panicln(err)
- }
- ast.Walk(sc, root)
- return *sc
-}
diff --git a/toolkit/astutils/structcollector.go b/toolkit/astutils/structcollector.go
deleted file mode 100644
index 436d9b93..00000000
--- a/toolkit/astutils/structcollector.go
+++ /dev/null
@@ -1,175 +0,0 @@
-package astutils
-
-import (
- "go/ast"
- "go/parser"
- "go/token"
- "regexp"
- "strings"
- "unicode"
-
- "github.com/sirupsen/logrus"
-)
-
-// StructCollector collect structs by parsing source code
-type StructCollector struct {
- Structs []StructMeta
- Methods map[string][]MethodMeta
- Package PackageMeta
- NonStructTypeMap map[string]ast.Expr
- exprString func(ast.Expr) string
- enums map[string]EnumMeta
- GlobalStructs []StructMeta
-}
-
-// Visit traverse each node from source code
-func (sc *StructCollector) Visit(n ast.Node) ast.Visitor {
- return sc.Collect(n)
-}
-
-// Collect collects all structs from source code
-func (sc *StructCollector) Collect(n ast.Node) ast.Visitor {
- switch spec := n.(type) {
- case *ast.Package:
- return sc
- case *ast.File: // actually it is package name
- sc.Package = PackageMeta{
- Name: spec.Name.Name,
- }
- return sc
- case *ast.FuncDecl:
- if spec.Recv != nil {
- typeName := strings.TrimPrefix(sc.exprString(spec.Recv.List[0].Type), "*")
- methods, _ := sc.Methods[typeName]
- methods = append(methods, GetMethodMeta(spec))
- if sc.Methods == nil {
- sc.Methods = make(map[string][]MethodMeta)
- }
- sc.Methods[typeName] = methods
- }
- case *ast.GenDecl:
- if spec.Tok == token.TYPE {
- var comments []string
- if spec.Doc != nil {
- for _, comment := range spec.Doc.List {
- comments = append(comments, strings.TrimSpace(strings.TrimPrefix(comment.Text, "//")))
- }
- }
- for _, item := range spec.Specs {
- typeSpec := item.(*ast.TypeSpec)
- typeName := typeSpec.Name.Name
- switch specType := typeSpec.Type.(type) {
- case *ast.StructType:
- structmeta := NewStructMeta(specType, sc.exprString)
- structmeta.Name = typeName
- structmeta.Comments = comments
- structmeta.IsExport = unicode.IsUpper(rune(typeName[0]))
- sc.Structs = append(sc.Structs, structmeta)
- default:
- sc.NonStructTypeMap[typeName] = typeSpec.Type
- }
- }
- }
- }
- return nil
-}
-
-// DocFlatEmbed flatten embed struct fields
-func (sc *StructCollector) DocFlatEmbed() []StructMeta {
- structs := sc.GlobalStructs
- if len(structs) == 0 {
- structs = sc.Structs
- }
- structMap := make(map[string]StructMeta)
- for _, structMeta := range structs {
- if _, exists := structMap[structMeta.Name]; !exists {
- structMap[structMeta.Name] = structMeta
- }
- }
-
- var exStructs []StructMeta
- for _, structMeta := range structs {
- if !structMeta.IsExport {
- continue
- }
- exStructs = append(exStructs, structMeta)
- }
-
- re := regexp.MustCompile(`json:"(.*?)"`)
- var result []StructMeta
- for _, structMeta := range exStructs {
- _structMeta := StructMeta{
- Name: structMeta.Name,
- Fields: make([]FieldMeta, 0),
- Comments: make([]string, len(structMeta.Comments)),
- IsExport: true,
- }
- copy(_structMeta.Comments, structMeta.Comments)
- fieldMap := make(map[string]FieldMeta)
- embedFieldMap := make(map[string]FieldMeta)
- for _, fieldMeta := range structMeta.Fields {
- if strings.HasPrefix(fieldMeta.Type, "embed") {
- if re.MatchString(fieldMeta.Tag) {
- fieldMeta.Type = strings.TrimPrefix(fieldMeta.Type, "embed:")
- _structMeta.Fields = append(_structMeta.Fields, fieldMeta)
- fieldMap[fieldMeta.Name] = fieldMeta
- } else {
- if embedded, exists := structMap[fieldMeta.Name]; exists {
- for _, field := range embedded.Fields {
- if !field.IsExport {
- continue
- }
- embedFieldMap[field.Name] = field
- }
- }
- }
- } else if fieldMeta.IsExport {
- _structMeta.Fields = append(_structMeta.Fields, fieldMeta)
- fieldMap[fieldMeta.Name] = fieldMeta
- }
- }
-
- for key, field := range embedFieldMap {
- if _, exists := fieldMap[key]; !exists {
- _structMeta.Fields = append(_structMeta.Fields, field)
- }
- }
- result = append(result, _structMeta)
- }
- return result
-}
-
-type StructCollectorOption func(collector *StructCollector)
-
-func WithEnums(enums map[string]EnumMeta) StructCollectorOption {
- return func(collector *StructCollector) {
- collector.enums = enums
- }
-}
-
-// NewStructCollector initializes an StructCollector
-func NewStructCollector(exprString func(ast.Expr) string, opts ...StructCollectorOption) *StructCollector {
- sc := &StructCollector{
- Structs: nil,
- Methods: make(map[string][]MethodMeta),
- Package: PackageMeta{},
- NonStructTypeMap: make(map[string]ast.Expr),
- exprString: exprString,
- }
- for _, opt := range opts {
- opt(sc)
- }
- return sc
-}
-
-// BuildStructCollector initializes an StructCollector and collects structs
-func BuildStructCollector(file string, exprString func(ast.Expr) string) StructCollector {
- sc := NewStructCollector(exprString)
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- logrus.Panicln(err)
- }
- ast.Walk(sc, root)
- return *sc
-}
diff --git a/toolkit/astutils/structcollector_test.go b/toolkit/astutils/structcollector_test.go
deleted file mode 100644
index 6719ca35..00000000
--- a/toolkit/astutils/structcollector_test.go
+++ /dev/null
@@ -1,252 +0,0 @@
-package astutils
-
-import (
- "encoding/json"
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "regexp"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
-)
-
-func TestStruct(t *testing.T) {
- file := pathutils.Abs("testdata/vo.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := NewStructCollector(ExprString)
- ast.Walk(sc, root)
- fmt.Println(sc.Structs)
-}
-
-func TestInter(t *testing.T) {
- file := pathutils.Abs("testdata/svc.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := NewInterfaceCollector(ExprString)
- ast.Walk(sc, root)
- fmt.Println(sc.Interfaces)
-}
-
-func TestStructFuncDecl(t *testing.T) {
- file := pathutils.Abs("testdata/cat.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := NewStructCollector(ExprString)
- ast.Walk(sc, root)
- methods, exists := sc.Methods["Cat"]
- if !exists {
- t.Error("Cat should has methods")
- }
- if len(methods) != 1 {
- t.Error("Cat should has only one method")
- }
-}
-
-func TestRegex(t *testing.T) {
- re := regexp.MustCompile(`anonystruct«(.*)»`)
- a := `[]anonystruct«{"Name":"","Fields":[{"Name":"Name","Type":"string","Tag":"","Comments":null,"IsExport":true,"DocName":"Name"},{"Name":"Addr","Type":"anonystruct«{\"Name\":\"\",\"Fields\":[{\"Name\":\"Zip\",\"Type\":\"string\",\"Tag\":\"\",\"Comments\":null,\"IsExport\":true,\"DocName\":\"Zip\"},{\"Name\":\"Block\",\"Type\":\"string\",\"Tag\":\"\",\"Comments\":null,\"IsExport\":true,\"DocName\":\"Block\"},{\"Name\":\"Full\",\"Type\":\"string\",\"Tag\":\"\",\"Comments\":null,\"IsExport\":true,\"DocName\":\"Full\"}],\"Comments\":null,\"Methods\":null,\"IsExport\":false}»","Tag":"","Comments":null,"IsExport":true,"DocName":"Addr"}],"Comments":null,"Methods":null,"IsExport":false}»`
- result := re.FindStringSubmatch(a)
- fmt.Println(result[1])
-
- j := result[1]
- var structmeta StructMeta
- json.Unmarshal([]byte(j), &structmeta)
-}
-
-func TestStructCollector_DocFlatEmbed(t *testing.T) {
- file := pathutils.Abs("testdata/embed.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := NewStructCollector(ExprString)
- ast.Walk(sc, root)
- structs := sc.DocFlatEmbed()
- for _, item := range structs {
- if item.Name == "TestEmbed" {
- var fields []string
- var docFields []string
- for _, fieldMeta := range item.Fields {
- fields = append(fields, fieldMeta.Name)
- docFields = append(docFields, fieldMeta.DocName)
- }
- assert.ElementsMatch(t, fields, []string{"Fields", "Type", "Index"})
- assert.ElementsMatch(t, docFields, []string{"fields", "type", "index"})
- }
- }
-}
-
-func TestStructCollector_DocFlatEmbed1(t *testing.T) {
- file := pathutils.Abs("testdata/embed1.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := NewStructCollector(ExprString)
- ast.Walk(sc, root)
- structs := sc.DocFlatEmbed()
- for _, item := range structs {
- if item.Name == "TestEmbed1" {
- var fields []string
- var docFields []string
- for _, fieldMeta := range item.Fields {
- fields = append(fields, fieldMeta.Name)
- docFields = append(docFields, fieldMeta.DocName)
- }
- assert.ElementsMatch(t, fields, []string{"Fields", "TestBase1"})
- assert.ElementsMatch(t, docFields, []string{"fields", "test_base_1"})
- }
- }
-}
-
-func TestStructCollector_DocFlatEmbed_ExcludeUnexportedFields(t *testing.T) {
- file := pathutils.Abs("testdata/embed2.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := NewStructCollector(ExprString)
- ast.Walk(sc, root)
- structs := sc.DocFlatEmbed()
- for _, item := range structs {
- if item.Name == "TestEmbed2" {
- var fields []string
- var docFields []string
- for _, fieldMeta := range item.Fields {
- fields = append(fields, fieldMeta.Name)
- docFields = append(docFields, fieldMeta.DocName)
- }
- assert.ElementsMatch(t, fields, []string{"Fields", "Index", "Type"})
- assert.ElementsMatch(t, docFields, []string{"fields", "index", "type"})
- }
- }
-}
-
-func TestStructCollector_DocFlatEmbed_ExcludeUnexportedFields2(t *testing.T) {
- file := pathutils.Abs("testdata/embed3.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := NewStructCollector(ExprString)
- ast.Walk(sc, root)
- structs := sc.DocFlatEmbed()
- for _, item := range structs {
- if item.Name == "TestEmbed3" {
- var fields []string
- var docFields []string
- for _, fieldMeta := range item.Fields {
- fields = append(fields, fieldMeta.Name)
- docFields = append(docFields, fieldMeta.DocName)
- }
- assert.ElementsMatch(t, fields, []string{"Fields", "Type"})
- assert.ElementsMatch(t, docFields, []string{"fields", "type"})
- }
- }
-}
-
-func TestStructCollector_DocFlatEmbed_ExcludeUnexportedFields3(t *testing.T) {
- file := pathutils.Abs("testdata/embed4.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := NewStructCollector(ExprString)
- ast.Walk(sc, root)
- structs := sc.DocFlatEmbed()
- for _, item := range structs {
- if item.Name == "TestEmbed4" {
- var fields []string
- var docFields []string
- for _, fieldMeta := range item.Fields {
- fields = append(fields, fieldMeta.Name)
- docFields = append(docFields, fieldMeta.DocName)
- }
- assert.ElementsMatch(t, fields, []string{"Fields", "TestBase1"})
- assert.ElementsMatch(t, docFields, []string{"fields", "test_base_1"})
- }
- if item.Name == "TestBase4" {
- var fields []string
- var docFields []string
- for _, fieldMeta := range item.Fields {
- fields = append(fields, fieldMeta.Name)
- docFields = append(docFields, fieldMeta.DocName)
- }
- assert.ElementsMatch(t, fields, []string{"Type"})
- assert.ElementsMatch(t, docFields, []string{"Type"})
- }
- }
-}
-
-func TestStructCollector_DocFlatEmbed_ExcludeUnexportedFields4(t *testing.T) {
- file := pathutils.Abs("testdata/embed5.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := NewStructCollector(ExprString)
- ast.Walk(sc, root)
- structs := sc.DocFlatEmbed()
- for _, item := range structs {
- if item.Name == "TestEmbed5" {
- var fields []string
- var docFields []string
- for _, fieldMeta := range item.Fields {
- fields = append(fields, fieldMeta.Name)
- docFields = append(docFields, fieldMeta.DocName)
- }
- assert.ElementsMatch(t, fields, []string{"Fields", "testBase5"})
- assert.ElementsMatch(t, docFields, []string{"fields", "testBase"})
- }
- }
-}
-
-func TestStructCollector_Alias(t *testing.T) {
- file := pathutils.Abs("testdata/alias.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- //spew.Dump(root)
- sc := NewStructCollector(ExprString)
- ast.Walk(sc, root)
- structs := sc.DocFlatEmbed()
- for _, item := range structs {
- if item.Name == "TestAlias" {
- fmt.Println(item)
- }
- }
-}
-
-func TestStructCollector_Entity(t *testing.T) {
- file := pathutils.Abs("testdata/entity/purchase.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- //spew.Dump(root)
- sc := NewStructCollector(ExprString)
- ast.Walk(sc, root)
- fmt.Println(sc)
-}
diff --git a/toolkit/astutils/testdata/alias.go b/toolkit/astutils/testdata/alias.go
deleted file mode 100644
index 5d97af58..00000000
--- a/toolkit/astutils/testdata/alias.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package main
-
-import (
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/esutils"
-)
-
-// comment for alia age
-type age int
-
-type Event struct {
- Name string
- EventType int
-}
-
-type TestAlias struct {
- esutils.Base
- Age age
- School []struct {
- Name string
- Addr struct {
- Zip string
- Block string
- Full string
- }
- }
- EventChan chan Event
- SigChan chan int
- Callback func(string) bool
- CallbackN func(param string) bool
-}
-
-type ta TestAlias
-
-type TT time.Time
-
-type mm map[string]interface{}
-
-type MyInter interface {
- Speak() error
-}
-
-type starM *time.Time
diff --git a/toolkit/astutils/testdata/cat.go b/toolkit/astutils/testdata/cat.go
deleted file mode 100644
index 9e5bba03..00000000
--- a/toolkit/astutils/testdata/cat.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package main
-
-import (
- "context"
- "fmt"
-)
-
-type User struct {
- Name string `json:"name"`
-}
-
-type Cat struct {
- Hobbies map[string]interface{}
- Sleep func() bool
- Run chan string
-}
-
-// eat execute eat behavior for Cat
-func (c *Cat) eat(food string) (
- // not hungry
- full bool,
- // how feel
- mood string) {
- fmt.Println("eat " + food)
- return true, "happy"
-}
-
-func (c *Cat) PostSelectVersionPage(ctx context.Context, body PageDTO[VersionDTO, Cat, User]) (code int, message string, data PageDTO[VersionDTO, Cat, User], err error) {
- return 0, "", PageDTO[VersionDTO, Cat, User]{}, err
-}
-
-type VersionDTO struct {
- VersionName string `json:"versionName"`
- VersionId interface{} `json:"versionId"`
-}
-
-type PageDTO[T any, R any, K any] struct {
- TotalRow int `json:"totalRow"`
- PageNumber int `json:"pageNumber"`
- TotalPage int `json:"totalPage"`
- PageSize int `json:"pageSize"`
- ReturnMsg string `json:"return_msg"`
- List []T `json:"list"`
- Item []R `json:"item"`
- Many []K `json:"many"`
- ReturnCode string `json:"return_code"`
-}
diff --git a/toolkit/astutils/testdata/demo/config/config.go b/toolkit/astutils/testdata/demo/config/config.go
deleted file mode 100644
index d912156b..00000000
--- a/toolkit/astutils/testdata/demo/config/config.go
+++ /dev/null
@@ -1 +0,0 @@
-package config
diff --git a/toolkit/astutils/testdata/demo/go.mod b/toolkit/astutils/testdata/demo/go.mod
deleted file mode 100644
index 5f3ef328..00000000
--- a/toolkit/astutils/testdata/demo/go.mod
+++ /dev/null
@@ -1,3 +0,0 @@
-module testdata/demo
-
-go 1.20
diff --git a/toolkit/astutils/testdata/embed.go b/toolkit/astutils/testdata/embed.go
deleted file mode 100644
index 1f59449d..00000000
--- a/toolkit/astutils/testdata/embed.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package main
-
-//go:generate go-doudou name --file $GOFILE
-
-type TestBase struct {
- Index string `json:"index"`
- Type string `json:"type"`
-}
-
-type TestEmbed struct {
- TestBase
- Fields []Field `json:"fields"`
-}
diff --git a/toolkit/astutils/testdata/embed1.go b/toolkit/astutils/testdata/embed1.go
deleted file mode 100644
index 83408279..00000000
--- a/toolkit/astutils/testdata/embed1.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package main
-
-type TestBase1 struct {
- Index string
- Type string
-}
-
-type TestEmbed1 struct {
- TestBase1 `json:"test_base_1,omitempty"`
- Fields []Field `json:"fields"`
-}
diff --git a/toolkit/astutils/testdata/embed2.go b/toolkit/astutils/testdata/embed2.go
deleted file mode 100644
index f6d2fbcb..00000000
--- a/toolkit/astutils/testdata/embed2.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package main
-
-type testBase struct {
- Index string `json:"index"`
- Type string `json:"type"`
-}
-
-type TestEmbed2 struct {
- testBase
- Fields []Field `json:"fields"`
-}
diff --git a/toolkit/astutils/testdata/embed3.go b/toolkit/astutils/testdata/embed3.go
deleted file mode 100644
index 691cff4c..00000000
--- a/toolkit/astutils/testdata/embed3.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package main
-
-type TestBase3 struct {
- index string `json:"index"`
- Type string `json:"type"`
-}
-
-type TestEmbed3 struct {
- TestBase3
- Fields []Field `json:"fields"`
-}
diff --git a/toolkit/astutils/testdata/embed4.go b/toolkit/astutils/testdata/embed4.go
deleted file mode 100644
index 3fd0a3c8..00000000
--- a/toolkit/astutils/testdata/embed4.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package main
-
-type TestBase4 struct {
- index string
- Type string
-}
-
-type TestEmbed4 struct {
- TestBase1 `json:"test_base_1,omitempty"`
- Fields []Field `json:"fields"`
-}
diff --git a/toolkit/astutils/testdata/embed5.go b/toolkit/astutils/testdata/embed5.go
deleted file mode 100644
index 076dee92..00000000
--- a/toolkit/astutils/testdata/embed5.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package main
-
-type testBase5 struct {
- Index string `json:"index"`
- Type string `json:"type"`
-}
-
-type TestEmbed5 struct {
- testBase5 `json:"testBase"`
- Fields []Field `json:"fields"`
-}
diff --git a/toolkit/astutils/testdata/entity/base.go b/toolkit/astutils/testdata/entity/base.go
deleted file mode 100644
index 45d091ef..00000000
--- a/toolkit/astutils/testdata/entity/base.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package entity
-
-import "time"
-
-type Base struct {
- CreateAt *time.Time `dd:"default:CURRENT_TIMESTAMP"`
- UpdateAt *time.Time `dd:"default:CURRENT_TIMESTAMP;extra:ON UPDATE CURRENT_TIMESTAMP"`
- DeleteAt *time.Time
-}
diff --git a/toolkit/astutils/testdata/entity/material.go b/toolkit/astutils/testdata/entity/material.go
deleted file mode 100644
index 8159c31b..00000000
--- a/toolkit/astutils/testdata/entity/material.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package entity
-
-import "time"
-
-//dd:table
-type Material struct {
- Id int `dd:"pk;auto;type:int(11)"`
- Name string `dd:"type:varchar(45);extra:comment '原料名称'"`
- Amount int `dd:"type:int(11);extra:comment '原料单件克数'"`
- CreateAt *time.Time `dd:"type:datetime;default:CURRENT_TIMESTAMP"`
- UpdateAt *time.Time `dd:"type:datetime;default:CURRENT_TIMESTAMP;extra:on update CURRENT_TIMESTAMP"`
- DeleteAt *time.Time `dd:"type:datetime"`
- Price float32 `dd:"type:decimal(10,2);extra:comment '原料单件进价'"`
-}
diff --git a/toolkit/astutils/testdata/entity/purchase.go b/toolkit/astutils/testdata/entity/purchase.go
deleted file mode 100644
index 523f132b..00000000
--- a/toolkit/astutils/testdata/entity/purchase.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package entity
-
-import "time"
-
-//dd:table
-type Purchase struct {
- Id int `dd:"pk;auto;type:int(11)"`
- PurchaseAt *time.Time `dd:"type:datetime;extra:comment '采购时间'"`
- CreateAt *time.Time `dd:"type:datetime;default:CURRENT_TIMESTAMP"`
- UpdateAt *time.Time `dd:"type:datetime;default:CURRENT_TIMESTAMP;extra:on update CURRENT_TIMESTAMP"`
- DeleteAt *time.Time `dd:"type:datetime"`
- ArriveAt *time.Time `dd:"type:datetime;extra:comment '到货时间'"`
- Status int8 `dd:"type:tinyint(4);extra:comment '0: 进行中
-1: 完结
-2: 取消'"`
- Note string `dd:"type:text;extra:comment '备注'"`
-}
diff --git a/toolkit/astutils/testdata/entity/user.go b/toolkit/astutils/testdata/entity/user.go
deleted file mode 100644
index 2c4fbac1..00000000
--- a/toolkit/astutils/testdata/entity/user.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package entity
-
-//dd:table
-type User struct {
- ID int `dd:"pk;auto"`
- Name string `dd:"index:name_phone_idx,2;default:'jack'"`
- Phone string `dd:"index:name_phone_idx,1;default:'13552053960';extra:comment '手机号'"`
- Age int `dd:"index"`
- No int `dd:"unique"`
- School string `dd:"null;default:'harvard';extra:comment '学校'"`
- IsStudent bool
-
- Base
-}
diff --git a/toolkit/astutils/testdata/enum.go b/toolkit/astutils/testdata/enum.go
deleted file mode 100644
index c3d61206..00000000
--- a/toolkit/astutils/testdata/enum.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package main
-
-import "encoding/json"
-
-//go:generate go-doudou name --file $GOFILE -o
-
-type KeyboardLayout int
-
-const (
- UNKNOWN KeyboardLayout = iota
- QWERTZ = 300
-
- AZERTY int64 = 400
- QWERTY
- ddd = "abc"
-)
-
-func (k *KeyboardLayout) StringSetter(value string) {
- switch value {
- case "UNKNOWN":
- *k = UNKNOWN
- case "QWERTY":
- *k = QWERTY
- case "QWERTZ":
- *k = QWERTZ
- case "AZERTY":
- *k = AZERTY
- default:
- *k = UNKNOWN
- }
-}
-
-func (k *KeyboardLayout) StringGetter() string {
- switch *k {
- case UNKNOWN:
- return "UNKNOWN"
- case QWERTY:
- return "QWERTY"
- case QWERTZ:
- return "QWERTZ"
- case AZERTY:
- return "AZERTY"
- default:
- return "UNKNOWN"
- }
-}
-
-func (k *KeyboardLayout) UnmarshalJSON(bytes []byte) error {
- var _k string
- err := json.Unmarshal(bytes, &_k)
- if err != nil {
- return err
- }
- k.StringSetter(_k)
- return nil
-}
-
-func (k KeyboardLayout) MarshalJSON() ([]byte, error) {
- return json.Marshal(k.StringGetter())
-}
-
-type Keyboard struct {
- Layout *KeyboardLayout `json:"layout,omitempty"`
- Backlit bool `json:"backlit,omitempty"`
-}
diff --git a/toolkit/astutils/testdata/output.go b/toolkit/astutils/testdata/output.go
deleted file mode 100644
index a3ea3174..00000000
--- a/toolkit/astutils/testdata/output.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package main
-
-import (
- "encoding/json"
- "fmt"
-)
-
-type UserVo struct {
- Id int
- Name string
- Phone string
- Dept string
-}
-
-type Page struct {
- PageNo int
- Size int
- Items []UserVo
-}
-
-func main() {
- page := Page{
- PageNo: 10,
- Size: 30,
- }
- b, _ := json.Marshal(page)
- fmt.Println(string(b))
-}
diff --git a/toolkit/astutils/testdata/rewritejsontag.go b/toolkit/astutils/testdata/rewritejsontag.go
deleted file mode 100644
index 93d16dad..00000000
--- a/toolkit/astutils/testdata/rewritejsontag.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package main
-
-type base struct {
- Index string
- Type string
-}
-
-type struct1 struct {
- base
- Name string `json:"good"`
- StructType int `json:"struct_type" dd:"awesomtag"`
- Format string `dd:"anothertag"`
- Pos int
-}
diff --git a/toolkit/astutils/testdata/svc.go b/toolkit/astutils/testdata/svc.go
deleted file mode 100644
index 4a307f38..00000000
--- a/toolkit/astutils/testdata/svc.go
+++ /dev/null
@@ -1,45 +0,0 @@
-package main
-
-import (
- "context"
- "mime/multipart"
-)
-
-// 用户服务接口
-// v1版本
-type Usersvc interface {
- // You can define your service methods as your need. Below is an example.
- PageUsers(ctx context.Context, query PageQuery) (code int, data PageRet, msg error)
-
- // comment1
- // comment2
- GetUser(ctx context.Context,
- // 用户ID
- // @validate(gt=0,lte=10)
- userId,
- // 测试
- school,
- // 图片地址
- photo string,
- ) (
- // 业务状态码
- code int,
- // 结果
- data string,
- // 错误
- msg error)
-
- // comment3
- SignUp(ctx context.Context,
- // @validate(gt=0,lte=10)
- username string, password int, actived bool, score float64) (code int, data string, msg error)
-
- // comment4
- UploadAvatar(context.Context, []*multipart.FileHeader, string) (int, string, error)
-
- // comment5
- DownloadAvatar(ctx context.Context, userId string, userAttrs ...int) (a, b string)
-
- // @role(SUPER_ADMIN)
- BulkSaveOrUpdate(context.Context, int) error
-}
diff --git a/toolkit/astutils/testdata/vo.go b/toolkit/astutils/testdata/vo.go
deleted file mode 100644
index 0cf9068e..00000000
--- a/toolkit/astutils/testdata/vo.go
+++ /dev/null
@@ -1,145 +0,0 @@
-package main
-
-//go:generate go-doudou name --file $GOFILE -o
-
-// 筛选条件
-type PageFilter struct {
- // 真实姓名,前缀匹配
- Name string `json:"name,omitempty"`
- // 所属部门ID
- Dept int `json:"dept,omitempty"`
-}
-
-// 排序条件
-type Order struct {
- Col string `json:"col,omitempty"`
- Sort, Name, Banana string
-}
-
-// 分页筛选条件
-type PageQuery struct {
- Filter PageFilter `json:"filter,omitempty"`
- Page Page `json:"page,omitempty"`
-}
-
-type PageRet struct {
- Items interface{} `json:"items,omitempty"`
- PageNo int `json:"pageNo,omitempty"`
- PageSize int `json:"pageSize,omitempty"`
- Total int `json:"total,omitempty"`
- HasNext bool `json:"hasNext,omitempty"`
-}
-
-type queryType int
-type queryLogic int
-
-const (
- SHOULD queryLogic = iota + 1
- MUST
- MUSTNOT
-)
-
-const (
- TERMS queryType = iota + 1
- MATCHPHRASE
- RANGE
- PREFIX
- WILDCARD
- EXISTS
-)
-
-type esFieldType string
-
-const (
- TEXT esFieldType = "text"
- KEYWORD esFieldType = "keyword"
- DATE esFieldType = "date"
- LONG esFieldType = "long"
- INTEGER esFieldType = "integer"
- SHORT esFieldType = "short"
- DOUBLE esFieldType = "double"
- FLOAT esFieldType = "float"
- BOOL esFieldType = "boolean"
-)
-
-type Base struct {
- Index string `json:"index,omitempty"`
- Type string `json:"type,omitempty"`
-}
-
-type QueryCond struct {
- Pair map[string][]interface{} `json:"pair,omitempty"`
- QueryLogic queryLogic `json:"queryLogic,omitempty"`
- QueryType queryType `json:"queryType,omitempty"`
- Children []QueryCond `json:"children,omitempty"`
-}
-
-type Sort struct {
- Field string `json:"field,omitempty"`
- Ascending bool `json:"ascending,omitempty"`
-}
-
-type Paging struct {
- StartDate string `json:"startDate,omitempty"`
- EndDate string `json:"endDate,omitempty"`
- DateField string `json:"dateField,omitempty"`
- QueryConds []QueryCond `json:"queryConds,omitempty"`
- Skip int `json:"skip,omitempty"`
- Limit int `json:"limit,omitempty"`
- Sortby []Sort `json:"sortby,omitempty"`
-}
-
-type BulkSavePayload struct {
- Base
- Docs []map[string]interface{} `json:"docs,omitempty"`
-}
-
-type SavePayload struct {
- Base
- Doc map[string]interface{} `json:"doc,omitempty"`
-}
-
-type BulkDeletePayload struct {
- Base
- DocIds []string `json:"docIds,omitempty"`
-}
-
-type PagePayload struct {
- Base
- Paging
-}
-
-type PageResult struct {
- Page int `json:"page,omitempty"`
- PageSize int `json:"pageSize,omitempty"`
- Total int `json:"total,omitempty"`
- Docs []map[string]interface{} `json:"docs,omitempty"`
- HasNextPage bool `json:"hasNextPage,omitempty"`
-}
-
-type StatPayload struct {
- Base
- Paging
- Aggr interface{} `json:"aggr,omitempty"`
-}
-
-type RandomPayload struct {
- Base
- Paging
-}
-
-type CountPayload struct {
- Base
- Paging
-}
-
-type Field struct {
- Name string `json:"name,omitempty"`
- Type esFieldType `json:"type,omitempty"`
- Format string `json:"format,omitempty"`
-}
-
-type MappingPayload struct {
- Base
- Fields []Field `json:"fields,omitempty"`
-}
diff --git a/toolkit/astutils/testdata/vo/cat.go b/toolkit/astutils/testdata/vo/cat.go
deleted file mode 100644
index 0a5845c2..00000000
--- a/toolkit/astutils/testdata/vo/cat.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package vo
-
-import "fmt"
-
-type Cat struct {
- Hobbies map[string]interface{}
- Sleep func() bool
- Run chan string
-}
-
-func (c *Cat) eat(food string) bool {
- fmt.Println("eat " + food)
- return true
-}
diff --git a/toolkit/cache/.prettierrc.yml b/toolkit/cache/.prettierrc.yml
deleted file mode 100644
index 8b7f044a..00000000
--- a/toolkit/cache/.prettierrc.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-semi: false
-singleQuote: true
-proseWrap: always
-printWidth: 100
diff --git a/toolkit/cache/CHANGELOG.md b/toolkit/cache/CHANGELOG.md
deleted file mode 100644
index e9d6eece..00000000
--- a/toolkit/cache/CHANGELOG.md
+++ /dev/null
@@ -1,9 +0,0 @@
-# Changelog
-
-> :heart: [**Uptrace.dev** - distributed traces, logs, and errors in one place](https://uptrace.dev)
-
-## v8
-
-- Added s2 (snappy) compression. That means that v8 can't read the data set by v7.
-- Replaced LRU with TinyLFU for local cache.
-- Requires go-redis v8.
diff --git a/toolkit/cache/LICENSE b/toolkit/cache/LICENSE
deleted file mode 100644
index 9a21b9aa..00000000
--- a/toolkit/cache/LICENSE
+++ /dev/null
@@ -1,25 +0,0 @@
-Copyright (c) 2016 The github.com/go-redis/cache Contributors.
-All rights reserved.
-
-Redistribution and use in source and binary forms, with or without
-modification, are permitted provided that the following conditions are
-met:
-
- * Redistributions of source code must retain the above copyright
-notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above
-copyright notice, this list of conditions and the following disclaimer
-in the documentation and/or other materials provided with the
-distribution.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/toolkit/cache/Makefile b/toolkit/cache/Makefile
deleted file mode 100644
index 57914e33..00000000
--- a/toolkit/cache/Makefile
+++ /dev/null
@@ -1,6 +0,0 @@
-all:
- go test ./...
- go test ./... -short -race
- go test ./... -run=NONE -bench=. -benchmem
- env GOOS=linux GOARCH=386 go test ./...
- golangci-lint run
diff --git a/toolkit/cache/README.md b/toolkit/cache/README.md
deleted file mode 100644
index 5d31c87a..00000000
--- a/toolkit/cache/README.md
+++ /dev/null
@@ -1,122 +0,0 @@
-# Redis cache library for Golang
-
-[![Build Status](https://travis-ci.org/go-redis/cache.svg)](https://travis-ci.org/go-redis/cache)
-[![GoDoc](https://godoc.org/github.com/go-redis/cache?status.svg)](https://pkg.go.dev/github.com/go-redis/cache/v9?tab=doc)
-
-> go-redis/cache is brought to you by :star:
-> [**uptrace/uptrace**](https://github.com/uptrace/uptrace). Uptrace is an open source and blazingly
-> fast [distributed tracing tool](https://get.uptrace.dev/) powered by OpenTelemetry and ClickHouse.
-> Give it a star as well!
-
-go-redis/cache library implements a cache using Redis as a key/value storage. It uses
-[MessagePack](https://github.com/vmihailenco/msgpack) to marshal values.
-
-Optionally, you can use [TinyLFU](https://github.com/dgryski/go-tinylfu) or any other
-[cache algorithm](https://github.com/vmihailenco/go-cache-benchmark) as a local in-process cache.
-
-If you are interested in monitoring cache hit rate, see the guide for
-[Monitoring using OpenTelemetry Metrics](https://blog.uptrace.dev/posts/opentelemetry-metrics-cache-stats/).
-
-## Installation
-
-go-redis/cache supports 2 last Go versions and requires a Go version with
-[modules](https://github.com/golang/go/wiki/Modules) support. So make sure to initialize a Go
-module:
-
-```shell
-go mod init github.com/my/repo
-```
-
-And then install go-redis/cache/v9 (note _v9_ in the import; omitting it is a popular mistake):
-
-```shell
-go get github.com/go-redis/cache/v9
-```
-
-## Quickstart
-
-```go
-package cache_test
-
-import (
- "context"
- "fmt"
- "time"
-
- "github.com/redis/go-redis/v9"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cache"
-)
-
-type Object struct {
- Str string
- Num int
-}
-
-func Example_basicUsage() {
- ring := redis.NewRing(&redis.RingOptions{
- Addrs: map[string]string{
- "server1": ":6379",
- "server2": ":6380",
- },
- })
-
- mycache := cache.New(&cache.Options{
- Redis: ring,
- LocalCache: cache.NewTinyLFU(1000, time.Minute),
- })
-
- ctx := context.TODO()
- key := "mykey"
- obj := &Object{
- Str: "mystring",
- Num: 42,
- }
-
- if err := mycache.Set(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: obj,
- TTL: time.Hour,
- }); err != nil {
- panic(err)
- }
-
- var wanted Object
- if err := mycache.Get(ctx, key, &wanted); err == nil {
- fmt.Println(wanted)
- }
-
- // Output: {mystring 42}
-}
-
-func Example_advancedUsage() {
- ring := redis.NewRing(&redis.RingOptions{
- Addrs: map[string]string{
- "server1": ":6379",
- "server2": ":6380",
- },
- })
-
- mycache := cache.New(&cache.Options{
- Redis: ring,
- LocalCache: cache.NewTinyLFU(1000, time.Minute),
- })
-
- obj := new(Object)
- err := mycache.Once(&cache.Item{
- Key: "mykey",
- Value: obj, // destination
- Do: func(*cache.Item) (interface{}, error) {
- return &Object{
- Str: "mystring",
- Num: 42,
- }, nil
- },
- })
- if err != nil {
- panic(err)
- }
- fmt.Println(obj)
- // Output: &{mystring 42}
-}
-```
diff --git a/toolkit/cache/bench_test.go b/toolkit/cache/bench_test.go
deleted file mode 100644
index 75f2d681..00000000
--- a/toolkit/cache/bench_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package cache_test
-
-import (
- "strings"
- "testing"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cache"
-)
-
-func BenchmarkOnce(b *testing.B) {
- mycache := newCacheWithLocal(newRing())
- obj := &Object{
- Str: strings.Repeat("my very large string", 10),
- Num: 42,
- }
-
- b.ResetTimer()
-
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- var dst Object
- err := mycache.Once(&cache.Item{
- Key: "bench-once",
- Value: &dst,
- Do: func(*cache.Item) (interface{}, error) {
- return obj, nil
- },
- })
- if err != nil {
- b.Fatal(err)
- }
- if dst.Num != 42 {
- b.Fatalf("%d != 42", dst.Num)
- }
- }
- })
-}
-
-func BenchmarkSet(b *testing.B) {
- mycache := newCacheWithLocal(newRing())
- obj := &Object{
- Str: strings.Repeat("my very large string", 10),
- Num: 42,
- }
-
- b.ResetTimer()
-
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- if err := mycache.Set(&cache.Item{
- Key: "bench-set",
- Value: obj,
- }); err != nil {
- b.Fatal(err)
- }
- }
- })
-}
diff --git a/toolkit/cache/cache.go b/toolkit/cache/cache.go
deleted file mode 100644
index dde3d442..00000000
--- a/toolkit/cache/cache.go
+++ /dev/null
@@ -1,404 +0,0 @@
-package cache
-
-import (
- "context"
- "errors"
- "fmt"
- "sync/atomic"
- "time"
-
- "github.com/klauspost/compress/s2"
- "github.com/redis/go-redis/v9"
- "github.com/vmihailenco/msgpack/v5"
- "golang.org/x/sync/singleflight"
-)
-
-const (
- compressionThreshold = 64
- timeLen = 4
-)
-
-const (
- noCompression = 0x0
- s2Compression = 0x1
-)
-
-var (
- ErrCacheMiss = errors.New("cache: key is missing")
- errRedisLocalCacheNil = errors.New("cache: both Redis and LocalCache are nil")
-)
-
-type rediser interface {
- Set(ctx context.Context, key string, value interface{}, ttl time.Duration) *redis.StatusCmd
- SetXX(ctx context.Context, key string, value interface{}, ttl time.Duration) *redis.BoolCmd
- SetNX(ctx context.Context, key string, value interface{}, ttl time.Duration) *redis.BoolCmd
-
- Get(ctx context.Context, key string) *redis.StringCmd
- Del(ctx context.Context, keys ...string) *redis.IntCmd
-}
-
-type Item struct {
- Ctx context.Context
-
- Key string
- Value interface{}
-
- // TTL is the cache expiration time.
- // Default TTL is 1 hour.
- TTL time.Duration
-
- // Do returns value to be cached.
- Do func(*Item) (interface{}, error)
-
- // SetXX only sets the key if it already exists.
- SetXX bool
-
- // SetNX only sets the key if it does not already exist.
- SetNX bool
-
- // SkipLocalCache skips local cache as if it is not set.
- SkipLocalCache bool
-}
-
-func (item *Item) Context() context.Context {
- if item.Ctx == nil {
- return context.Background()
- }
- return item.Ctx
-}
-
-func (item *Item) value() (interface{}, error) {
- if item.Do != nil {
- return item.Do(item)
- }
- if item.Value != nil {
- return item.Value, nil
- }
- return nil, nil
-}
-
-func (item *Item) ttl() time.Duration {
- return item.TTL
-}
-
-// ------------------------------------------------------------------------------
-type (
- MarshalFunc func(interface{}) ([]byte, error)
- UnmarshalFunc func([]byte, interface{}) error
-)
-
-type Options struct {
- Redis rediser
- LocalCache LocalCache
- StatsEnabled bool
- Marshal MarshalFunc
- Unmarshal UnmarshalFunc
-}
-
-type Cache struct {
- opt *Options
-
- group singleflight.Group
-
- marshal MarshalFunc
- unmarshal UnmarshalFunc
-
- hits uint64
- misses uint64
-}
-
-func New(opt *Options) *Cache {
- cacher := &Cache{
- opt: opt,
- }
-
- if opt.Marshal == nil {
- cacher.marshal = cacher._marshal
- } else {
- cacher.marshal = opt.Marshal
- }
-
- if opt.Unmarshal == nil {
- cacher.unmarshal = cacher._unmarshal
- } else {
- cacher.unmarshal = opt.Unmarshal
- }
- return cacher
-}
-
-// Set caches the item.
-func (cd *Cache) Set(item *Item) error {
- _, _, err := cd.set(item)
- return err
-}
-
-func (cd *Cache) set(item *Item) ([]byte, bool, error) {
- value, err := item.value()
- if err != nil {
- return nil, false, err
- }
-
- b, err := cd.Marshal(value)
- if err != nil {
- return nil, false, err
- }
-
- if cd.opt.LocalCache != nil && !item.SkipLocalCache {
- cd.opt.LocalCache.Set(item.Key, b)
- }
-
- if cd.opt.Redis == nil {
- if cd.opt.LocalCache == nil {
- return b, true, errRedisLocalCacheNil
- }
- return b, true, nil
- }
-
- ttl := item.ttl()
-
- if item.SetXX {
- return b, true, cd.opt.Redis.SetXX(item.Context(), item.Key, b, ttl).Err()
- }
- if item.SetNX {
- return b, true, cd.opt.Redis.SetNX(item.Context(), item.Key, b, ttl).Err()
- }
- return b, true, cd.opt.Redis.Set(item.Context(), item.Key, b, ttl).Err()
-}
-
-// Exists reports whether value for the given key exists.
-func (cd *Cache) Exists(ctx context.Context, key string) bool {
- _, err := cd.getBytes(ctx, key, false)
- return err == nil
-}
-
-// Get gets the value for the given key.
-func (cd *Cache) Get(ctx context.Context, key string, value interface{}) error {
- return cd.get(ctx, key, value, false)
-}
-
-// Get gets the value for the given key skipping local cache.
-func (cd *Cache) GetSkippingLocalCache(
- ctx context.Context, key string, value interface{},
-) error {
- return cd.get(ctx, key, value, true)
-}
-
-func (cd *Cache) get(
- ctx context.Context,
- key string,
- value interface{},
- skipLocalCache bool,
-) error {
- b, err := cd.getBytes(ctx, key, skipLocalCache)
- if err != nil {
- return err
- }
- return cd.unmarshal(b, value)
-}
-
-func (cd *Cache) getBytes(ctx context.Context, key string, skipLocalCache bool) ([]byte, error) {
- if !skipLocalCache && cd.opt.LocalCache != nil {
- b, ok := cd.opt.LocalCache.Get(key)
- if ok {
- return b, nil
- }
- }
-
- if cd.opt.Redis == nil {
- if cd.opt.LocalCache == nil {
- return nil, errRedisLocalCacheNil
- }
- return nil, ErrCacheMiss
- }
-
- b, err := cd.opt.Redis.Get(ctx, key).Bytes()
- if err != nil {
- if cd.opt.StatsEnabled {
- atomic.AddUint64(&cd.misses, 1)
- }
- if err == redis.Nil {
- return nil, ErrCacheMiss
- }
- return nil, err
- }
-
- if cd.opt.StatsEnabled {
- atomic.AddUint64(&cd.hits, 1)
- }
-
- if !skipLocalCache && cd.opt.LocalCache != nil {
- cd.opt.LocalCache.Set(key, b)
- }
- return b, nil
-}
-
-// Once gets the item.Value for the given item.Key from the cache or
-// executes, caches, and returns the results of the given item.Func,
-// making sure that only one execution is in-flight for a given item.Key
-// at a time. If a duplicate comes in, the duplicate caller waits for the
-// original to complete and receives the same results.
-func (cd *Cache) Once(item *Item) error {
- b, cached, err := cd.getSetItemBytesOnce(item)
- if err != nil {
- return err
- }
-
- if item.Value == nil || len(b) == 0 {
- return nil
- }
-
- if err := cd.unmarshal(b, item.Value); err != nil {
- if cached {
- _ = cd.Delete(item.Context(), item.Key)
- return cd.Once(item)
- }
- return err
- }
-
- return nil
-}
-
-func (cd *Cache) getSetItemBytesOnce(item *Item) (b []byte, cached bool, err error) {
- if cd.opt.LocalCache != nil {
- b, ok := cd.opt.LocalCache.Get(item.Key)
- if ok {
- return b, true, nil
- }
- }
-
- v, err, _ := cd.group.Do(item.Key, func() (interface{}, error) {
- b, err := cd.getBytes(item.Context(), item.Key, item.SkipLocalCache)
- if err == nil {
- cached = true
- return b, nil
- }
-
- b, ok, err := cd.set(item)
- if ok {
- return b, nil
- }
- return nil, err
- })
- if err != nil {
- return nil, false, err
- }
- return v.([]byte), cached, nil
-}
-
-func (cd *Cache) Delete(ctx context.Context, key string) error {
- if cd.opt.LocalCache != nil {
- cd.opt.LocalCache.Del(key)
- }
-
- if cd.opt.Redis == nil {
- if cd.opt.LocalCache == nil {
- return errRedisLocalCacheNil
- }
- return nil
- }
-
- _, err := cd.opt.Redis.Del(ctx, key).Result()
- return err
-}
-
-func (cd *Cache) DeleteFromLocalCache(key string) {
- if cd.opt.LocalCache != nil {
- cd.opt.LocalCache.Del(key)
- }
-}
-
-func (cd *Cache) Marshal(value interface{}) ([]byte, error) {
- return cd.marshal(value)
-}
-
-func (cd *Cache) _marshal(value interface{}) ([]byte, error) {
- switch value := value.(type) {
- case nil:
- return nil, nil
- case []byte:
- return value, nil
- case string:
- return []byte(value), nil
- }
-
- b, err := msgpack.Marshal(value)
- if err != nil {
- return nil, err
- }
-
- return compress(b), nil
-}
-
-func compress(data []byte) []byte {
- if len(data) < compressionThreshold {
- n := len(data) + 1
- b := make([]byte, n, n+timeLen)
- copy(b, data)
- b[len(b)-1] = noCompression
- return b
- }
-
- n := s2.MaxEncodedLen(len(data)) + 1
- b := make([]byte, n, n+timeLen)
- b = s2.Encode(b, data)
- b = append(b, s2Compression)
- return b
-}
-
-func (cd *Cache) Unmarshal(b []byte, value interface{}) error {
- return cd.unmarshal(b, value)
-}
-
-func (cd *Cache) _unmarshal(b []byte, value interface{}) error {
- if len(b) == 0 {
- return nil
- }
-
- switch value := value.(type) {
- case nil:
- return nil
- case *[]byte:
- clone := make([]byte, len(b))
- copy(clone, b)
- *value = clone
- return nil
- case *string:
- *value = string(b)
- return nil
- }
-
- switch c := b[len(b)-1]; c {
- case noCompression:
- b = b[:len(b)-1]
- case s2Compression:
- b = b[:len(b)-1]
-
- var err error
- b, err = s2.Decode(nil, b)
- if err != nil {
- return err
- }
- default:
- return fmt.Errorf("unknown compression method: %x", c)
- }
-
- return msgpack.Unmarshal(b, value)
-}
-
-//------------------------------------------------------------------------------
-
-type Stats struct {
- Hits uint64
- Misses uint64
-}
-
-// Stats returns cache statistics.
-func (cd *Cache) Stats() *Stats {
- if !cd.opt.StatsEnabled {
- return nil
- }
- return &Stats{
- Hits: atomic.LoadUint64(&cd.hits),
- Misses: atomic.LoadUint64(&cd.misses),
- }
-}
diff --git a/toolkit/cache/cache_test.go b/toolkit/cache/cache_test.go
deleted file mode 100644
index a7a2ab1e..00000000
--- a/toolkit/cache/cache_test.go
+++ /dev/null
@@ -1,426 +0,0 @@
-package cache_test
-
-import (
- "context"
- "errors"
- "io"
- "sync"
- "sync/atomic"
- "testing"
- "time"
-
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
- "github.com/redis/go-redis/v9"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cache"
-)
-
-func TestGinkgo(t *testing.T) {
- RegisterFailHandler(Fail)
- RunSpecs(t, "cache")
-}
-
-func perform(n int, cbs ...func(int)) {
- var wg sync.WaitGroup
- for _, cb := range cbs {
- for i := 0; i < n; i++ {
- wg.Add(1)
- go func(cb func(int), i int) {
- defer wg.Done()
- defer GinkgoRecover()
-
- cb(i)
- }(cb, i)
- }
- }
- wg.Wait()
-}
-
-var _ = Describe("Cache", func() {
- ctx := context.TODO()
-
- const key = "mykey"
- var obj *Object
-
- var rdb *redis.Ring
- var mycache *cache.Cache
-
- testCache := func() {
- It("Gets and Sets nil", func() {
- err := mycache.Set(&cache.Item{
- Key: key,
- TTL: time.Hour,
- })
- Expect(err).NotTo(HaveOccurred())
-
- err = mycache.Get(ctx, key, nil)
- Expect(err).NotTo(HaveOccurred())
-
- Expect(mycache.Exists(ctx, key)).To(BeTrue())
- })
-
- It("Deletes key", func() {
- err := mycache.Set(&cache.Item{
- Ctx: ctx,
- Key: key,
- TTL: time.Hour,
- })
- Expect(err).NotTo(HaveOccurred())
-
- Expect(mycache.Exists(ctx, key)).To(BeTrue())
-
- err = mycache.Delete(ctx, key)
- Expect(err).NotTo(HaveOccurred())
-
- err = mycache.Get(ctx, key, nil)
- Expect(err).To(Equal(cache.ErrCacheMiss))
-
- Expect(mycache.Exists(ctx, key)).To(BeFalse())
- })
-
- It("Gets and Sets data", func() {
- err := mycache.Set(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: obj,
- TTL: time.Hour,
- })
- Expect(err).NotTo(HaveOccurred())
-
- wanted := new(Object)
- err = mycache.Get(ctx, key, wanted)
- Expect(err).NotTo(HaveOccurred())
- Expect(wanted).To(Equal(obj))
-
- Expect(mycache.Exists(ctx, key)).To(BeTrue())
- })
-
- It("Sets string as is", func() {
- value := "str_value"
-
- err := mycache.Set(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: value,
- })
- Expect(err).NotTo(HaveOccurred())
-
- var dst string
- err = mycache.Get(ctx, key, &dst)
- Expect(err).NotTo(HaveOccurred())
- Expect(dst).To(Equal(value))
- })
-
- It("Sets bytes as is", func() {
- value := []byte("str_value")
-
- err := mycache.Set(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: value,
- })
- Expect(err).NotTo(HaveOccurred())
-
- var dst []byte
- err = mycache.Get(ctx, key, &dst)
- Expect(err).NotTo(HaveOccurred())
- Expect(dst).To(Equal(value))
- })
-
- It("can be used with Incr", func() {
- if rdb == nil {
- return
- }
-
- value := "123"
-
- err := mycache.Set(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: value,
- })
- Expect(err).NotTo(HaveOccurred())
-
- n, err := rdb.Incr(ctx, key).Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(n).To(Equal(int64(124)))
- })
-
- Describe("Once func", func() {
- It("calls Func when cache fails", func() {
- err := mycache.Set(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: int64(0),
- })
- Expect(err).NotTo(HaveOccurred())
-
- var got bool
- err = mycache.Get(ctx, key, &got)
- Expect(err).To(MatchError("msgpack: invalid code=d3 decoding bool"))
-
- err = mycache.Once(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: &got,
- Do: func(*cache.Item) (interface{}, error) {
- return true, nil
- },
- })
- Expect(err).NotTo(HaveOccurred())
- Expect(got).To(BeTrue())
-
- got = false
- err = mycache.Get(ctx, key, &got)
- Expect(err).NotTo(HaveOccurred())
- Expect(got).To(BeTrue())
- })
-
- It("does not cache when Func fails", func() {
- perform(100, func(int) {
- var got bool
- err := mycache.Once(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: &got,
- Do: func(*cache.Item) (interface{}, error) {
- return nil, io.EOF
- },
- })
- Expect(err).To(Equal(io.EOF))
- Expect(got).To(BeFalse())
- })
-
- var got bool
- err := mycache.Get(ctx, key, &got)
- Expect(err).To(Equal(cache.ErrCacheMiss))
-
- err = mycache.Once(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: &got,
- Do: func(*cache.Item) (interface{}, error) {
- return true, nil
- },
- })
- Expect(err).NotTo(HaveOccurred())
- Expect(got).To(BeTrue())
- })
-
- It("works with Value", func() {
- var callCount int64
- perform(100, func(int) {
- got := new(Object)
- err := mycache.Once(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: got,
- Do: func(*cache.Item) (interface{}, error) {
- atomic.AddInt64(&callCount, 1)
- return obj, nil
- },
- })
- Expect(err).NotTo(HaveOccurred())
- Expect(got).To(Equal(obj))
- })
- Expect(callCount).To(Equal(int64(1)))
- })
-
- It("works with ptr and non-ptr", func() {
- var callCount int64
- perform(100, func(int) {
- got := new(Object)
- err := mycache.Once(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: got,
- Do: func(*cache.Item) (interface{}, error) {
- atomic.AddInt64(&callCount, 1)
- return *obj, nil
- },
- })
- Expect(err).NotTo(HaveOccurred())
- Expect(got).To(Equal(obj))
- })
- Expect(callCount).To(Equal(int64(1)))
- })
-
- It("works with bool", func() {
- var callCount int64
- perform(100, func(int) {
- var got bool
- err := mycache.Once(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: &got,
- Do: func(*cache.Item) (interface{}, error) {
- atomic.AddInt64(&callCount, 1)
- return true, nil
- },
- })
- Expect(err).NotTo(HaveOccurred())
- Expect(got).To(BeTrue())
- })
- Expect(callCount).To(Equal(int64(1)))
- })
-
- It("works without Value and nil result", func() {
- var callCount int64
- perform(100, func(int) {
- err := mycache.Once(&cache.Item{
- Ctx: ctx,
- Key: key,
- Do: func(*cache.Item) (interface{}, error) {
- atomic.AddInt64(&callCount, 1)
- return nil, nil
- },
- })
- Expect(err).NotTo(HaveOccurred())
- })
- Expect(callCount).To(Equal(int64(1)))
- })
-
- It("works without Value and error result", func() {
- var callCount int64
- perform(100, func(int) {
- err := mycache.Once(&cache.Item{
- Ctx: ctx,
- Key: key,
- Do: func(*cache.Item) (interface{}, error) {
- time.Sleep(100 * time.Millisecond)
- atomic.AddInt64(&callCount, 1)
- return nil, errors.New("error stub")
- },
- })
- Expect(err).To(MatchError("error stub"))
- })
- Expect(callCount).To(Equal(int64(1)))
- })
-
- It("does not cache error result", func() {
- var callCount int64
- do := func(sleep time.Duration) (int, error) {
- var n int
- err := mycache.Once(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: &n,
- Do: func(*cache.Item) (interface{}, error) {
- time.Sleep(sleep)
-
- n := atomic.AddInt64(&callCount, 1)
- if n == 1 {
- return nil, errors.New("error stub")
- }
- return 42, nil
- },
- })
- if err != nil {
- return 0, err
- }
- return n, nil
- }
-
- perform(100, func(int) {
- n, err := do(100 * time.Millisecond)
- Expect(err).To(MatchError("error stub"))
- Expect(n).To(Equal(0))
- })
-
- perform(100, func(int) {
- n, err := do(0)
- Expect(err).NotTo(HaveOccurred())
- Expect(n).To(Equal(42))
- })
-
- Expect(callCount).To(Equal(int64(2)))
- })
-
- It("skips Set when TTL = -1", func() {
- key := "skip-set"
-
- var value string
- err := mycache.Once(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: &value,
- Do: func(item *cache.Item) (interface{}, error) {
- item.TTL = -1
- return "hello", nil
- },
- })
- Expect(err).NotTo(HaveOccurred())
- Expect(value).To(Equal("hello"))
-
- if rdb != nil {
- exists, err := rdb.Exists(ctx, key).Result()
- Expect(err).NotTo(HaveOccurred())
- Expect(exists).To(Equal(int64(0)))
- }
- })
- })
- }
-
- BeforeEach(func() {
- obj = &Object{
- Str: "mystring",
- Num: 42,
- }
- })
-
- Context("without LocalCache", func() {
- BeforeEach(func() {
- rdb = newRing()
- mycache = newCache(rdb)
- })
-
- testCache()
- })
-
- Context("with LocalCache", func() {
- BeforeEach(func() {
- rdb = newRing()
- mycache = newCacheWithLocal(rdb)
- })
-
- testCache()
- })
-
- Context("with LocalCache and without Redis", func() {
- BeforeEach(func() {
- rdb = nil
- mycache = cache.New(&cache.Options{
- LocalCache: cache.NewTinyLFU(1000, time.Minute),
- })
- })
-
- testCache()
- })
-})
-
-func newRing() *redis.Ring {
- ctx := context.TODO()
- ring := redis.NewRing(&redis.RingOptions{
- Addrs: map[string]string{
- "server1": ":6379",
- },
- })
- _ = ring.ForEachShard(ctx, func(ctx context.Context, client *redis.Client) error {
- return client.FlushDB(ctx).Err()
- })
- return ring
-}
-
-func newCache(rdb *redis.Ring) *cache.Cache {
- return cache.New(&cache.Options{
- Redis: rdb,
- })
-}
-
-func newCacheWithLocal(rdb *redis.Ring) *cache.Cache {
- return cache.New(&cache.Options{
- Redis: rdb,
- LocalCache: cache.NewTinyLFU(1000, time.Minute),
- })
-}
diff --git a/toolkit/cache/example_cache_test.go b/toolkit/cache/example_cache_test.go
deleted file mode 100644
index b42abc0e..00000000
--- a/toolkit/cache/example_cache_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package cache_test
-
-import (
- "context"
- "fmt"
- "time"
-
- "github.com/redis/go-redis/v9"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cache"
-)
-
-type Object struct {
- Str string
- Num int
-}
-
-func Example_basicUsage() {
- ring := redis.NewRing(&redis.RingOptions{
- Addrs: map[string]string{
- "server1": ":6379",
- "server2": ":6380",
- },
- })
-
- mycache := cache.New(&cache.Options{
- Redis: ring,
- LocalCache: cache.NewTinyLFU(1000, time.Minute),
- })
-
- ctx := context.TODO()
- key := "mykey"
- obj := &Object{
- Str: "mystring",
- Num: 42,
- }
-
- if err := mycache.Set(&cache.Item{
- Ctx: ctx,
- Key: key,
- Value: obj,
- TTL: time.Hour,
- }); err != nil {
- panic(err)
- }
-
- var wanted Object
- if err := mycache.Get(ctx, key, &wanted); err == nil {
- fmt.Println(wanted)
- }
-
- // Output: {mystring 42}
-}
-
-func Example_advancedUsage() {
- ring := redis.NewRing(&redis.RingOptions{
- Addrs: map[string]string{
- "server1": ":6379",
- "server2": ":6380",
- },
- })
-
- mycache := cache.New(&cache.Options{
- Redis: ring,
- LocalCache: cache.NewTinyLFU(1000, time.Minute),
- })
-
- obj := new(Object)
- err := mycache.Once(&cache.Item{
- Key: "mykey",
- Value: obj, // destination
- Do: func(*cache.Item) (interface{}, error) {
- return &Object{
- Str: "mystring",
- Num: 42,
- }, nil
- },
- })
- if err != nil {
- panic(err)
- }
- fmt.Println(obj)
- // Output: &{mystring 42}
-}
diff --git a/toolkit/cache/local.go b/toolkit/cache/local.go
deleted file mode 100644
index 81f50978..00000000
--- a/toolkit/cache/local.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package cache
-
-import (
- "math/rand"
- "sync"
- "time"
-
- "github.com/vmihailenco/go-tinylfu"
-)
-
-type LocalCache interface {
- Set(key string, data []byte)
- Get(key string) ([]byte, bool)
- Del(key string)
-}
-
-type TinyLFU struct {
- mu sync.Mutex
- rand *rand.Rand
- lfu *tinylfu.T
- ttl time.Duration
- offset time.Duration
-}
-
-var _ LocalCache = (*TinyLFU)(nil)
-
-func NewTinyLFU(size int, ttl time.Duration) *TinyLFU {
- const maxOffset = 10 * time.Second
-
- offset := ttl / 10
- if offset > maxOffset {
- offset = maxOffset
- }
-
- return &TinyLFU{
- rand: rand.New(rand.NewSource(time.Now().UnixNano())),
- lfu: tinylfu.New(size, 100000),
- ttl: ttl,
- offset: offset,
- }
-}
-
-func (c *TinyLFU) UseRandomizedTTL(offset time.Duration) {
- c.offset = offset
-}
-
-func (c *TinyLFU) Set(key string, b []byte) {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- ttl := c.ttl
- if c.offset > 0 {
- ttl += time.Duration(c.rand.Int63n(int64(c.offset)))
- }
-
- c.lfu.Set(&tinylfu.Item{
- Key: key,
- Value: b,
- ExpireAt: time.Now().Add(ttl),
- })
-}
-
-func (c *TinyLFU) Get(key string) ([]byte, bool) {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- val, ok := c.lfu.Get(key)
- if !ok {
- return nil, false
- }
-
- b := val.([]byte)
- return b, true
-}
-
-func (c *TinyLFU) Del(key string) {
- c.mu.Lock()
- defer c.mu.Unlock()
-
- c.lfu.Del(key)
-}
diff --git a/toolkit/cache/local_test.go b/toolkit/cache/local_test.go
deleted file mode 100644
index f8dad079..00000000
--- a/toolkit/cache/local_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package cache_test
-
-import (
- "context"
- "fmt"
- "math/rand"
- "testing"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cache"
-)
-
-func TestTinyLFU_Get_CorruptionOnExpiry(t *testing.T) {
- strFor := func(i int) string {
- return fmt.Sprintf("a string %d", i)
- }
- keyName := func(i int) string {
- return fmt.Sprintf("key-%00000d", i)
- }
-
- mycache := cache.NewTinyLFU(1000, 1*time.Second)
- size := 50000
- // Put a bunch of stuff in the cache with a TTL of 1 second
- for i := 0; i < size; i++ {
- key := keyName(i)
- mycache.Set(key, []byte(strFor(i)))
- }
-
- // Read stuff for a bit longer than the TTL - that's when the corruption occurs
- ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
- defer cancel()
-
- done := ctx.Done()
-loop:
- for {
- select {
- case <-done:
- // this is expected
- break loop
- default:
- i := rand.Intn(size)
- key := keyName(i)
-
- b, ok := mycache.Get(key)
- if !ok {
- continue loop
- }
-
- got := string(b)
- expected := strFor(i)
- if got != expected {
- t.Fatalf("expected=%q got=%q key=%q", expected, got, key)
- }
- }
- }
-}
diff --git a/toolkit/caches/LICENSE b/toolkit/caches/LICENSE
deleted file mode 100644
index ed43104c..00000000
--- a/toolkit/caches/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2023-NOW Kristian Tsivkov
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/toolkit/caches/README.md b/toolkit/caches/README.md
deleted file mode 100644
index be0639a7..00000000
--- a/toolkit/caches/README.md
+++ /dev/null
@@ -1,235 +0,0 @@
-# Gorm Caches
-
-Gorm Caches plugin using database request reductions (easer), and response caching mechanism provide you an easy way to optimize database performance.
-
-## Features
-
-- Database request reduction. If three identical requests are running at the same time, only the first one is going to be executed, and its response will be returned for all.
-- Database response caching. By implementing the Cacher interface, you can easily setup a caching mechanism for your database queries.
-- Supports all databases that are supported by gorm itself.
-
-## Install
-
-```bash
-go get -u github.com/go-gorm/caches/v2
-```
-
-## Usage
-
-Configure the `easer`, and the `cacher`, and then load the plugin to gorm.
-
-```go
-package main
-
-import (
- "fmt"
- "sync"
-
- "github.com/go-gorm/caches"
- "github.com/wubin1989/mysql"
- "github.com/wubin1989/gorm"
-)
-
-func main() {
- db, _ := gorm.Open(
- mysql.Open("DATABASE_DSN"),
- &gorm.Config{},
- )
- cachesPlugin := &caches.Caches{Conf: &caches.Config{
- Easer: true,
- Cacher: &yourCacherImplementation{},
- }}
- _ = db.Use(cachesPlugin)
-}
-```
-
-## Easer Example
-
-```go
-package main
-
-import (
- "fmt"
- "sync"
- "time"
-
- "github.com/go-gorm/caches"
- "github.com/wubin1989/mysql"
- "github.com/wubin1989/gorm"
-)
-
-type UserRoleModel struct {
- gorm.Model
- Name string `gorm:"unique"`
-}
-
-type UserModel struct {
- gorm.Model
- Name string
- RoleId uint
- Role *UserRoleModel `gorm:"foreignKey:role_id;references:id"`
-}
-
-func main() {
- db, _ := gorm.Open(
- mysql.Open("DATABASE_DSN"),
- &gorm.Config{},
- )
-
- cachesPlugin := &caches.Caches{Conf: &caches.Config{
- Easer: true,
- }}
-
- _ = db.Use(cachesPlugin)
-
- _ = db.AutoMigrate(&UserRoleModel{})
-
- _ = db.AutoMigrate(&UserModel{})
-
- adminRole := &UserRoleModel{
- Name: "Admin",
- }
- db.FirstOrCreate(adminRole, "Name = ?", "Admin")
-
- guestRole := &UserRoleModel{
- Name: "Guest",
- }
- db.FirstOrCreate(guestRole, "Name = ?", "Guest")
-
- db.Save(&UserModel{
- Name: "ktsivkov",
- Role: adminRole,
- })
- db.Save(&UserModel{
- Name: "anonymous",
- Role: guestRole,
- })
-
- var (
- q1Users []UserModel
- q2Users []UserModel
- )
- wg := &sync.WaitGroup{}
- wg.Add(2)
- go func() {
- db.Model(&UserModel{}).Joins("Role").Find(&q1Users, "Role.Name = ? AND Sleep(1) = false", "Admin")
- wg.Done()
- }()
- go func() {
- time.Sleep(500 * time.Millisecond)
- db.Model(&UserModel{}).Joins("Role").Find(&q2Users, "Role.Name = ? AND Sleep(1) = false", "Admin")
- wg.Done()
- }()
- wg.Wait()
-
- fmt.Println(fmt.Sprintf("%+v", q1Users))
- fmt.Println(fmt.Sprintf("%+v", q2Users))
-}
-```
-
-## Cacher Example
-
-```go
-package main
-
-import (
- "fmt"
- "sync"
-
- "github.com/go-gorm/caches"
- "github.com/wubin1989/mysql"
- "github.com/wubin1989/gorm"
-)
-
-type UserRoleModel struct {
- gorm.Model
- Name string `gorm:"unique"`
-}
-
-type UserModel struct {
- gorm.Model
- Name string
- RoleId uint
- Role *UserRoleModel `gorm:"foreignKey:role_id;references:id"`
-}
-
-type dummyCacher struct {
- store *sync.Map
-}
-
-func (c *dummyCacher) init() {
- if c.store == nil {
- c.store = &sync.Map{}
- }
-}
-
-func (c *dummyCacher) Get(key string) *caches.Query {
- c.init()
- val, ok := c.store.Load(key)
- if !ok {
- return nil
- }
-
- return val.(*caches.Query)
-}
-
-func (c *dummyCacher) Store(key string, val *caches.Query) error {
- c.init()
- c.store.Store(key, val)
- return nil
-}
-
-func main() {
- db, _ := gorm.Open(
- mysql.Open("DATABASE_DSN"),
- &gorm.Config{},
- )
-
- cachesPlugin := &caches.Caches{Conf: &caches.Config{
- Cacher: &dummyCacher{},
- }}
-
- _ = db.Use(cachesPlugin)
-
- _ = db.AutoMigrate(&UserRoleModel{})
-
- _ = db.AutoMigrate(&UserModel{})
-
- adminRole := &UserRoleModel{
- Name: "Admin",
- }
- db.FirstOrCreate(adminRole, "Name = ?", "Admin")
-
- guestRole := &UserRoleModel{
- Name: "Guest",
- }
- db.FirstOrCreate(guestRole, "Name = ?", "Guest")
-
- db.Save(&UserModel{
- Name: "ktsivkov",
- Role: adminRole,
- })
- db.Save(&UserModel{
- Name: "anonymous",
- Role: guestRole,
- })
-
- var (
- q1Users []UserModel
- q2Users []UserModel
- )
-
- db.Model(&UserModel{}).Joins("Role").Find(&q1Users, "Role.Name = ? AND Sleep(1) = false", "Admin")
- fmt.Println(fmt.Sprintf("%+v", q1Users))
-
- db.Model(&UserModel{}).Joins("Role").Find(&q2Users, "Role.Name = ? AND Sleep(1) = false", "Admin")
- fmt.Println(fmt.Sprintf("%+v", q2Users))
-}
-```
-
-## License
-
-MIT license.
-
-## Easer
-The easer is an adjusted version of the [ServantGo](https://github.com/ktsivkov/servantgo) library to fit the needs of this plugin.
diff --git a/toolkit/caches/cacher.go b/toolkit/caches/cacher.go
deleted file mode 100644
index 44536aba..00000000
--- a/toolkit/caches/cacher.go
+++ /dev/null
@@ -1,7 +0,0 @@
-package caches
-
-type Cacher interface {
- Get(key string) *Query
- Store(key string, val *Query) error
- Delete(tag string, tags ...string) error
-}
diff --git a/toolkit/caches/cacher_test.go b/toolkit/caches/cacher_test.go
deleted file mode 100644
index b0d836d0..00000000
--- a/toolkit/caches/cacher_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package caches
-
-import (
- "errors"
- "sync"
-)
-
-type cacherMock struct {
- store *sync.Map
-}
-
-func (c *cacherMock) Delete(tag string, tags ...string) error {
- //TODO implement me
- panic("implement me")
-}
-
-func (c *cacherMock) init() {
- if c.store == nil {
- c.store = &sync.Map{}
- }
-}
-
-func (c *cacherMock) Get(key string) *Query {
- c.init()
- val, ok := c.store.Load(key)
- if !ok {
- return nil
- }
-
- return val.(*Query)
-}
-
-func (c *cacherMock) Store(key string, val *Query) error {
- c.init()
- c.store.Store(key, val)
- return nil
-}
-
-type cacherStoreErrorMock struct {
- store *sync.Map
-}
-
-func (c *cacherStoreErrorMock) Delete(tag string, tags ...string) error {
- //TODO implement me
- panic("implement me")
-}
-
-func (c *cacherStoreErrorMock) init() {
- if c.store == nil {
- c.store = &sync.Map{}
- }
-}
-
-func (c *cacherStoreErrorMock) Get(key string) *Query {
- c.init()
- val, ok := c.store.Load(key)
- if !ok {
- return nil
- }
-
- return val.(*Query)
-}
-
-func (c *cacherStoreErrorMock) Store(string, *Query) error {
- return errors.New("store-error")
-}
diff --git a/toolkit/caches/caches.go b/toolkit/caches/caches.go
deleted file mode 100644
index 0ca787f5..00000000
--- a/toolkit/caches/caches.go
+++ /dev/null
@@ -1,365 +0,0 @@
-package caches
-
-import (
- "context"
- "fmt"
- "strings"
- "sync"
-
- "github.com/auxten/postgresql-parser/pkg/sql/parser"
- "github.com/auxten/postgresql-parser/pkg/sql/sem/tree"
- "github.com/auxten/postgresql-parser/pkg/walk"
- mapset "github.com/deckarep/golang-set/v2"
- "github.com/samber/lo"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/mysql"
- "github.com/wubin1989/postgres"
- "github.com/xwb1989/sqlparser"
-)
-
-type ctxKey int
-
-const tablesKey = ctxKey(0)
-
-func NewTablesContext(ctx context.Context, tables mapset.Set[string]) context.Context {
- return context.WithValue(ctx, tablesKey, tables)
-}
-
-func TablesFromContext(ctx context.Context) (mapset.Set[string], bool) {
- tables, ok := ctx.Value(tablesKey).(mapset.Set[string])
- return tables, ok
-}
-
-type Caches struct {
- Conf *Config
- queue *sync.Map
-}
-
-type Config struct {
- Easer bool
- Cacher Cacher
-}
-
-func (c *Caches) Name() string {
- return "gorm:caches"
-}
-
-func (c *Caches) Initialize(db *gorm.DB) error {
- if c.Conf == nil {
- c.Conf = &Config{
- Easer: false,
- Cacher: nil,
- }
- }
-
- if c.Conf.Easer {
- c.queue = &sync.Map{}
- }
-
- callback := db.Callback().Query().Get("gorm:query")
-
- err := db.Callback().Query().Replace("gorm:query", c.Query(callback))
- if err != nil {
- return err
- }
-
- err = db.Callback().Create().After("gorm:after_create").Register("cache:after_create", c.AfterWrite)
- if err != nil {
- return err
- }
-
- err = db.Callback().Delete().After("gorm:after_delete").Register("cache:after_delete", c.AfterWrite)
- if err != nil {
- return err
- }
-
- err = db.Callback().Update().After("gorm:after_update").Register("cache:after_update", c.AfterWrite)
- if err != nil {
- return err
- }
-
- err = db.Callback().Raw().After("gorm:raw").Register("cache:after_raw", c.AfterWrite)
- if err != nil {
- return err
- }
-
- // Run sql SHOW default_transaction_isolation; to make sure the transaction isolation level is >= read committed
- err = db.Callback().Begin().Register("cache:after_begin", c.AfterBegin)
- if err != nil {
- return err
- }
-
- err = db.Callback().Commit().Register("cache:after_commit", c.AfterCommit)
- if err != nil {
- return err
- }
-
- return nil
-}
-
-func (c *Caches) Query(callback func(*gorm.DB)) func(*gorm.DB) {
- return func(db *gorm.DB) {
- if db.Error != nil {
- return
- }
-
- if c.Conf.Easer == false && c.Conf.Cacher == nil {
- callback(db)
- return
- }
-
- identifier := buildIdentifier(db)
- if stringutils.ContainsI(identifier, "INSERT INTO") {
- callback(db)
- c.AfterWrite(db)
- return
- }
-
- if _, ok := db.Statement.ConnPool.(gorm.TxCommitter); ok {
- // query from database directly when in transaction
- callback(db)
- return
- }
-
- if db.DryRun {
- return
- }
-
- if res, ok := c.checkCache(identifier); ok {
- res.replaceOn(db)
- return
- }
-
- c.ease(db, identifier, callback)
- if db.Error != nil {
- return
- }
-
- c.storeInCache(db, identifier)
- if db.Error != nil {
- return
- }
- }
-}
-
-func (c *Caches) AfterWrite(db *gorm.DB) {
- if db.Error != nil {
- return
- }
-
- if c.Conf.Easer == false && c.Conf.Cacher == nil {
- return
- }
-
- tables := getTables(db)
-
- if len(tables) == 0 {
- return
- }
-
- if _, ok := db.Statement.ConnPool.(gorm.TxCommitter); ok {
- // query from database directly when in transaction
- if value, ok := TablesFromContext(db.Statement.Context); ok {
- value.Append(tables...)
- }
- return
- }
-
- if len(tables) == 1 {
- c.deleteCache(db, tables[0])
- } else {
- c.deleteCache(db, tables[0], tables[1:]...)
- }
-
- if db.Error != nil {
- return
- }
-}
-
-func (c *Caches) AfterBegin(db *gorm.DB) {
- if db.Error != nil {
- return
- }
-
- if c.Conf.Easer == false && c.Conf.Cacher == nil {
- return
- }
-
- db.Statement.Context = NewTablesContext(db.Statement.Context, mapset.NewSet[string]())
-}
-
-func (c *Caches) AfterCommit(db *gorm.DB) {
- if db.Error != nil {
- return
- }
-
- if c.Conf.Easer == false && c.Conf.Cacher == nil {
- return
- }
-
- value, ok := TablesFromContext(db.Statement.Context)
- if !ok {
- return
- }
-
- tables := value.ToSlice()
-
- if len(tables) == 0 {
- return
- }
-
- if len(tables) == 1 {
- c.deleteCache(db, tables[0])
- } else {
- c.deleteCache(db, tables[0], tables[1:]...)
- }
-
- if db.Error != nil {
- return
- }
-}
-
-func (c *Caches) ease(db *gorm.DB, identifier string, callback func(*gorm.DB)) {
- if c.Conf.Easer == false {
- //if true {
- callback(db)
- return
- }
-
- res := ease(&queryTask{
- id: identifier,
- db: db,
- queryCb: callback,
- }, c.queue).(*queryTask)
-
- if db.Error != nil {
- return
- }
-
- if res.db.Statement.Dest == db.Statement.Dest {
- return
- }
-
- q := Query{
- Dest: res.db.Statement.Dest,
- RowsAffected: res.db.Statement.RowsAffected,
- }
- q.replaceOn(db)
-}
-
-func (c *Caches) checkCache(identifier string) (res *Query, ok bool) {
- if c.Conf.Cacher != nil {
- if res = c.Conf.Cacher.Get(identifier); res != nil {
- return res, true
- }
- }
- return nil, false
-}
-
-func getTables(db *gorm.DB) []string {
- switch db.Dialector.(type) {
- case *mysql.Dialector:
- return getTablesMysql(db)
- case *postgres.Dialector:
- return getTablesPostgres(db)
- }
- return nil
-}
-
-func getTablesMysql(db *gorm.DB) []string {
- stmt, err := sqlparser.Parse(db.Statement.SQL.String())
- if err != nil {
- fmt.Println("Error: " + err.Error())
- }
- tableNames := make([]string, 0)
- _ = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
- switch node := node.(type) {
- case sqlparser.TableName:
- tableNames = append(tableNames, node.Name.CompliantName())
- }
- return true, nil
- }, stmt)
- tableNames = lo.Filter(tableNames, func(x string, index int) bool {
- return stringutils.IsNotEmpty(x)
- })
- tableNames = lo.Uniq(tableNames)
- return tableNames
-}
-
-func getTablesPostgres(db *gorm.DB) []string {
- tableNames := make([]string, 0)
- sql := db.Statement.SQL.String()
- w := &walk.AstWalker{
- Fn: func(ctx interface{}, node interface{}) (stop bool) {
- //log.Printf("%T", node)
- switch expr := node.(type) {
- case *tree.TableName:
- var sb strings.Builder
- fmtCtx := tree.NewFmtCtx(tree.FmtSimple)
- expr.TableNamePrefix.Format(fmtCtx)
- sb.WriteString(fmtCtx.String())
-
- if sb.Len() > 0 {
- sb.WriteString(".")
- }
-
- fmtCtx = tree.NewFmtCtx(tree.FmtSimple)
- expr.TableName.Format(fmtCtx)
- sb.WriteString(fmtCtx.String())
-
- tableNames = append(tableNames, sb.String())
- case *tree.Insert:
- fmtCtx := tree.NewFmtCtx(tree.FmtSimple)
- expr.Table.Format(fmtCtx)
- tableName := fmtCtx.String()
- tableNames = append(tableNames, tableName)
- case *tree.Update:
- fmtCtx := tree.NewFmtCtx(tree.FmtSimple)
- expr.Table.Format(fmtCtx)
- tableName := fmtCtx.String()
- tableNames = append(tableNames, tableName)
- case *tree.Delete:
- fmtCtx := tree.NewFmtCtx(tree.FmtSimple)
- expr.Table.Format(fmtCtx)
- tableName := fmtCtx.String()
- tableNames = append(tableNames, tableName)
- }
- return false
- },
- }
- stmts, err := parser.Parse(sql)
- if err != nil {
- return nil
- }
- _, err = w.Walk(stmts, nil)
- if err != nil {
- return nil
- }
- return tableNames
-}
-
-func (c *Caches) storeInCache(db *gorm.DB, identifier string) {
- if c.Conf.Cacher != nil {
- if _, ok := db.Statement.Dest.(map[string]interface{}); ok {
- fmt.Println(db.Statement.Dest)
- }
- err := c.Conf.Cacher.Store(identifier, &Query{
- Tags: getTables(db),
- Dest: db.Statement.Dest,
- RowsAffected: db.Statement.RowsAffected,
- })
- if err != nil {
- _ = db.AddError(err)
- }
- }
-}
-
-func (c *Caches) deleteCache(db *gorm.DB, tag string, tags ...string) {
- if c.Conf.Cacher != nil {
- err := c.Conf.Cacher.Delete(tag, tags...)
- if err != nil {
- _ = db.AddError(err)
- }
- }
-}
diff --git a/toolkit/caches/caches_test.go b/toolkit/caches/caches_test.go
deleted file mode 100644
index ee14c6fc..00000000
--- a/toolkit/caches/caches_test.go
+++ /dev/null
@@ -1,423 +0,0 @@
-package caches
-
-import (
- "fmt"
- "github.com/goccy/go-reflect"
- "sync"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/utils/tests"
-)
-
-type mockDest struct {
- Result string
-}
-
-func TestCaches_Name(t *testing.T) {
- caches := &Caches{
- Conf: &Config{
- Easer: true,
- Cacher: nil,
- },
- }
- expectedName := "gorm:caches"
- if act := caches.Name(); act != expectedName {
- t.Errorf("Name on caches did not return the expected value, expected: %s, actual: %s",
- expectedName, act)
- }
-}
-
-func TestCaches_Initialize(t *testing.T) {
- t.Run("empty config", func(t *testing.T) {
- caches := &Caches{}
- db, err := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- if err != nil {
- t.Fatalf("gorm initialization resulted into an unexpected error, %s", err.Error())
- }
-
- originalQueryCb := db.Callback().Query().Get("gorm:query")
-
- err = db.Use(caches)
- if err != nil {
- t.Fatalf("gorm:caches loading resulted into an unexpected error, %s", err.Error())
- }
-
- newQueryCallback := db.Callback().Query().Get("gorm:query")
-
- if reflect.ValueOf(originalQueryCb).Pointer() == reflect.ValueOf(newQueryCallback).Pointer() {
- t.Errorf("loading of gorm:caches, expected to replace the `gorm:query` callback")
- }
-
- if reflect.ValueOf(newQueryCallback).Pointer() != reflect.ValueOf(caches.Query).Pointer() {
- t.Errorf("loading of gorm:caches, expected to replace the `gorm:query` callback, with caches.Query")
- }
-
- if reflect.ValueOf(originalQueryCb).Pointer() != reflect.ValueOf(caches.queryCb).Pointer() {
- t.Errorf("loading of gorm:caches, expected to load original `gorm:query` callback, to caches.queryCb")
- }
- })
- t.Run("config - easer", func(t *testing.T) {
- caches := &Caches{
- Conf: &Config{
- Easer: true,
- Cacher: nil,
- },
- }
- db, err := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- if err != nil {
- t.Fatalf("gorm initialization resulted into an unexpected error, %s", err.Error())
- }
-
- originalQueryCb := db.Callback().Query().Get("gorm:query")
-
- err = db.Use(caches)
- if err != nil {
- t.Fatalf("gorm:caches loading resulted into an unexpected error, %s", err.Error())
- }
-
- newQueryCallback := db.Callback().Query().Get("gorm:query")
-
- if reflect.ValueOf(originalQueryCb).Pointer() == reflect.ValueOf(newQueryCallback).Pointer() {
- t.Errorf("loading of gorm:caches, expected to replace the `gorm:query` callback")
- }
-
- if reflect.ValueOf(newQueryCallback).Pointer() != reflect.ValueOf(caches.Query).Pointer() {
- t.Errorf("loading of gorm:caches, expected to replace the `gorm:query` callback, with caches.Query")
- }
-
- if reflect.ValueOf(originalQueryCb).Pointer() != reflect.ValueOf(caches.queryCb).Pointer() {
- t.Errorf("loading of gorm:caches, expected to load original `gorm:query` callback, to caches.queryCb")
- }
- })
-}
-
-func TestCaches_Query(t *testing.T) {
- t.Run("nothing enabled", func(t *testing.T) {
- conf := &Config{
- Easer: false,
- Cacher: nil,
- }
- db, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- db.Statement.Dest = &mockDest{}
- caches := &Caches{
- Conf: conf,
- queryCb: func(db *gorm.DB) {
- db.Statement.Dest.(*mockDest).Result = db.Statement.SQL.String()
- },
- }
-
- // Set the query SQL into something specific
- exampleQuery := "demo-query"
- db.Statement.SQL.WriteString(exampleQuery)
-
- caches.Query(db) // Execute the query
-
- if db.Error != nil {
- t.Fatalf("an unexpected error has occurred, %v", db.Error)
- }
-
- if db.Statement.Dest == nil {
- t.Fatal("no query result was set after caches Query was executed")
- }
-
- if res := db.Statement.Dest.(*mockDest); res.Result != exampleQuery {
- t.Errorf("the execution of the Query expected a result of `%s`, got `%s`", exampleQuery, res)
- }
- })
-
- t.Run("easer only", func(t *testing.T) {
- conf := &Config{
- Easer: true,
- Cacher: nil,
- }
-
- t.Run("one query", func(t *testing.T) {
- db, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- db.Statement.Dest = &mockDest{}
- caches := &Caches{
- Conf: conf,
-
- queue: &sync.Map{},
- queryCb: func(db *gorm.DB) {
- db.Statement.Dest.(*mockDest).Result = db.Statement.SQL.String()
- },
- }
-
- // Set the query SQL into something specific
- exampleQuery := "demo-query"
- db.Statement.SQL.WriteString(exampleQuery)
-
- caches.Query(db) // Execute the query
-
- if db.Error != nil {
- t.Fatalf("an unexpected error has occurred, %v", db.Error)
- }
-
- if db.Statement.Dest == nil {
- t.Fatal("no query result was set after caches Query was executed")
- }
-
- if res := db.Statement.Dest.(*mockDest); res.Result != exampleQuery {
- t.Errorf("the execution of the Query expected a result of `%s`, got `%s`", exampleQuery, res)
- }
- })
-
- t.Run("two identical queries", func(t *testing.T) {
- t.Run("without error", func(t *testing.T) {
- var incr int32
- db1, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- db1.Statement.Dest = &mockDest{}
- db2, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- db2.Statement.Dest = &mockDest{}
-
- caches := &Caches{
- Conf: conf,
-
- queue: &sync.Map{},
- queryCb: func(db *gorm.DB) {
- time.Sleep(1 * time.Second)
- atomic.AddInt32(&incr, 1)
-
- db.Statement.Dest.(*mockDest).Result = fmt.Sprintf("%d", atomic.LoadInt32(&incr))
- },
- }
-
- // Set the queries' SQL into something specific
- exampleQuery := "demo-query"
- db1.Statement.SQL.WriteString(exampleQuery)
- db2.Statement.SQL.WriteString(exampleQuery)
-
- wg := &sync.WaitGroup{}
- wg.Add(2)
- go func() {
- caches.Query(db1) // Execute the query
- wg.Done()
- }()
- go func() {
- time.Sleep(500 * time.Millisecond) // Execute the second query half a second later
- caches.Query(db2) // Execute the query
- wg.Done()
- }()
- wg.Wait()
-
- if db1.Error != nil {
- t.Fatalf("an unexpected error has occurred, %v", db1.Error)
- }
-
- if db2.Error != nil {
- t.Fatalf("an unexpected error has occurred, %v", db2.Error)
- }
-
- if act := atomic.LoadInt32(&incr); act != 1 {
- t.Errorf("when executing two identical queries, expected to run %d time, but %d", 1, act)
- }
- })
- })
-
- t.Run("two different queries", func(t *testing.T) {
- var incr int32
- db1, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- db1.Statement.Dest = &mockDest{}
- db2, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- db2.Statement.Dest = &mockDest{}
-
- caches := &Caches{
- Conf: conf,
-
- queue: &sync.Map{},
- queryCb: func(db *gorm.DB) {
- time.Sleep(1 * time.Second)
- atomic.AddInt32(&incr, 1)
-
- db.Statement.Dest.(*mockDest).Result = fmt.Sprintf("%d", atomic.LoadInt32(&incr))
- },
- }
-
- // Set the queries' SQL into something specific
- exampleQuery1 := "demo-query-1"
- db1.Statement.SQL.WriteString(exampleQuery1)
- exampleQuery2 := "demo-query-2"
- db2.Statement.SQL.WriteString(exampleQuery2)
-
- wg := &sync.WaitGroup{}
- wg.Add(2)
- go func() {
- caches.Query(db1) // Execute the query
- wg.Done()
- }()
- go func() {
- time.Sleep(500 * time.Millisecond) // Execute the second query half a second later
- caches.Query(db2) // Execute the query
- wg.Done()
- }()
- wg.Wait()
-
- if db1.Error != nil {
- t.Fatalf("an unexpected error has occurred, %v", db1.Error)
- }
-
- if db2.Error != nil {
- t.Fatalf("an unexpected error has occurred, %v", db2.Error)
- }
-
- if act := atomic.LoadInt32(&incr); act != 2 {
- t.Errorf("when executing two identical queries, expected to run %d times, but %d", 2, act)
- }
- })
- })
-
- t.Run("cacher only", func(t *testing.T) {
- t.Run("one query", func(t *testing.T) {
- t.Run("with error", func(t *testing.T) {
- db, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- db.Statement.Dest = &mockDest{}
-
- caches := &Caches{
- Conf: &Config{
- Easer: false,
- Cacher: &cacherStoreErrorMock{},
- },
-
- queue: &sync.Map{},
- queryCb: func(db *gorm.DB) {
- db.Statement.Dest.(*mockDest).Result = db.Statement.SQL.String()
- },
- }
-
- // Set the query SQL into something specific
- exampleQuery := "demo-query"
- db.Statement.SQL.WriteString(exampleQuery)
-
- caches.Query(db) // Execute the query
-
- if db.Error == nil {
- t.Error("an error was expected, got none")
- }
- })
-
- t.Run("without error", func(t *testing.T) {
- db, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- db.Statement.Dest = &mockDest{}
-
- caches := &Caches{
- Conf: &Config{
- Easer: false,
- Cacher: &cacherMock{},
- },
-
- queue: &sync.Map{},
- queryCb: func(db *gorm.DB) {
- db.Statement.Dest.(*mockDest).Result = db.Statement.SQL.String()
- },
- }
-
- // Set the query SQL into something specific
- exampleQuery := "demo-query"
- db.Statement.SQL.WriteString(exampleQuery)
-
- caches.Query(db) // Execute the query
-
- if db.Error != nil {
- t.Fatalf("an unexpected error has occurred, %v", db.Error)
- }
-
- if db.Statement.Dest == nil {
- t.Fatal("no query result was set after caches Query was executed")
- }
-
- if res := db.Statement.Dest.(*mockDest); res.Result != exampleQuery {
- t.Errorf("the execution of the Query expected a result of `%s`, got `%s`", exampleQuery, res)
- }
- })
- })
-
- t.Run("two identical queries", func(t *testing.T) {
- var incr int32
- db1, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- db1.Statement.Dest = &mockDest{}
- db2, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- db2.Statement.Dest = &mockDest{}
-
- caches := &Caches{
- Conf: &Config{
- Easer: false,
- Cacher: &cacherMock{},
- },
-
- queue: &sync.Map{},
- queryCb: func(db *gorm.DB) {
- time.Sleep(1 * time.Second)
- atomic.AddInt32(&incr, 1)
-
- db.Statement.Dest.(*mockDest).Result = fmt.Sprintf("%d", atomic.LoadInt32(&incr))
- },
- }
-
- // Set the queries' SQL into something specific
- exampleQuery := "demo-query"
- db1.Statement.SQL.WriteString(exampleQuery)
- db2.Statement.SQL.WriteString(exampleQuery)
-
- caches.Query(db1)
- caches.Query(db2)
-
- if db1.Error != nil {
- t.Fatalf("an unexpected error has occurred, %v", db1.Error)
- }
-
- if db2.Error != nil {
- t.Fatalf("an unexpected error has occurred, %v", db2.Error)
- }
-
- if act := atomic.LoadInt32(&incr); act != 1 {
- t.Errorf("when executing two identical queries, expected to run %d time, but %d", 1, act)
- }
- })
-
- t.Run("two different queries", func(t *testing.T) {
- var incr int32
- db1, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- db1.Statement.Dest = &mockDest{}
- db2, _ := gorm.Open(tests.DummyDialector{}, &gorm.Config{})
- db2.Statement.Dest = &mockDest{}
-
- caches := &Caches{
- Conf: &Config{
- Easer: false,
- Cacher: &cacherMock{},
- },
-
- queue: &sync.Map{},
- queryCb: func(db *gorm.DB) {
- time.Sleep(1 * time.Second)
- atomic.AddInt32(&incr, 1)
-
- db.Statement.Dest.(*mockDest).Result = fmt.Sprintf("%d", atomic.LoadInt32(&incr))
- },
- }
-
- // Set the queries' SQL into something specific
- exampleQuery1 := "demo-query-1"
- db1.Statement.SQL.WriteString(exampleQuery1)
- exampleQuery2 := "demo-query-2"
- db2.Statement.SQL.WriteString(exampleQuery2)
-
- caches.Query(db1)
- if db1.Error != nil {
- t.Fatalf("an unexpected error has occurred, %v", db1.Error)
- }
-
- caches.Query(db2)
- if db2.Error != nil {
- t.Fatalf("an unexpected error has occurred, %v", db2.Error)
- }
-
- if act := atomic.LoadInt32(&incr); act != 2 {
- t.Errorf("when executing two identical queries, expected to run %d times, but %d", 2, act)
- }
- })
- })
-}
diff --git a/toolkit/caches/easer.go b/toolkit/caches/easer.go
deleted file mode 100644
index b3135442..00000000
--- a/toolkit/caches/easer.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package caches
-
-import "sync"
-
-func ease(t task, queue *sync.Map) task {
- eq := &eased{
- task: t,
- wg: &sync.WaitGroup{},
- }
- eq.wg.Add(1)
-
- runner, ok := queue.LoadOrStore(t.GetId(), eq)
- et := runner.(*eased)
-
- // If this request is the first of its kind, we execute the Run
- if !ok {
- et.task.Run()
-
- queue.Delete(et.task.GetId())
- et.wg.Done()
- }
-
- et.wg.Wait()
- return et.task
-}
-
-type eased struct {
- task task
- wg *sync.WaitGroup
-}
diff --git a/toolkit/caches/easer_test.go b/toolkit/caches/easer_test.go
deleted file mode 100644
index c2b49e53..00000000
--- a/toolkit/caches/easer_test.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package caches
-
-import (
- "sync"
- "testing"
- "time"
-)
-
-func TestEase(t *testing.T) {
- t.Run("same queries", func(t *testing.T) {
- queue := &sync.Map{}
-
- myTask := &mockTask{
- delay: 1 * time.Second,
- expRes: "expect-this",
- id: "unique-id",
- }
- myDupTask := &mockTask{
- delay: 1 * time.Second,
- expRes: "not-this",
- id: "unique-id",
- }
-
- wg := &sync.WaitGroup{}
- wg.Add(2)
-
- var (
- myTaskRes *mockTask
- myDupTaskRes *mockTask
- )
-
- // Both queries will run at the same time, the second one will run half a second later
- go func() {
- myTaskRes = ease(myTask, queue).(*mockTask)
- wg.Done()
- }()
- go func() {
- time.Sleep(500 * time.Millisecond)
- myDupTaskRes = ease(myDupTask, queue).(*mockTask)
- wg.Done()
- }()
- wg.Wait()
-
- if myTaskRes.actRes != myTaskRes.expRes {
- t.Error("expected first query to be executed")
- }
-
- if myTaskRes.actRes != myDupTaskRes.actRes {
- t.Errorf("expected same result from both tasks, expected: %s, actual: %s",
- myTaskRes.actRes, myDupTaskRes.actRes)
- }
- })
-
- t.Run("different queries", func(t *testing.T) {
- queue := &sync.Map{}
-
- myTask := &mockTask{
- delay: 1 * time.Second,
- expRes: "expect-this",
- id: "unique-id",
- }
- myDupTask := &mockTask{
- delay: 1 * time.Second,
- expRes: "not-this",
- id: "other-unique-id",
- }
-
- wg := &sync.WaitGroup{}
- wg.Add(2)
-
- var (
- myTaskRes *mockTask
- myDupTaskRes *mockTask
- )
-
- // Both queries will run at the same time, the second one will run half a second later
- go func() {
- myTaskRes = ease(myTask, queue).(*mockTask)
- wg.Done()
- }()
- go func() {
- time.Sleep(500 * time.Millisecond)
- myDupTaskRes = ease(myDupTask, queue).(*mockTask)
- wg.Done()
- }()
- wg.Wait()
-
- if myTaskRes.actRes != myTaskRes.expRes {
- t.Errorf("expected first query to be executed, expected: %s, actual: %s",
- myTaskRes.actRes, myTaskRes.expRes)
- }
-
- if myTaskRes.actRes == myDupTaskRes.actRes {
- t.Errorf("expected different result from both tasks, expected: %s, actual: %s",
- myTaskRes.actRes, myDupTaskRes.actRes)
- }
-
- if myDupTaskRes.actRes != myDupTaskRes.expRes {
- t.Error("expected second query to be executed")
- }
- })
-}
diff --git a/toolkit/caches/identifier.go b/toolkit/caches/identifier.go
deleted file mode 100644
index e85e0253..00000000
--- a/toolkit/caches/identifier.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package caches
-
-import (
- "fmt"
- "github.com/goccy/go-reflect"
-
- "github.com/samber/lo"
- "github.com/wubin1989/gorm/callbacks"
-
- "github.com/wubin1989/gorm"
-)
-
-func buildIdentifier(db *gorm.DB) string {
- // Build query identifier,
- // for that reason we need to compile all arguments into a string
- // and concat them with the SQL query itself
-
- callbacks.BuildQuerySQL(db)
- var (
- identifier string
- query string
- queryArgs string
- )
- query = db.Statement.SQL.String()
- vars := lo.Map[interface{}, interface{}](db.Statement.Vars, func(item interface{}, index int) interface{} {
- if reflect.ValueOf(item).Kind() == reflect.Ptr {
- return reflect.ValueOf(item).Elem().Interface()
- }
- return item
- })
- queryArgs = fmt.Sprintf("%v", vars)
- identifier = fmt.Sprintf("%s-%s", query, queryArgs)
-
- return identifier
-}
diff --git a/toolkit/caches/identifier_test.go b/toolkit/caches/identifier_test.go
deleted file mode 100644
index 7d403306..00000000
--- a/toolkit/caches/identifier_test.go
+++ /dev/null
@@ -1,20 +0,0 @@
-package caches
-
-import (
- "testing"
-
- "github.com/wubin1989/gorm"
-)
-
-func Test_buildIdentifier(t *testing.T) {
- db := &gorm.DB{}
- db.Statement = &gorm.Statement{}
- db.Statement.SQL.WriteString("TEST-SQL")
- db.Statement.Vars = append(db.Statement.Vars, "test", 123, 12.3, true, false, []string{"test", "me"})
-
- actual := buildIdentifier(db)
- expected := "TEST-SQL-[test 123 12.3 true false [test me]]"
- if actual != expected {
- t.Errorf("buildIdentifier expected to return `%s` but got `%s`", expected, actual)
- }
-}
diff --git a/toolkit/caches/query.go b/toolkit/caches/query.go
deleted file mode 100644
index ecb9d9fe..00000000
--- a/toolkit/caches/query.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package caches
-
-import "github.com/wubin1989/gorm"
-
-type Query struct {
- Tags []string
- Dest interface{}
- RowsAffected int64
-}
-
-func (q *Query) replaceOn(db *gorm.DB) {
- SetPointedValue(db.Statement.Dest, q.Dest)
- SetPointedValue(&db.Statement.RowsAffected, &q.RowsAffected)
-}
diff --git a/toolkit/caches/query_task.go b/toolkit/caches/query_task.go
deleted file mode 100644
index 5a58c7d1..00000000
--- a/toolkit/caches/query_task.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package caches
-
-import "github.com/wubin1989/gorm"
-
-type queryTask struct {
- id string
- db *gorm.DB
- queryCb func(db *gorm.DB)
-}
-
-func (q *queryTask) GetId() string {
- return q.id
-}
-
-func (q *queryTask) Run() {
- q.queryCb(q.db)
-}
diff --git a/toolkit/caches/query_task_test.go b/toolkit/caches/query_task_test.go
deleted file mode 100644
index b7fabf5a..00000000
--- a/toolkit/caches/query_task_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package caches
-
-import (
- "sync/atomic"
- "testing"
-
- "github.com/wubin1989/gorm"
-)
-
-func TestQueryTask_GetId(t *testing.T) {
- task := &queryTask{
- id: "myId",
- db: nil,
- queryCb: func(db *gorm.DB) {
- },
- }
-
- if task.GetId() != "myId" {
- t.Error("GetId on queryTask returned an unexpected value")
- }
-}
-
-func TestQueryTask_Run(t *testing.T) {
- var inc int32
- task := &queryTask{
- id: "myId",
- db: nil,
- queryCb: func(db *gorm.DB) {
- atomic.AddInt32(&inc, 1)
- },
- }
-
- task.Run()
-
- if atomic.LoadInt32(&inc) != 1 {
- t.Error("Run on queryTask was expected to execute the callback specified once")
- }
-}
diff --git a/toolkit/caches/reflection.go b/toolkit/caches/reflection.go
deleted file mode 100644
index cececda3..00000000
--- a/toolkit/caches/reflection.go
+++ /dev/null
@@ -1,81 +0,0 @@
-package caches
-
-import (
- "errors"
- "fmt"
- "github.com/goccy/go-reflect"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
-
- "github.com/wubin1989/gorm/schema"
-)
-
-func SetPointedValue(dest interface{}, src interface{}) {
- copier.DeepCopy(src, dest)
-}
-
-func deepCopy(src, dst interface{}) error {
- srcVal := reflect.ValueOf(src)
- dstVal := reflect.ValueOf(dst)
-
- if srcVal.Kind() == reflect.Ptr {
- srcVal = srcVal.Elem()
- }
-
- if srcVal.Type() != dstVal.Elem().Type() {
- return errors.New("src and dst must be of the same type")
- }
-
- return copyValue(srcVal, dstVal.Elem())
-}
-
-func copyValue(src, dst reflect.Value) error {
- switch src.Kind() {
- case reflect.Ptr:
- src = src.Elem()
- dst.Set(reflect.New(src.Type()))
- err := copyValue(src, dst.Elem())
- if err != nil {
- return err
- }
-
- case reflect.Struct:
- for i := 0; i < src.NumField(); i++ {
- if src.Type().Field(i).PkgPath != "" {
- return fmt.Errorf("%w: %+v", schema.ErrUnsupportedDataType, src.Type().Field(i).Name)
- }
- err := copyValue(src.Field(i), dst.Field(i))
- if err != nil {
- return err
- }
- }
-
- case reflect.Slice:
- newSlice := reflect.MakeSlice(src.Type(), src.Len(), src.Cap())
- for i := 0; i < src.Len(); i++ {
- err := copyValue(src.Index(i), newSlice.Index(i))
- if err != nil {
- return err
- }
- }
- dst.Set(newSlice)
-
- case reflect.Map:
- newMap := reflect.MakeMapWithSize(src.Type(), src.Len())
- for _, key := range src.MapKeys() {
- value := src.MapIndex(key)
- newValue := reflect.New(value.Type()).Elem()
- err := copyValue(value, newValue)
- if err != nil {
- return err
- }
- newMap.SetMapIndex(key, newValue)
- }
- dst.Set(newMap)
-
- default:
- dst.Set(src)
- }
-
- return nil
-}
diff --git a/toolkit/caches/reflection_test.go b/toolkit/caches/reflection_test.go
deleted file mode 100644
index 9fb726cc..00000000
--- a/toolkit/caches/reflection_test.go
+++ /dev/null
@@ -1,215 +0,0 @@
-package caches
-
-import (
- "github.com/goccy/go-reflect"
- "testing"
-)
-
-type unsupportedMockStruct struct {
- ExportedField string
- unexportedField string
- ExportedSliceField []string
- unexportedSliceField []string
- ExportedMapField map[string]string
- unexportedMapField map[string]string
-}
-
-type supportedMockStruct struct {
- ExportedField string
- ExportedSliceField []string
- ExportedMapField map[string]string
-}
-
-func Test_SetPointedValue(t *testing.T) {
- src := &struct {
- Name string
- }{
- Name: "Test",
- }
-
- dest := &struct {
- Name string
- }{}
-
- SetPointedValue(dest, src)
-
- if !reflect.DeepEqual(src, dest) {
- t.Error("SetPointedValue was expected to point the dest to the source")
- }
-
- if dest.Name != src.Name {
- t.Errorf("src and dest were expected to have the same name, src.Name `%s`, dest.Name `%s`", src.Name, dest.Name)
- }
-}
-
-func Test_deepCopy(t *testing.T) {
- t.Run("struct", func(t *testing.T) {
- t.Run("supported", func(t *testing.T) {
- srcStruct := supportedMockStruct{
- ExportedField: "exported field",
- ExportedSliceField: []string{"1st elem of an exported slice field", "2nd elem of an exported slice field"},
- ExportedMapField: map[string]string{
- "key1": "exported map elem",
- "key2": "exported map elem",
- },
- }
- dstStruct := supportedMockStruct{}
-
- if err := deepCopy(srcStruct, &dstStruct); err != nil {
- t.Errorf("deepCopy returned an unexpected error %+v", err)
- }
-
- if !reflect.DeepEqual(srcStruct, dstStruct) {
- t.Errorf("deepCopy failed to copy structure: got %+v, want %+v", dstStruct, srcStruct)
- }
- })
- t.Run("unsupported", func(t *testing.T) {
- srcStruct := unsupportedMockStruct{
- ExportedField: "exported field",
- unexportedField: "unexported field",
- ExportedSliceField: []string{"1st elem of an exported slice field", "2nd elem of an exported slice field"},
- unexportedSliceField: []string{"1st elem of an unexported slice field", "2nd elem of an unexported slice field"},
- ExportedMapField: map[string]string{
- "key1": "exported map elem",
- "key2": "exported map elem",
- },
- unexportedMapField: map[string]string{
- "key1": "unexported map elem",
- "key2": "unexported map elem",
- },
- }
- dstStruct := unsupportedMockStruct{}
-
- if err := deepCopy(srcStruct, &dstStruct); err == nil {
- t.Error("deepCopy was expected to fail copying an structure with unexported fields")
- }
- })
- })
-
- t.Run("map", func(t *testing.T) {
- t.Run("map[string]string", func(t *testing.T) {
- srcMap := map[string]string{
- "key1": "value1",
- "key2": "value2",
- }
- dstMap := make(map[string]string)
-
- if err := deepCopy(srcMap, &dstMap); err != nil {
- t.Errorf("deepCopy returned an unexpected error %+v", err)
- }
-
- if !reflect.DeepEqual(srcMap, dstMap) {
- t.Errorf("deepCopy failed to copy map: got %+v, want %+v", dstMap, srcMap)
- }
- })
-
- t.Run("map[string]struct", func(t *testing.T) {
- srcMap := map[string]supportedMockStruct{
- "key1": {
- ExportedField: "exported field",
- ExportedSliceField: []string{"1st elem of an exported slice field", "2nd elem of an exported slice field"},
- ExportedMapField: map[string]string{
- "key1": "exported map elem",
- "key2": "exported map elem",
- },
- },
- "key2": {
- ExportedField: "exported field",
- ExportedSliceField: []string{"1st elem of an exported slice field", "2nd elem of an exported slice field"},
- ExportedMapField: map[string]string{
- "key1": "exported map elem",
- "key2": "exported map elem",
- },
- },
- }
- dstMap := make(map[string]supportedMockStruct)
-
- if err := deepCopy(srcMap, &dstMap); err != nil {
- t.Errorf("deepCopy returned an unexpected error %+v", err)
- }
-
- if !reflect.DeepEqual(srcMap, dstMap) {
- t.Errorf("deepCopy failed to copy map: got %+v, want %+v", dstMap, srcMap)
- }
- })
- })
-
- t.Run("slice", func(t *testing.T) {
- t.Run("[]string", func(t *testing.T) {
- srcSlice := []string{"A", "B", "C"}
- dstSlice := make([]string, len(srcSlice))
-
- if err := deepCopy(srcSlice, &dstSlice); err != nil {
- t.Errorf("deepCopy returned an unexpected error %+v", err)
- }
-
- if !reflect.DeepEqual(srcSlice, dstSlice) {
- t.Errorf("deepCopy failed to copy slice: got %+v, want %+v", dstSlice, srcSlice)
- }
- })
- t.Run("[]struct", func(t *testing.T) {
- srcSlice := []supportedMockStruct{
- {
- ExportedField: "exported field",
- ExportedSliceField: []string{"1st elem of an exported slice field", "2nd elem of an exported slice field"},
- ExportedMapField: map[string]string{
- "key1": "exported map elem",
- "key2": "exported map elem",
- },
- }, {
- ExportedField: "exported field",
- ExportedSliceField: []string{"1st elem of an exported slice field", "2nd elem of an exported slice field"},
- ExportedMapField: map[string]string{
- "key1": "exported map elem",
- "key2": "exported map elem",
- },
- }, {
- ExportedField: "exported field",
- ExportedSliceField: []string{"1st elem of an exported slice field", "2nd elem of an exported slice field"},
- ExportedMapField: map[string]string{
- "key1": "exported map elem",
- "key2": "exported map elem",
- },
- },
- }
- dstSlice := make([]supportedMockStruct, len(srcSlice))
-
- if err := deepCopy(srcSlice, &dstSlice); err != nil {
- t.Errorf("deepCopy returned an unexpected error %+v", err)
- }
-
- if !reflect.DeepEqual(srcSlice, dstSlice) {
- t.Errorf("deepCopy failed to copy slice: got %+v, want %+v", dstSlice, srcSlice)
- }
- })
- })
-
- t.Run("pointer", func(t *testing.T) {
- srcStruct := &supportedMockStruct{
- ExportedField: "exported field",
- ExportedSliceField: []string{"1st elem of an exported slice field", "2nd elem of an exported slice field"},
- ExportedMapField: map[string]string{
- "key1": "exported map elem",
- "key2": "exported map elem",
- },
- }
- dstStruct := &supportedMockStruct{}
-
- if err := deepCopy(srcStruct, dstStruct); err != nil {
- t.Errorf("deepCopy returned an unexpected error %+v", err)
- }
-
- if !reflect.DeepEqual(srcStruct, dstStruct) {
- t.Errorf("deepCopy failed to copy structure: got %+v, want %+v", dstStruct, srcStruct)
- }
- })
-
- t.Run("mismatched", func(t *testing.T) {
- src := "a string"
- dst := 123
-
- if err := deepCopy(src, &dst); err == nil {
- t.Error("deepCopy did not return an error when provided mismatched types")
- }
- })
-}
diff --git a/toolkit/caches/task.go b/toolkit/caches/task.go
deleted file mode 100644
index 2b470ff3..00000000
--- a/toolkit/caches/task.go
+++ /dev/null
@@ -1,6 +0,0 @@
-package caches
-
-type task interface {
- GetId() string
- Run()
-}
diff --git a/toolkit/caches/task_test.go b/toolkit/caches/task_test.go
deleted file mode 100644
index 431a1968..00000000
--- a/toolkit/caches/task_test.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package caches
-
-import (
- "time"
-)
-
-type mockTask struct {
- delay time.Duration
- actRes string
- expRes string
- id string
-}
-
-func (q *mockTask) GetId() string {
- return q.id
-}
-
-func (q *mockTask) Run() {
- time.Sleep(q.delay)
- q.actRes = q.expRes
-}
diff --git a/toolkit/caller/caller.go b/toolkit/caller/caller.go
deleted file mode 100644
index b3474494..00000000
--- a/toolkit/caller/caller.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package caller
-
-import (
- "fmt"
- "runtime"
-)
-
-type Caller struct {
- Name string
- File string
- Line int
-}
-
-func (c Caller) String() string {
- return fmt.Sprintf("called from %s on %s#%d", c.Name, c.File, c.Line)
-}
-
-func NewCaller() Caller {
- var caller Caller
- pc, file, line, ok := runtime.Caller(1)
- details := runtime.FuncForPC(pc)
- if ok && details != nil {
- caller.File = file
- caller.Line = line
- caller.Name = details.Name()
- }
- return caller
-}
diff --git a/toolkit/caller/caller_test.go b/toolkit/caller/caller_test.go
deleted file mode 100644
index ac535d0d..00000000
--- a/toolkit/caller/caller_test.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package caller_test
-
-import (
- "fmt"
- "testing"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
-)
-
-func TestCaller_String(t *testing.T) {
- c := caller.NewCaller()
- fmt.Println(c.String())
-}
diff --git a/toolkit/cast/string.go b/toolkit/cast/string.go
deleted file mode 100644
index 1fd8a60a..00000000
--- a/toolkit/cast/string.go
+++ /dev/null
@@ -1,174 +0,0 @@
-package cast
-
-import (
- "fmt"
- "strconv"
-
- "github.com/pkg/errors"
- "github.com/shopspring/decimal"
-)
-
-func ToInt(s string) int {
- v, err := strconv.ParseInt(s, 0, 0)
- if err == nil {
- return int(v)
- }
- return 0
-}
-
-func ToIntE(s string) (int, error) {
- v, err := strconv.ParseInt(s, 0, 0)
- if err == nil {
- return int(v), nil
- }
- return 0, fmt.Errorf("unable to cast string %#v to int", s)
-}
-
-func ToInt8E(s string) (int8, error) {
- v, err := strconv.ParseInt(s, 0, 8)
- if err == nil {
- return int8(v), nil
- }
- return 0, fmt.Errorf("unable to cast string %#v to int8", s)
-}
-
-func ToInt16E(s string) (int16, error) {
- v, err := strconv.ParseInt(s, 0, 16)
- if err == nil {
- return int16(v), nil
- }
- return 0, fmt.Errorf("unable to cast string %#v to int16", s)
-}
-
-func ToInt32E(s string) (int32, error) {
- v, err := strconv.ParseInt(s, 0, 32)
- if err == nil {
- return int32(v), nil
- }
- return 0, fmt.Errorf("unable to cast string %#v to int32", s)
-}
-
-func ToInt64E(s string) (int64, error) {
- v, err := strconv.ParseInt(s, 0, 64)
- if err == nil {
- return v, nil
- }
- return 0, fmt.Errorf("unable to cast string %#v to int64", s)
-}
-
-func ToUintE(s string) (uint, error) {
- v, err := strconv.ParseUint(s, 0, 0)
- if err == nil {
- return uint(v), nil
- }
- return 0, fmt.Errorf("unable to cast string %#v to uint", s)
-}
-
-func ToUint8E(s string) (uint8, error) {
- v, err := strconv.ParseUint(s, 0, 8)
- if err == nil {
- return uint8(v), nil
- }
- return 0, fmt.Errorf("unable to cast string %#v to uint8", s)
-}
-
-func ToUint16E(s string) (uint16, error) {
- v, err := strconv.ParseUint(s, 0, 16)
- if err == nil {
- return uint16(v), nil
- }
- return 0, fmt.Errorf("unable to cast string %#v to uint16", s)
-}
-
-func ToUint32E(s string) (uint32, error) {
- v, err := strconv.ParseUint(s, 0, 32)
- if err == nil {
- return uint32(v), nil
- }
- return 0, fmt.Errorf("unable to cast string %#v to uint32", s)
-}
-
-func ToUint64E(s string) (uint64, error) {
- v, err := strconv.ParseUint(s, 0, 64)
- if err == nil {
- return v, nil
- }
- return 0, fmt.Errorf("unable to cast string %#v to uint64", s)
-}
-
-func ToFloat32E(s string) (float32, error) {
- v, err := strconv.ParseFloat(s, 32)
- if err == nil {
- return float32(v), nil
- }
- return 0, fmt.Errorf("unable to cast string %#v to float32", s)
-}
-
-func ToFloat64E(s string) (float64, error) {
- v, err := strconv.ParseFloat(s, 64)
- if err == nil {
- return v, nil
- }
- return 0, fmt.Errorf("unable to cast string %#v to float64", s)
-}
-
-func ToErrorE(s string) (error, error) {
- return errors.New(s), nil
-}
-
-func ToBoolE(s string) (bool, error) {
- b, err := strconv.ParseBool(s)
- if err == nil {
- return b, nil
- }
- return false, fmt.Errorf("unable to cast string %#v to bool", s)
-}
-
-func ToBoolOrDefault(s string, d bool) bool {
- result := d
- if eg, err := ToBoolE(s); err == nil {
- result = eg
- }
- return result
-}
-
-func ToIntOrDefault(s string, d int) int {
- result := d
- if eg, err := ToIntE(s); err == nil {
- result = eg
- }
- return result
-}
-
-func ToInt64OrDefault(s string, d int64) int64 {
- result := d
- if eg, err := ToInt64E(s); err == nil {
- result = eg
- }
- return result
-}
-
-func ToUInt32OrDefault(s string, d uint32) uint32 {
- result := d
- if eg, err := ToUint32E(s); err == nil {
- result = eg
- }
- return result
-}
-
-func ToRuneSliceE(s string) ([]rune, error) {
- return []rune(s), nil
-}
-
-func ToByteSliceE(s string) ([]byte, error) {
- return []byte(s), nil
-}
-
-func ToDecimal(s string) decimal.Decimal {
- ret, _ := decimal.NewFromString(s)
- return ret
-}
-
-func ToDecimalE(s string) (decimal.Decimal, error) {
- return decimal.NewFromString(s)
-}
diff --git a/toolkit/cast/string_test.go b/toolkit/cast/string_test.go
deleted file mode 100644
index 11fe2b8b..00000000
--- a/toolkit/cast/string_test.go
+++ /dev/null
@@ -1,884 +0,0 @@
-package cast
-
-import (
- "github.com/goccy/go-reflect"
- "testing"
-
- "github.com/shopspring/decimal"
-)
-
-func TestToInt(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want int
- }{
- {
- name: "",
- args: args{
- s: "21",
- },
- want: 21,
- },
- {
- name: "",
- args: args{
- s: "not_int",
- },
- want: 0,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := ToInt(tt.args.s); got != tt.want {
- t.Errorf("ToInt() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToIntE(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want int
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "21",
- },
- want: 21,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: 0,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: "",
- },
- want: 0,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: "003",
- },
- want: 3,
- wantErr: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToIntE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToIntE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToIntE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToInt8E(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want int8
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "21",
- },
- want: 21,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: 0,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToInt8E(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToInt8E() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToInt8E() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToInt16E(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want int16
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "21",
- },
- want: 21,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: 0,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToInt16E(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToInt16E() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToInt16E() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToInt32E(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want int32
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "21",
- },
- want: 21,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: 0,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToInt32E(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToInt32E() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToInt32E() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToInt64E(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want int64
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "21",
- },
- want: 21,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: 0,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToInt64E(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToInt64E() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToInt64E() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToUintE(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want uint
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "21",
- },
- want: 21,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: 0,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToUintE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToUintE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToUintE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToUint8E(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want uint8
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "21",
- },
- want: 21,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: 0,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToUint8E(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToUint8E() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToUint8E() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToUint16E(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want uint16
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "21",
- },
- want: 21,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: 0,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToUint16E(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToUint16E() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToUint16E() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToUint32E(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want uint32
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "21",
- },
- want: 21,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: 0,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToUint32E(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToUint32E() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToUint32E() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToUint64E(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want uint64
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "21",
- },
- want: 21,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: 0,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToUint64E(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToUint64E() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToUint64E() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToFloat32E(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want float32
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "21.7812",
- },
- want: 21.7812,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: 0,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToFloat32E(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToFloat32E() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToFloat32E() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToFloat64E(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want float64
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "21",
- },
- want: 21,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: 0,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToFloat64E(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToFloat64E() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToFloat64E() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToErrorE(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want string
- want1 error
- }{
- {
- name: "",
- args: args{
- s: "error",
- },
- want: "error",
- want1: nil,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, got1 := ToErrorE(tt.args.s)
- if got.Error() != tt.want {
- t.Errorf("ToErrorE() got = %v, want %v", got.Error(), tt.want)
- }
- if !reflect.DeepEqual(got1, tt.want1) {
- t.Errorf("ToErrorE() got1 = %v, want %v", got1, tt.want1)
- }
- })
- }
-}
-
-func TestToBoolE(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want bool
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "true",
- },
- want: true,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: "21a",
- },
- want: false,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: "",
- },
- want: false,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToBoolE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToBoolE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("ToBoolE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-//func TestToComplex64E(t *testing.T) {
-// type args struct {
-// s string
-// }
-// tests := []struct {
-// name string
-// args args
-// want complex64
-// wantErr bool
-// }{
-// {
-// name: "",
-// args: args{
-// s: "21",
-// },
-// want: 21,
-// wantErr: false,
-// },
-// {
-// name: "",
-// args: args{
-// s: "21a",
-// },
-// want: 0,
-// wantErr: true,
-// },
-// }
-// for _, tt := range tests {
-// t.Run(tt.name, func(t *testing.T) {
-// got, err := ToComplex64E(tt.args.s)
-// if (err != nil) != tt.wantErr {
-// t.Errorf("ToComplex64E() error = %v, wantErr %v", err, tt.wantErr)
-// return
-// }
-// if got != tt.want {
-// t.Errorf("ToComplex64E() got = %v, want %v", got, tt.want)
-// }
-// })
-// }
-//}
-
-//func TestToComplex128E(t *testing.T) {
-// type args struct {
-// s string
-// }
-// tests := []struct {
-// name string
-// args args
-// want complex128
-// wantErr bool
-// }{
-// {
-// name: "",
-// args: args{
-// s: "21",
-// },
-// want: 21,
-// wantErr: false,
-// },
-// {
-// name: "",
-// args: args{
-// s: "21a",
-// },
-// want: 0,
-// wantErr: true,
-// },
-// }
-// for _, tt := range tests {
-// t.Run(tt.name, func(t *testing.T) {
-// got, err := ToComplex128E(tt.args.s)
-// if (err != nil) != tt.wantErr {
-// t.Errorf("ToComplex128E() error = %v, wantErr %v", err, tt.wantErr)
-// return
-// }
-// if got != tt.want {
-// t.Errorf("ToComplex128E() got = %v, want %v", got, tt.want)
-// }
-// })
-// }
-//}
-
-func TestToRuneSliceE(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want []rune
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "text",
- },
- want: []rune("text"),
- wantErr: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToRuneSliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToRuneSliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToRuneSliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToByteSliceE(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want []byte
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: "text",
- },
- want: []byte("text"),
- wantErr: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToByteSliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToByteSliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToByteSliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToBoolOrDefault(t *testing.T) {
- type args struct {
- s string
- d bool
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {
- name: "",
- args: args{
- s: "true",
- d: false,
- },
- want: true,
- },
- {
- name: "",
- args: args{
- s: "21a",
- d: true,
- },
- want: true,
- },
- {
- name: "",
- args: args{
- s: "",
- d: true,
- },
- want: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := ToBoolOrDefault(tt.args.s, tt.args.d); got != tt.want {
- t.Errorf("ToBoolOrDefault() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToIntOrDefault(t *testing.T) {
- type args struct {
- s string
- d int
- }
- tests := []struct {
- name string
- args args
- want int
- }{
- {
- name: "",
- args: args{
- s: "not_int",
- d: 10,
- },
- want: 10,
- },
- {
- name: "",
- args: args{
- s: "5",
- d: 10,
- },
- want: 5,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := ToIntOrDefault(tt.args.s, tt.args.d); got != tt.want {
- t.Errorf("ToIntOrDefault() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToDecimal(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want decimal.Decimal
- }{
- {
- name: "",
- args: args{
- s: "2.43",
- },
- want: decimal.NewFromFloat(2.43),
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := ToDecimal(tt.args.s); !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToDecimal() = %v, want %v", got, tt.want)
- }
- })
- }
-}
diff --git a/toolkit/cast/stringslice.go b/toolkit/cast/stringslice.go
deleted file mode 100644
index 25ce26ab..00000000
--- a/toolkit/cast/stringslice.go
+++ /dev/null
@@ -1,300 +0,0 @@
-package cast
-
-import (
- "fmt"
-
- "github.com/shopspring/decimal"
-)
-
-func ToIntSliceE(s []string) ([]int, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []int", s)
- }
- var ret []int
- for _, item := range s {
- i, err := ToIntE(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []int because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToInt8SliceE(s []string) ([]int8, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []int8", s)
- }
- var ret []int8
- for _, item := range s {
- i, err := ToInt8E(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []int8 because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToInt16SliceE(s []string) ([]int16, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []int16", s)
- }
- var ret []int16
- for _, item := range s {
- i, err := ToInt16E(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []int16 because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToInt32SliceE(s []string) ([]int32, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []int32", s)
- }
- var ret []int32
- for _, item := range s {
- i, err := ToInt32E(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []int32 because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToInt64SliceE(s []string) ([]int64, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []int64", s)
- }
- var ret []int64
- for _, item := range s {
- i, err := ToInt64E(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []int64 because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToUintSliceE(s []string) ([]uint, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []uint", s)
- }
- var ret []uint
- for _, item := range s {
- i, err := ToUintE(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []uint because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToUint8SliceE(s []string) ([]uint8, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []uint8", s)
- }
- var ret []uint8
- for _, item := range s {
- i, err := ToUint8E(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []uint8 because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToUint16SliceE(s []string) ([]uint16, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []uint16", s)
- }
- var ret []uint16
- for _, item := range s {
- i, err := ToUint16E(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []uint16 because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToUint32SliceE(s []string) ([]uint32, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []uint32", s)
- }
- var ret []uint32
- for _, item := range s {
- i, err := ToUint32E(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []uint32 because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToUint64SliceE(s []string) ([]uint64, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []uint64", s)
- }
- var ret []uint64
- for _, item := range s {
- i, err := ToUint64E(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []uint64 because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToFloat32SliceE(s []string) ([]float32, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []float32", s)
- }
- var ret []float32
- for _, item := range s {
- i, err := ToFloat32E(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []float32 because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToFloat64SliceE(s []string) ([]float64, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []float64", s)
- }
- var ret []float64
- for _, item := range s {
- i, err := ToFloat64E(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []float64 because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToErrorSliceE(s []string) ([]error, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []error", s)
- }
- var ret []error
- for _, item := range s {
- i, _ := ToErrorE(item)
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToBoolSliceE(s []string) ([]bool, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []bool", s)
- }
- var ret []bool
- for _, item := range s {
- i, err := ToBoolE(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []bool because of error %s", s, err)
- }
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-//func ToComplex64SliceE(s []string) ([]complex64, error) {
-// if s == nil {
-// return nil, fmt.Errorf("unable to cast string slice %#v to []complex64", s)
-// }
-// var ret []complex64
-// for _, item := range s {
-// i, err := ToComplex64E(item)
-// if err != nil {
-// return nil, fmt.Errorf("unable to cast string slice %#v to []complex64 because of error %s", s, err)
-// }
-// ret = append(ret, i)
-// }
-// return ret, nil
-//}
-//
-//func ToComplex128SliceE(s []string) ([]complex128, error) {
-// if s == nil {
-// return nil, fmt.Errorf("unable to cast string slice %#v to []complex128", s)
-// }
-// var ret []complex128
-// for _, item := range s {
-// i, err := ToComplex128E(item)
-// if err != nil {
-// return nil, fmt.Errorf("unable to cast string slice %#v to []complex128 because of error %s", s, err)
-// }
-// ret = append(ret, i)
-// }
-// return ret, nil
-//}
-
-func ToRuneSliceSliceE(s []string) ([][]rune, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to [][]rune", s)
- }
- var ret [][]rune
- for _, item := range s {
- i, _ := ToRuneSliceE(item)
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToByteSliceSliceE(s []string) ([][]byte, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to [][]byte", s)
- }
- var ret [][]byte
- for _, item := range s {
- i, _ := ToByteSliceE(item)
- ret = append(ret, i)
- }
- return ret, nil
-}
-
-func ToInterfaceSliceE(s []string) ([]interface{}, error) {
- if s == nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []interface{}", s)
- }
- var ret []interface{}
- for _, item := range s {
- ret = append(ret, item)
- }
- return ret, nil
-}
-
-func ToDecimalSlice(s []string) []decimal.Decimal {
- var ret []decimal.Decimal
- for _, item := range s {
- d, _ := decimal.NewFromString(item)
- ret = append(ret, d)
- }
- return ret
-}
-
-func ToDecimalSliceE(s []string) ([]decimal.Decimal, error) {
- var ret []decimal.Decimal
- for _, item := range s {
- d, err := ToDecimalE(item)
- if err != nil {
- return nil, fmt.Errorf("unable to cast string slice %#v to []decimal.Decimal because of error %s", s, err)
- }
- ret = append(ret, d)
- }
- return ret, nil
-}
diff --git a/toolkit/cast/stringslice_test.go b/toolkit/cast/stringslice_test.go
deleted file mode 100644
index a752c8b2..00000000
--- a/toolkit/cast/stringslice_test.go
+++ /dev/null
@@ -1,976 +0,0 @@
-package cast
-
-import (
- "fmt"
- "github.com/goccy/go-reflect"
- "testing"
-
- "github.com/shopspring/decimal"
-)
-
-func TestToIntSliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []int
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"2", "3", "4"},
- },
- want: []int{2, 3, 4},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"2", "3q", "4"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToIntSliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToIntSliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToIntSliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToInt8SliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []int8
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"2", "3", "4"},
- },
- want: []int8{2, 3, 4},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"2", "3q", "4"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToInt8SliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToInt8SliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToInt8SliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToInt16SliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []int16
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"2", "3", "4"},
- },
- want: []int16{2, 3, 4},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"2", "3q", "4"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToInt16SliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToInt16SliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToInt16SliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToInt32SliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []int32
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"2", "3", "4"},
- },
- want: []int32{2, 3, 4},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"2", "3q", "4"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToInt32SliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToInt32SliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToInt32SliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToInt64SliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []int64
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"2", "3", "4"},
- },
- want: []int64{2, 3, 4},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"2", "3q", "4"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToInt64SliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToInt64SliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToInt64SliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToUintSliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []uint
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"2", "3", "4"},
- },
- want: []uint{2, 3, 4},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"2", "3q", "4"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToUintSliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToUintSliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToUintSliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToUint8SliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []uint8
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"2", "3", "4"},
- },
- want: []uint8{2, 3, 4},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"2", "3q", "4"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToUint8SliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToUint8SliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToUint8SliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToUint16SliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []uint16
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"2", "3", "4"},
- },
- want: []uint16{2, 3, 4},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"2", "3q", "4"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToUint16SliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToUint16SliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToUint16SliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToUint32SliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []uint32
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"2", "3", "4"},
- },
- want: []uint32{2, 3, 4},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"2", "3q", "4"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToUint32SliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToUint32SliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToUint32SliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToUint64SliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []uint64
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"2", "3", "4"},
- },
- want: []uint64{2, 3, 4},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"2", "3q", "4"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToUint64SliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToUint64SliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToUint64SliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToFloat32SliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []float32
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"2", "3.1314926", "4"},
- },
- want: []float32{2, 3.1314926, 4},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"2", "3q", "4"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToFloat32SliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToFloat32SliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToFloat32SliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToFloat64SliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []float64
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"2", "3.1314926", "4"},
- },
- want: []float64{2, 3.1314926, 4},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"2", "3q", "4"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToFloat64SliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToFloat64SliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToFloat64SliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func ExampleToErrorSliceE() {
- got, _ := ToErrorSliceE([]string{"test1", "test2"})
- fmt.Println(got)
-
- //Output:
- //[test1 test2]
-}
-
-func TestToBoolSliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []bool
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"true", "false"},
- },
- want: []bool{true, false},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{"T", "fff"},
- },
- want: nil,
- wantErr: true,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToBoolSliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToBoolSliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToBoolSliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-//func TestToComplex64SliceE(t *testing.T) {
-// type args struct {
-// s []string
-// }
-// tests := []struct {
-// name string
-// args args
-// want []complex64
-// wantErr bool
-// }{
-// {
-// name: "",
-// args: args{
-// s: []string{"2", "3.1314926", "4"},
-// },
-// want: []complex64{2, 3.1314926, 4},
-// wantErr: false,
-// },
-// {
-// name: "",
-// args: args{
-// s: []string{"2", "3q", "4"},
-// },
-// want: nil,
-// wantErr: true,
-// },
-// }
-// for _, tt := range tests {
-// t.Run(tt.name, func(t *testing.T) {
-// got, err := ToComplex64SliceE(tt.args.s)
-// if (err != nil) != tt.wantErr {
-// t.Errorf("ToComplex64SliceE() error = %v, wantErr %v", err, tt.wantErr)
-// return
-// }
-// if !reflect.DeepEqual(got, tt.want) {
-// t.Errorf("ToComplex64SliceE() got = %v, want %v", got, tt.want)
-// }
-// })
-// }
-//}
-
-//func TestToComplex128SliceE(t *testing.T) {
-// type args struct {
-// s []string
-// }
-// tests := []struct {
-// name string
-// args args
-// want []complex128
-// wantErr bool
-// }{
-// {
-// name: "",
-// args: args{
-// s: []string{"2", "3.1314926", "4"},
-// },
-// want: []complex128{2, 3.1314926, 4},
-// wantErr: false,
-// },
-// {
-// name: "",
-// args: args{
-// s: []string{"2", "3q", "4"},
-// },
-// want: nil,
-// wantErr: true,
-// },
-// }
-// for _, tt := range tests {
-// t.Run(tt.name, func(t *testing.T) {
-// got, err := ToComplex128SliceE(tt.args.s)
-// if (err != nil) != tt.wantErr {
-// t.Errorf("ToComplex128SliceE() error = %v, wantErr %v", err, tt.wantErr)
-// return
-// }
-// if !reflect.DeepEqual(got, tt.want) {
-// t.Errorf("ToComplex128SliceE() got = %v, want %v", got, tt.want)
-// }
-// })
-// }
-//}
-
-func TestToRuneSliceSliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want [][]rune
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"test1"},
- },
- want: [][]rune{[]rune("test1")},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToRuneSliceSliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToRuneSliceSliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToRuneSliceSliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToByteSliceSliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want [][]byte
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"test1"},
- },
- want: [][]byte{[]byte("test1")},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToByteSliceSliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToByteSliceSliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToByteSliceSliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToInterfaceSliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []interface{}
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{"test1"},
- },
- want: []interface{}{"test1"},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToInterfaceSliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToInterfaceSliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToInterfaceSliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToErrorSliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []error
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: nil,
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToErrorSliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToErrorSliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToErrorSliceE() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToDecimalSlice(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []decimal.Decimal
- }{
- {
- name: "",
- args: args{
- s: []string{
- "2.43",
- "17.89",
- },
- },
- want: []decimal.Decimal{
- decimal.NewFromFloat(2.43),
- decimal.NewFromFloat(17.89),
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := ToDecimalSlice(tt.args.s); !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToDecimalSlice() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToDecimalSliceE(t *testing.T) {
- type args struct {
- s []string
- }
- tests := []struct {
- name string
- args args
- want []decimal.Decimal
- wantErr bool
- }{
- {
- name: "",
- args: args{
- s: []string{
- "2.43",
- "17.89",
- },
- },
- want: []decimal.Decimal{
- decimal.NewFromFloat(2.43),
- decimal.NewFromFloat(17.89),
- },
- wantErr: false,
- },
- {
- name: "",
- args: args{
- s: []string{
- "2.43",
- "17d.89",
- },
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ToDecimalSliceE(tt.args.s)
- if (err != nil) != tt.wantErr {
- t.Errorf("ToDecimalSliceE() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != nil {
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ToDecimalSliceE() got = %v, want %v", got, tt.want)
- }
- }
- })
- }
-}
diff --git a/toolkit/common/utils.go b/toolkit/common/utils.go
deleted file mode 100644
index becbebb6..00000000
--- a/toolkit/common/utils.go
+++ /dev/null
@@ -1,76 +0,0 @@
-package common
-
-import (
- "os"
- "path/filepath"
- "strings"
- "text/template"
-
- "github.com/go-git/go-billy/v5/osfs"
- "github.com/go-git/go-git/v5"
- "github.com/go-git/go-git/v5/plumbing/cache"
- "github.com/go-git/go-git/v5/storage/filesystem"
- "github.com/pkg/errors"
- "github.com/sirupsen/logrus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/executils"
-)
-
-// InitGitRepo inits git repository.
-// Reinitialized existing Git repository is safe
-// https://stackoverflow.com/questions/5149694/does-running-git-init-twice-initialize-a-repository-or-reinitialize-an-existing
-func InitGitRepo(dir string) {
- fs := osfs.New(dir)
- dot, _ := fs.Chroot(".git")
- storage := filesystem.NewStorage(dot, cache.NewObjectLRUDefault())
-
- _, _ = git.Init(storage, fs)
-}
-
-const gitignoreTmpl = `# Binaries for programs and plugins
-*.exe
-*.exe~
-*.dll
-*.so
-*.dylib
-
-# Output of the go coverage tool, specifically when used with LiteIDE
-*.out
-
-# Dependency directories (remove the comment below to include it)
-# vendor/
-**/*.local
-.DS_Store
-.idea`
-
-// GitIgnore adds .gitignore file
-func GitIgnore(dir string) {
- var (
- gitignorefile string
- err error
- f *os.File
- tpl *template.Template
- )
- gitignorefile = filepath.Join(dir, ".gitignore")
- if _, err = os.Stat(gitignorefile); os.IsNotExist(err) {
- if f, err = os.Create(gitignorefile); err != nil {
- panic(err)
- }
- defer f.Close()
-
- tpl, _ = template.New(".gitignore.tmpl").Parse(gitignoreTmpl)
- _ = tpl.Execute(f, nil)
- } else {
- logrus.Warnf("file %s already exists", ".gitignore")
- }
-}
-
-func GetGoVersionNum(runner executils.Runner) (string, error) {
- out, err := runner.Output("go", "version")
- if err != nil {
- return "", errors.WithStack(err)
- }
- // go version go1.18.8 darwin/amd64
- result := strings.TrimPrefix(strings.Split(string(out), " ")[2], "go")
- // invalid go version '1.18.8': must match format 1.23
- return result[:strings.LastIndex(result, ".")], nil
-}
diff --git a/toolkit/concurrency/do.go b/toolkit/concurrency/do.go
deleted file mode 100644
index 20cc38f8..00000000
--- a/toolkit/concurrency/do.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2020 StackRox Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-package concurrency
-
-import (
- "time"
-)
-
-// DoWithTimeout performs the action as soon as the waitable is done.
-// It gives up and returns after timeout, and returns a bool indicating whether
-// the action was performed or not.
-func DoWithTimeout(w Waitable, action func(), timeout time.Duration) bool {
- if WaitWithTimeout(w, timeout) {
- action()
- return true
- }
- return false
-}
diff --git a/toolkit/concurrency/signal.go b/toolkit/concurrency/signal.go
deleted file mode 100644
index ec12f4b2..00000000
--- a/toolkit/concurrency/signal.go
+++ /dev/null
@@ -1,124 +0,0 @@
-// Copyright (c) 2020 StackRox Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-package concurrency
-
-import (
- "sync/atomic"
- "unsafe"
-)
-
-var (
- closedCh = func() chan struct{} {
- ch := make(chan struct{})
- close(ch)
- return ch
- }()
-)
-
-// Signal implements a signalling facility. Unlike sync.Cond, it is based on channels and can hence be used
-// in `select` statements.
-// There are two ways to instantiate a Signal. The preferred way is by calling `NewSignal()`, which will return a signal
-// that is not triggered. Alternatively, the zero-value can be used to instantiate a signal in triggered condition,
-// which is not what you usually want. To reset it to the non-triggered state, call `Reset()`.
-// Similarly to `sync.(RW)Mutex` and `sync.Cond`, a signal should not be copied once used.
-type Signal struct {
- ch unsafe.Pointer // ch is a pointer to the signal channel, or `nil` if the signal is in the triggered state.
-}
-
-// NewSignal creates a new signal that is in the reset state.
-func NewSignal() Signal {
- var s Signal
- s.Reset()
- return s
-}
-
-// WaitC returns a WaitableChan for this signal.
-func (s *Signal) WaitC() WaitableChan {
- chPtr := atomic.LoadPointer(&s.ch)
- if chPtr == nil {
- return closedCh
- }
-
- ch := (*chan struct{})(chPtr)
- return *ch
-}
-
-// Done returns a channel that is closed when this signal was triggered.
-func (s *Signal) Done() <-chan struct{} {
- return s.WaitC()
-}
-
-// IsDone checks if the signal was triggered. It is a slightly more efficient alternative to calling `IsDone(s)`.
-func (s *Signal) IsDone() bool {
- chPtr := atomic.LoadPointer(&s.ch)
- if chPtr == nil {
- return true
- }
- ch := (*chan struct{})(chPtr)
- return IsDone(WaitableChan(*ch))
-}
-
-// Wait waits for the signal to be triggered. It is a slightly more efficient and convenient alternative to calling
-// `Wait(s)`.
-func (s *Signal) Wait() {
- chPtr := atomic.LoadPointer(&s.ch)
- if chPtr == nil {
- return
- }
- ch := (*chan struct{})(chPtr)
- Wait(WaitableChan(*ch))
-}
-
-// Reset resets the signal to the non-triggered state, if necessary. The return value indicates whether a reset was
-// actually performed (i.e., the signal was triggered). It returns false if the signal was not in the triggered state.
-func (s *Signal) Reset() bool {
- ch := make(chan struct{})
- return atomic.CompareAndSwapPointer(&s.ch, nil, unsafe.Pointer(&ch))
-}
-
-// Signal triggers the signal. The return value indicates whether the signal was actually triggered. It returns false
-// if the signal was already in the triggered state.
-func (s *Signal) Signal() bool {
- chPtr := atomic.SwapPointer(&s.ch, nil)
- if chPtr == nil {
- return false
- }
-
- ch := (*chan struct{})(chPtr)
- close(*ch)
- return true
-}
-
-// Snapshot returns a WaitableChan that observers will only see triggering once, i.e., if this signal is triggered (or
-// has been triggered) and then `Reset()` is called, subsequent calls to `Done()` on the returned object will still see
-// a triggered channel.
-func (s *Signal) Snapshot() WaitableChan {
- return s.Done()
-}
-
-// SignalWhen triggers this signal when the given trigger condition is satisfied. It returns as soon as either this
-// signal is triggered (either by this function or another goroutine), or cancelCond is triggered (in which case the
-// signal will not be triggered).
-// CAREFUL: This function blocks; if you do not want this, invoke it in a goroutine.
-func (s *Signal) SignalWhen(triggerCond Waitable, cancelCond Waitable) bool {
- select {
- case <-triggerCond.Done():
- return s.Signal()
- case <-cancelCond.Done():
- return false
- case <-s.Done():
- return false
- }
-}
diff --git a/toolkit/concurrency/signal_test.go b/toolkit/concurrency/signal_test.go
deleted file mode 100644
index eba56836..00000000
--- a/toolkit/concurrency/signal_test.go
+++ /dev/null
@@ -1,95 +0,0 @@
-// Copyright (c) 2020 StackRox Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-package concurrency
-
-import (
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestNewSignalIsNotDone(t *testing.T) {
- t.Parallel()
- a := assert.New(t)
-
- s := NewSignal()
- a.False(s.IsDone(), "signal should not be triggered")
-}
-
-func TestNewSignalResetHasNoEffect(t *testing.T) {
- t.Parallel()
- a := assert.New(t)
-
- s := NewSignal()
- wc := s.WaitC()
- a.False(s.Reset(), "Reset on a new signal should return false")
- a.False(s.IsDone(), "signal should not be triggered")
- a.Equal(wc, s.WaitC(), "the channel should not change when reset has no effect")
-}
-
-func TestSignalTrigger(t *testing.T) {
- t.Parallel()
- a := assert.New(t)
-
- s := NewSignal()
- a.False(s.IsDone(), "signal should not be triggered")
- wc := s.WaitC()
-
- a.True(s.Signal(), "calling signal should return true")
- a.True(s.IsDone(), "signal should be triggered")
- a.True(IsDone(wc), "the old wait channel should be closed")
-
- // Test that Signal() can be called repeatedly
- a.False(s.Signal(), "calling signal the second time should return false")
- a.True(s.IsDone(), "signal should be triggered")
-}
-
-func TestSignalTriggerAndReset(t *testing.T) {
- t.Parallel()
- a := assert.New(t)
-
- s := NewSignal()
- wc := s.WaitC()
- a.True(s.Signal(), "calling signal should return true")
- a.True(s.IsDone(), "signal should be triggered")
- a.True(IsDone(wc), "old wait channel should be closed")
-
- a.True(s.Reset(), "calling Reset on a triggered signal should return true")
- a.False(s.IsDone(), "signal should not be triggered after reset")
- a.True(IsDone(wc), "old wait channel should still be closed")
-
- a.False(s.Reset(), "calling reset a second time should return false")
-}
-
-func TestSignalDoWithTimeout(t *testing.T) {
- t.Parallel()
- a := assert.New(t)
-
- var done bool
- action := func() {
- done = true
- }
-
- s := NewSignal()
- a.False(DoWithTimeout(&s, action, 100*time.Millisecond))
- a.False(done)
- go func() {
- time.Sleep(10 * time.Millisecond)
- s.Signal()
- }()
- a.True(DoWithTimeout(&s, action, 500*time.Millisecond))
- a.True(done)
-}
diff --git a/toolkit/concurrency/types.go b/toolkit/concurrency/types.go
deleted file mode 100644
index 773772d9..00000000
--- a/toolkit/concurrency/types.go
+++ /dev/null
@@ -1,30 +0,0 @@
-// Copyright (c) 2020 StackRox Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-package concurrency
-
-// Waitable is a generic interface for things that can be waited upon. The method `Done` returns a channel that, when
-// closed, signals that whatever condition is represented by this waitable is satisfied.
-// Note: The name `Done` was chosen such that `context.Context` conforms to this interface.
-type Waitable interface {
- Done() <-chan struct{}
-}
-
-// WaitableChan is an alias around a `<-chan struct{}` that returns itself in its `Done` method.
-type WaitableChan <-chan struct{}
-
-// Done returns the channel itself.
-func (c WaitableChan) Done() <-chan struct{} {
- return c
-}
diff --git a/toolkit/concurrency/wait.go b/toolkit/concurrency/wait.go
deleted file mode 100644
index ac5024b6..00000000
--- a/toolkit/concurrency/wait.go
+++ /dev/null
@@ -1,51 +0,0 @@
-// Copyright (c) 2020 StackRox Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-package concurrency
-
-import "time"
-
-// Wait waits indefinitely until the condition represented by the given Waitable is fulfilled.
-func Wait(w Waitable) {
- <-w.Done()
-}
-
-// IsDone checks if the given waitable's condition is fulfilled.
-func IsDone(w Waitable) bool {
- select {
- case <-w.Done():
- return true
- default:
- return false
- }
-}
-
-// WaitWithTimeout waits for the given Waitable with a specified timeout. It returns false if the timeout expired
-// before the condition was fulfilled, true otherwise.
-func WaitWithTimeout(w Waitable, timeout time.Duration) bool {
- if timeout <= 0 {
- return IsDone(w)
- }
-
- t := time.NewTimer(timeout)
- select {
- case <-w.Done():
- if !t.Stop() {
- <-t.C
- }
- return true
- case <-t.C:
- return false
- }
-}
diff --git a/toolkit/constants/constants.go b/toolkit/constants/constants.go
deleted file mode 100644
index 74871d9f..00000000
--- a/toolkit/constants/constants.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package constants
-
-// FORMAT is a pattern for formatting time
-const FORMAT = "2006-01-02 15:04:05"
-
-// FORMAT2 is a pattern for formatting time
-const FORMAT2 = "2006-01-02"
-
-// FORMAT3 is a pattern for formatting time
-const FORMAT3 = "2006/01/02"
-
-// FORMATDOT is a pattern for formatting time
-const FORMATDOT = "2006.01.02"
-
-// FORMATENANO is a pattern for formatting time
-const FORMATENANO = "2006-01-02T15:04:05.999Z"
-
-// FORMATES is a pattern for formatting time
-const FORMATES = "2006-01-02T15:04:05Z"
-
-// FORMAT4 is a pattern for formatting time
-const FORMAT4 = "2006-01-02 15:04"
-
-// FORMAT5 is a pattern for formatting time
-const FORMAT5 = "2006年1月2日"
-
-// FORMAT7 is a pattern for formatting time
-const FORMAT7 = "2006年01月02日"
-
-// FORMAT6 is a pattern for formatting time
-const FORMAT6 = "2006年1月"
-
-// FORMAT8 is a pattern for formatting time
-const FORMAT8 = "2006-01-02T15:04:05-0700" // 2020-07-12T15:31:50+0800
-// FORMAT9 is a pattern for formatting time
-const FORMAT9 = "2006年01月02日15时04分" // "2019年1月04日09时04分"
-// FORMAT10 is a pattern for formatting time
-const FORMAT10 = "20060102"
-
-// FORMAT11 is a pattern for formatting time
-const FORMAT11 = "20060102150405"
-
-// FORMAT12 is a pattern for formatting time
-const FORMAT12 = "2006/1/2"
-
-// FORMAT13 is a pattern for formatting time
-const FORMAT13 = "06-01-02"
-
-// FORMAT14 is a pattern for formatting time
-const FORMAT14 = "01-02-06"
-
-// FORMAT15 is a pattern for formatting time
-const FORMAT15 = "Mon Jan 2 15:04:05 MST 2006"
diff --git a/toolkit/constants/constants_darwin.go b/toolkit/constants/constants_darwin.go
deleted file mode 100644
index feef4eac..00000000
--- a/toolkit/constants/constants_darwin.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package constants
-
-const LineBreak = "\n"
diff --git a/toolkit/constants/constants_linux.go b/toolkit/constants/constants_linux.go
deleted file mode 100644
index feef4eac..00000000
--- a/toolkit/constants/constants_linux.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package constants
-
-const LineBreak = "\n"
diff --git a/toolkit/constants/constants_windows.go b/toolkit/constants/constants_windows.go
deleted file mode 100644
index f9a75574..00000000
--- a/toolkit/constants/constants_windows.go
+++ /dev/null
@@ -1,3 +0,0 @@
-package constants
-
-const LineBreak = "\r\n"
diff --git a/toolkit/copier/copier.go b/toolkit/copier/copier.go
deleted file mode 100644
index 664f1dbb..00000000
--- a/toolkit/copier/copier.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package copier
-
-import (
- "github.com/bytedance/sonic"
- "github.com/bytedance/sonic/decoder"
- "github.com/pkg/errors"
- "reflect"
-)
-
-var json = sonic.ConfigDefault
-
-// DeepCopy src to target with json marshal and unmarshal
-func DeepCopy(src, target interface{}) error {
- if src == nil || target == nil {
- return nil
- }
- if reflect.ValueOf(target).Kind() != reflect.Ptr {
- return errors.New("Target should be a pointer")
- }
- b, err := json.MarshalToString(src)
- if err != nil {
- return errors.WithStack(err)
- }
- dec := decoder.NewDecoder(b)
- dec.UseInt64()
- if err = dec.Decode(target); err != nil {
- return errors.WithStack(err)
- }
- return nil
-}
diff --git a/toolkit/copier/copier_test.go b/toolkit/copier/copier_test.go
deleted file mode 100644
index d1029504..00000000
--- a/toolkit/copier/copier_test.go
+++ /dev/null
@@ -1,143 +0,0 @@
-package copier
-
-import (
- "fmt"
- "testing"
-)
-
-type Family struct {
- Father string
- Mather string
- Pets map[string]string
- Toys []string
-}
-
-type TestStruct struct {
- Name string
- Age int
- Family Family
-}
-
-type FamilyShadow struct {
- Father string
- Mather string
- Pets map[string]string
-}
-
-type TestStructShadow struct {
- Name string
- Family FamilyShadow
-}
-
-func TestDeepCopy(t *testing.T) {
- pets := make(map[string]string)
- pets["a"] = "dog"
- pets["b"] = "cat"
-
- family := Family{
- Father: "Jack",
- Mather: "Lily",
- Pets: pets,
- Toys: []string{
- "car",
- "lego",
- },
- }
- src := TestStruct{
- Name: "Rose",
- Age: 18,
- Family: family,
- }
-
- var target TestStructShadow
-
- type args struct {
- src interface{}
- target interface{}
- }
- tests := []struct {
- name string
- args args
- wantErr bool
- }{
- {
- args: args{
- src: src,
- target: &target,
- },
- wantErr: false,
- },
- {
- args: args{
- src: nil,
- target: nil,
- },
- wantErr: false,
- },
- {
- name: "",
- args: args{
- src: make(chan string),
- target: &target,
- },
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if err := DeepCopy(tt.args.src, tt.args.target); (err != nil) != tt.wantErr {
- t.Errorf("DeepCopy() error = %v, wantErr %v", err, tt.wantErr)
- }
- fmt.Printf("%#v\n", tt.args.target)
- })
- }
-}
-
-func TestDeepCopy_ShouldHasError(t *testing.T) {
- pets := make(map[string]string)
- pets["a"] = "dog"
- pets["b"] = "cat"
-
- family := Family{
- Father: "Jack",
- Mather: "Lily",
- Pets: pets,
- Toys: []string{
- "car",
- "lego",
- },
- }
- src := TestStruct{
- Name: "Rose",
- Age: 18,
- Family: family,
- }
-
- var target TestStructShadow
-
- type args struct {
- src interface{}
- target interface{}
- }
- tests := []struct {
- name string
- args args
- wantErr bool
- }{
- {
- name: "TestDeepCopy",
- args: args{
- src: src,
- target: target,
- },
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if err := DeepCopy(tt.args.src, tt.args.target); (err != nil) != tt.wantErr {
- t.Errorf("DeepCopy() error = %v, wantErr %v", err, tt.wantErr)
- }
- })
- }
-}
diff --git a/toolkit/customtypes/time.go b/toolkit/customtypes/time.go
deleted file mode 100644
index c3789a15..00000000
--- a/toolkit/customtypes/time.go
+++ /dev/null
@@ -1,46 +0,0 @@
-package customtypes
-
-import (
- "github.com/goccy/go-reflect"
- "time"
-)
-
-type Time time.Time
-
-var TimeLayout = "2006-01-02 15:04:05"
-
-func (t *Time) UnmarshalJSON(data []byte) (err error) {
- if data == nil || len(data) == 0 {
- *t = Time(time.Time{})
- return nil
- }
- if data[0] == '"' && data[len(data)-1] == '"' {
- data = data[1 : len(data)-1]
- }
- if data == nil || len(data) == 0 {
- *t = Time(time.Time{})
- return nil
- }
- if string(data) == "null" {
- *t = Time(time.Time{})
- return nil
- }
- now, err := time.ParseInLocation(TimeLayout, string(data), time.Local)
- *t = Time(now)
- return
-}
-
-func (t Time) MarshalJSON() ([]byte, error) {
- if reflect.ValueOf(t).IsZero() {
- return []byte("null"), nil
- }
- b := make([]byte, 0, len(TimeLayout)+2)
- b = append(b, '"')
- b = time.Time(t).AppendFormat(b, TimeLayout)
- b = append(b, '"')
- return b, nil
-}
-
-func (t Time) String() string {
- return time.Time(t).Format(TimeLayout)
-}
diff --git a/toolkit/customtypes/time_test.go b/toolkit/customtypes/time_test.go
deleted file mode 100644
index dba417c2..00000000
--- a/toolkit/customtypes/time_test.go
+++ /dev/null
@@ -1,35 +0,0 @@
-package customtypes
-
-import (
- "fmt"
- "testing"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
-)
-
-type User struct {
- CreatedAt Time `json:"created_at"`
-}
-
-type Author struct {
- CreatedAt Time `json:"created_at"`
-}
-
-func TestTime_UnmarshalJSON(t *testing.T) {
- u := User{}
- fmt.Println(u.CreatedAt)
- a := Author{}
- copier.DeepCopy(&u, &a)
- fmt.Println(a.CreatedAt)
-}
-
-func TestTime_UnmarshalJSON1(t *testing.T) {
- u := User{
- CreatedAt: Time(time.Now()),
- }
- fmt.Println(u.CreatedAt)
- a := Author{}
- copier.DeepCopy(&u, &a)
- fmt.Println(a.CreatedAt)
-}
diff --git a/toolkit/dotenv/dotenv.go b/toolkit/dotenv/dotenv.go
deleted file mode 100644
index dd829ec0..00000000
--- a/toolkit/dotenv/dotenv.go
+++ /dev/null
@@ -1,32 +0,0 @@
-package dotenv
-
-import (
- "io"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/joho/godotenv"
-)
-
-func Load(env string) {
- wd, _ := os.Getwd()
- _ = godotenv.Load(filepath.Join(wd, ".env."+env+".local"))
- if "test" != env {
- _ = godotenv.Load(filepath.Join(wd, ".env.local"))
- }
- _ = godotenv.Load(filepath.Join(wd, ".env."+env))
- _ = godotenv.Load() // The Original .env
-}
-
-func LoadAsMap(reader io.Reader) (map[string]interface{}, error) {
- envMap, err := godotenv.Parse(reader)
- if err != nil {
- return nil, err
- }
- result := make(map[string]interface{})
- for key, value := range envMap {
- result[strings.ToLower(strings.ReplaceAll(key, "_", "."))] = value
- }
- return result, nil
-}
diff --git a/toolkit/dotenv/dotenv_test.go b/toolkit/dotenv/dotenv_test.go
deleted file mode 100644
index 7dbd8b2d..00000000
--- a/toolkit/dotenv/dotenv_test.go
+++ /dev/null
@@ -1,44 +0,0 @@
-package dotenv_test
-
-import (
- "io"
- "os"
- "testing"
-
- "github.com/pkg/errors"
- "github.com/stretchr/testify/require"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/dotenv"
-)
-
-func TestLoadAsMap(t *testing.T) {
- _ = os.Chdir("testdata")
- f, _ := os.Open(".env")
- result, err := dotenv.LoadAsMap(f)
- require.NoError(t, err)
- require.Equal(t, "6060", result["gdd.port"])
- require.Equal(t, "/api", result["gdd.route.root.path"])
-}
-
-func ErrReader(err error) io.Reader {
- return &errReader{err: err}
-}
-
-type errReader struct {
- err error
-}
-
-func (r *errReader) Read(p []byte) (int, error) {
- return 0, r.err
-}
-
-func TestLoadAsMapError(t *testing.T) {
- _, err := dotenv.LoadAsMap(ErrReader(errors.New("test error")))
- require.Error(t, err)
-}
-
-func TestLoad(t *testing.T) {
- _ = os.Chdir("testdata")
- dotenv.Load("")
- require.Equal(t, "6060", os.Getenv("GDD_PORT"))
- require.Equal(t, "/api", os.Getenv("GDD_ROUTE_ROOT_PATH"))
-}
diff --git a/toolkit/dotenv/testdata/.env b/toolkit/dotenv/testdata/.env
deleted file mode 100644
index 891c52be..00000000
--- a/toolkit/dotenv/testdata/.env
+++ /dev/null
@@ -1,2 +0,0 @@
-GDD_PORT=6060
-GDD_ROUTE_ROOT_PATH=/api
\ No newline at end of file
diff --git a/toolkit/envconfig/LICENSE b/toolkit/envconfig/LICENSE
deleted file mode 100644
index 4bfa7a84..00000000
--- a/toolkit/envconfig/LICENSE
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 2013 Kelsey Hightower
-
-Permission is hereby granted, free of charge, to any person obtaining a copy of
-this software and associated documentation files (the "Software"), to deal in
-the Software without restriction, including without limitation the rights to
-use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
-of the Software, and to permit persons to whom the Software is furnished to do
-so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/toolkit/envconfig/README.md b/toolkit/envconfig/README.md
deleted file mode 100644
index 3446d2fe..00000000
--- a/toolkit/envconfig/README.md
+++ /dev/null
@@ -1,231 +0,0 @@
-# envconfig
-
-[![Build Status](https://travis-ci.org/kelseyhightower/envconfig.svg)](https://travis-ci.org/kelseyhightower/envconfig)
-
-```Go
-import "github.com/kelseyhightower/envconfig"
-```
-
-## Documentation
-
-See [godoc](http://godoc.org/github.com/kelseyhightower/envconfig)
-
-## Usage
-
-Set some environment variables:
-
-```Bash
-export MYAPP_DEBUG=false
-export MYAPP_PORT=8080
-export MYAPP_USER=Kelsey
-export MYAPP_RATE="0.5"
-export MYAPP_TIMEOUT="3m"
-export MYAPP_USERS="rob,ken,robert"
-export MYAPP_COLORCODES="red:1,green:2,blue:3"
-```
-
-Write some code:
-
-```Go
-package main
-
-import (
- "fmt"
- "log"
- "time"
-
- "github.com/kelseyhightower/envconfig"
-)
-
-type Specification struct {
- Debug bool
- Port int
- User string
- Users []string
- Rate float32
- Timeout time.Duration
- ColorCodes map[string]int
-}
-
-func main() {
- var s Specification
- err := envconfig.Process("myapp", &s)
- if err != nil {
- log.Fatal(err.Error())
- }
- format := "Debug: %v\nPort: %d\nUser: %s\nRate: %f\nTimeout: %s\n"
- _, err = fmt.Printf(format, s.Debug, s.Port, s.User, s.Rate, s.Timeout)
- if err != nil {
- log.Fatal(err.Error())
- }
-
- fmt.Println("Users:")
- for _, u := range s.Users {
- fmt.Printf(" %s\n", u)
- }
-
- fmt.Println("Color codes:")
- for k, v := range s.ColorCodes {
- fmt.Printf(" %s: %d\n", k, v)
- }
-}
-```
-
-Results:
-
-```Bash
-Debug: false
-Port: 8080
-User: Kelsey
-Rate: 0.500000
-Timeout: 3m0s
-Users:
- rob
- ken
- robert
-Color codes:
- red: 1
- green: 2
- blue: 3
-```
-
-## Struct Tag Support
-
-Envconfig supports the use of struct tags to specify alternate, default, and required
-environment variables.
-
-For example, consider the following struct:
-
-```Go
-type Specification struct {
- ManualOverride1 string `envconfig:"manual_override_1"`
- DefaultVar string `default:"foobar"`
- RequiredVar string `required:"true"`
- IgnoredVar string `ignored:"true"`
- AutoSplitVar string `split_words:"true"`
- RequiredAndAutoSplitVar string `required:"true" split_words:"true"`
-}
-```
-
-Envconfig has automatic support for CamelCased struct elements when the
-`split_words:"true"` tag is supplied. Without this tag, `AutoSplitVar` above
-would look for an environment variable called `MYAPP_AUTOSPLITVAR`. With the
-setting applied it will look for `MYAPP_AUTO_SPLIT_VAR`. Note that numbers
-will get globbed into the previous word. If the setting does not do the
-right thing, you may use a manual override.
-
-Envconfig will process value for `ManualOverride1` by populating it with the
-value for `MYAPP_MANUAL_OVERRIDE_1`. Without this struct tag, it would have
-instead looked up `MYAPP_MANUALOVERRIDE1`. With the `split_words:"true"` tag
-it would have looked up `MYAPP_MANUAL_OVERRIDE1`.
-
-```Bash
-export MYAPP_MANUAL_OVERRIDE_1="this will be the value"
-
-# export MYAPP_MANUALOVERRIDE1="and this will not"
-```
-
-If envconfig can't find an environment variable value for `MYAPP_DEFAULTVAR`,
-it will populate it with "foobar" as a default value.
-
-If envconfig can't find an environment variable value for `MYAPP_REQUIREDVAR`,
-it will return an error when asked to process the struct. If
-`MYAPP_REQUIREDVAR` is present but empty, envconfig will not return an error.
-
-If envconfig can't find an environment variable in the form `PREFIX_MYVAR`, and there
-is a struct tag defined, it will try to populate your variable with an environment
-variable that directly matches the envconfig tag in your struct definition:
-
-```shell
-export SERVICE_HOST=127.0.0.1
-export MYAPP_DEBUG=true
-```
-```Go
-type Specification struct {
- ServiceHost string `envconfig:"SERVICE_HOST"`
- Debug bool
-}
-```
-
-Envconfig won't process a field with the "ignored" tag set to "true", even if a corresponding
-environment variable is set.
-
-## Supported Struct Field Types
-
-envconfig supports these struct field types:
-
- * string
- * int8, int16, int32, int64
- * bool
- * float32, float64
- * slices of any supported type
- * maps (keys and values of any supported type)
- * [encoding.TextUnmarshaler](https://golang.org/pkg/encoding/#TextUnmarshaler)
- * [encoding.BinaryUnmarshaler](https://golang.org/pkg/encoding/#BinaryUnmarshaler)
- * [time.Duration](https://golang.org/pkg/time/#Duration)
-
-Embedded structs using these fields are also supported.
-
-## Custom Decoders
-
-Any field whose type (or pointer-to-type) implements `envconfig.Decoder` can
-control its own deserialization:
-
-```Bash
-export DNS_SERVER=8.8.8.8
-```
-
-```Go
-type IPDecoder net.IP
-
-func (ipd *IPDecoder) Decode(value string) error {
- *ipd = IPDecoder(net.ParseIP(value))
- return nil
-}
-
-type DNSConfig struct {
- Address IPDecoder `envconfig:"DNS_SERVER"`
-}
-```
-
-Example for decoding the environment variables into map[string][]structName type
-
-```Bash
-export SMS_PROVIDER_WITH_WEIGHT= `IND=[{"name":"SMSProvider1","weight":70},{"name":"SMSProvider2","weight":30}];US=[{"name":"SMSProvider1","weight":100}]`
-```
-
-```GO
-type providerDetails struct {
- Name string
- Weight int
-}
-
-type SMSProviderDecoder map[string][]providerDetails
-
-func (sd *SMSProviderDecoder) Decode(value string) error {
- smsProvider := map[string][]providerDetails{}
- pairs := strings.Split(value, ";")
- for _, pair := range pairs {
- providerdata := []providerDetails{}
- kvpair := strings.Split(pair, "=")
- if len(kvpair) != 2 {
- return fmt.Errorf("invalid map item: %q", pair)
- }
- err := json.Unmarshal([]byte(kvpair[1]), &providerdata)
- if err != nil {
- return fmt.Errorf("invalid map json: %w", err)
- }
- smsProvider[kvpair[0]] = providerdata
-
- }
- *sd = SMSProviderDecoder(smsProvider)
- return nil
-}
-
-type SMSProviderConfig struct {
- ProviderWithWeight SMSProviderDecoder `envconfig:"SMS_PROVIDER_WITH_WEIGHT"`
-}
-```
-
-Also, envconfig will use a `Set(string) error` method like from the
-[flag.Value](https://godoc.org/flag#Value) interface if implemented.
diff --git a/toolkit/envconfig/doc.go b/toolkit/envconfig/doc.go
deleted file mode 100644
index f28561cd..00000000
--- a/toolkit/envconfig/doc.go
+++ /dev/null
@@ -1,8 +0,0 @@
-// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
-// Use of this source code is governed by the MIT License that can be found in
-// the LICENSE file.
-
-// Package envconfig implements decoding of environment variables based on a user
-// defined specification. A typical use is using environment variables for
-// configuration settings.
-package envconfig
diff --git a/toolkit/envconfig/env_os.go b/toolkit/envconfig/env_os.go
deleted file mode 100644
index 6b85e3f5..00000000
--- a/toolkit/envconfig/env_os.go
+++ /dev/null
@@ -1,8 +0,0 @@
-//go:build appengine || go1.5
-// +build appengine go1.5
-
-package envconfig
-
-import "os"
-
-var lookupEnv = os.LookupEnv
diff --git a/toolkit/envconfig/env_syscall.go b/toolkit/envconfig/env_syscall.go
deleted file mode 100644
index 221ff7fa..00000000
--- a/toolkit/envconfig/env_syscall.go
+++ /dev/null
@@ -1,8 +0,0 @@
-//go:build !appengine && !go1.5
-// +build !appengine,!go1.5
-
-package envconfig
-
-import "syscall"
-
-var lookupEnv = syscall.Getenv
diff --git a/toolkit/envconfig/envconfig.go b/toolkit/envconfig/envconfig.go
deleted file mode 100644
index a1580f55..00000000
--- a/toolkit/envconfig/envconfig.go
+++ /dev/null
@@ -1,592 +0,0 @@
-// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
-// Use of this source code is governed by the MIT License that can be found in
-// the LICENSE file.
-
-package envconfig
-
-import (
- "encoding"
- "fmt"
- "math"
- "os"
- "reflect"
- "regexp"
- "strconv"
- "strings"
- "time"
-
- "github.com/pkg/errors"
-)
-
-// ErrInvalidSpecification indicates that a specification is of the wrong type.
-var ErrInvalidSpecification = errors.New("specification must be a struct pointer")
-
-var gatherRegexp = regexp.MustCompile("([^A-Z]+|[A-Z]+[^A-Z]+|[A-Z]+)")
-var acronymRegexp = regexp.MustCompile("([A-Z]+)([A-Z][^A-Z]+)")
-
-// A ParseError occurs when an environment variable cannot be converted to
-// the type required by a struct field during assignment.
-type ParseError struct {
- KeyName string
- FieldName string
- TypeName string
- Value string
- Err error
-}
-
-// Decoder has the same semantics as Setter, but takes higher precedence.
-// It is provided for historical compatibility.
-type Decoder interface {
- Decode(value string) error
-}
-
-// Setter is implemented by types can self-deserialize values.
-// Any type that implements flag.Value also implements Setter.
-type Setter interface {
- Set(value string) error
-}
-
-func (e *ParseError) Error() string {
- return fmt.Sprintf("envconfig.Process: assigning %[1]s to %[2]s: converting '%[3]s' to type %[4]s. details: %+v", e.KeyName, e.FieldName, e.Value, e.TypeName, e.Err)
-}
-
-type SeparatorType string
-
-const (
- INDEX SeparatorType = "index"
- COMMA SeparatorType = "comma"
-)
-
-// varInfo maintains information about the configuration variable
-type varInfo struct {
- Name string
- Alt string
- Key string
- Field reflect.Value
- Tags reflect.StructTag
- ValuesBy SeparatorType
-}
-
-// GatherInfo gathers information about the specified struct
-func gatherInfo(prefix string, spec interface{}) ([]varInfo, error) {
- s := reflect.ValueOf(spec)
-
- if s.Kind() != reflect.Ptr {
- return nil, ErrInvalidSpecification
- }
- s = s.Elem()
- if s.Kind() != reflect.Struct {
- return nil, ErrInvalidSpecification
- }
- typeOfSpec := s.Type()
-
- // over allocate an info array, we will extend if needed later
- infos := make([]varInfo, 0, s.NumField())
- for i := 0; i < s.NumField(); i++ {
- f := s.Field(i)
- ftype := typeOfSpec.Field(i)
- if !f.CanSet() || isTrue(ftype.Tag.Get("ignored")) {
- continue
- }
-
- for f.Kind() == reflect.Ptr {
- if f.IsNil() {
- if f.Type().Elem().Kind() != reflect.Struct {
- // nil pointer to a non-struct: leave it alone
- break
- }
- // nil pointer to struct: create a zero instance
- f.Set(reflect.New(f.Type().Elem()))
- }
- f = f.Elem()
- }
-
- // Capture information about the config variable
- info := varInfo{
- Name: ftype.Name,
- Field: f,
- Tags: ftype.Tag,
- Alt: strings.ToUpper(ftype.Tag.Get("envconfig")),
- }
-
- // Default to the field name as the env var name (will be upcased)
- info.Key = info.Name
-
- // Best effort to un-pick camel casing as separate words
- if isTrue(ftype.Tag.Get("split_words")) {
- words := gatherRegexp.FindAllStringSubmatch(ftype.Name, -1)
- if len(words) > 0 {
- var name []string
- for _, words := range words {
- if m := acronymRegexp.FindStringSubmatch(words[0]); len(m) == 3 {
- name = append(name, m[1], m[2])
- } else {
- name = append(name, words[0])
- }
- }
-
- info.Key = strings.Join(name, "_")
- }
- }
- if info.Alt != "" {
- info.Key = info.Alt
- }
- if prefix != "" {
- info.Key = fmt.Sprintf("%s_%s", prefix, info.Key)
- }
- info.Key = strings.ToUpper(info.Key)
- info.ValuesBy = SeparatorType(ftype.Tag.Get("values_by"))
-
- infos = append(infos, info)
-
- if f.Kind() == reflect.Struct {
- // honor Decode if present
- if decoderFrom(f) == nil && setterFrom(f) == nil && textUnmarshaler(f) == nil && binaryUnmarshaler(f) == nil {
- innerPrefix := prefix
- if !ftype.Anonymous {
- innerPrefix = info.Key
- }
-
- embeddedPtr := f.Addr().Interface()
- embeddedInfos, err := gatherInfo(innerPrefix, embeddedPtr)
- if err != nil {
- return nil, err
- }
- infos = append(infos[:len(infos)-1], embeddedInfos...)
-
- continue
- }
- }
- }
- return infos, nil
-}
-
-// CheckDisallowed checks that no environment variables with the prefix are set
-// that we don't know how or want to parse. This is likely only meaningful with
-// a non-empty prefix.
-func CheckDisallowed(prefix string, spec interface{}) error {
- infos, err := gatherInfo(prefix, spec)
- if err != nil {
- return err
- }
-
- vars := make(map[string]struct{})
- for _, info := range infos {
- vars[info.Key] = struct{}{}
- }
-
- if prefix != "" {
- prefix = strings.ToUpper(prefix) + "_"
- }
-
- for _, env := range os.Environ() {
- if !strings.HasPrefix(env, prefix) {
- continue
- }
- v := strings.SplitN(env, "=", 2)[0]
- if _, found := vars[v]; !found {
- return fmt.Errorf("unknown environment variable %s", v)
- }
- }
-
- return nil
-}
-
-// Process populates the specified struct based on environment variables
-func Process(prefix string, spec interface{}) error {
- infos, err := gatherInfo(prefix, spec)
-
- for _, info := range infos {
-
- if info.ValuesBy == INDEX {
- index := 0
- end := false
- for !end {
- key := info.Key + "_" + strconv.Itoa(index)
- if end, err = doProcess(key, info); err != nil {
- return errors.WithStack(err)
- }
- index++
- }
- if info.Field.IsNil() || info.Field.IsZero() {
- if err = checkRequired(info); err != nil {
- return errors.WithStack(err)
- }
- def := info.Tags.Get("default")
- if def != "" {
- err = processFieldWithConfig(def, info.Field, ProcessConfig{
- ValuesBy: info.ValuesBy,
- })
- }
- }
- continue
- }
-
- if _, err = doProcess(info.Key, info); err != nil {
- return errors.WithStack(err)
- }
- }
-
- return err
-}
-
-func checkRequired(info varInfo) error {
- def := info.Tags.Get("default")
- req := info.Tags.Get("required")
- if def == "" {
- if isTrue(req) {
- key := info.Key
- if info.Alt != "" {
- key = info.Alt
- }
- return fmt.Errorf("required key %s missing value", key)
- }
- }
- return nil
-}
-
-func doProcess(key string, info varInfo) (end bool, err error) {
- // `os.Getenv` cannot differentiate between an explicitly set empty value
- // and an unset value. `os.LookupEnv` is preferred to `syscall.Getenv`,
- // but it is only available in go1.5 or newer. We're using Go build tags
- // here to use os.LookupEnv for >=go1.5
- value, ok := lookupEnv(key)
-
- if info.ValuesBy == INDEX && !ok {
- return true, nil
- }
-
- if !ok && info.Alt != "" {
- value, ok = lookupEnv(info.Alt)
- }
-
- if info.ValuesBy != INDEX {
- if !ok {
- if err = checkRequired(info); err != nil {
- return false, errors.WithStack(err)
- }
- def := info.Tags.Get("default")
- if def == "" {
- return false, nil
- }
- value = def
- }
- }
-
- err = processFieldWithConfig(value, info.Field, ProcessConfig{
- ValuesBy: info.ValuesBy,
- })
- if err != nil {
- return false, &ParseError{
- KeyName: info.Key,
- FieldName: info.Name,
- TypeName: info.Field.Type().String(),
- Value: value,
- Err: errors.WithStack(err),
- }
- }
-
- return false, nil
-}
-
-// MustProcess is the same as Process but panics if an error occurs
-func MustProcess(prefix string, spec interface{}) {
- if err := Process(prefix, spec); err != nil {
- panic(err)
- }
-}
-
-func processField(value string, field reflect.Value) error {
- typ := field.Type()
-
- decoder := decoderFrom(field)
- if decoder != nil {
- return decoder.Decode(value)
- }
- // look for Set method if Decode not defined
- setter := setterFrom(field)
- if setter != nil {
- return setter.Set(value)
- }
-
- if t := textUnmarshaler(field); t != nil {
- return t.UnmarshalText([]byte(value))
- }
-
- if b := binaryUnmarshaler(field); b != nil {
- return b.UnmarshalBinary([]byte(value))
- }
-
- if typ.Kind() == reflect.Ptr {
- typ = typ.Elem()
- if field.IsNil() {
- field.Set(reflect.New(typ))
- }
- field = field.Elem()
- }
-
- switch typ.Kind() {
- case reflect.String:
- field.SetString(value)
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- var (
- val int64
- err error
- )
- if field.Kind() == reflect.Int64 && typ.PkgPath() == "time" && typ.Name() == "Duration" {
- var d time.Duration
- d, err = time.ParseDuration(value)
- val = int64(d)
- } else {
- val, err = strconv.ParseInt(value, 0, typ.Bits())
- }
- if err != nil {
- return err
- }
-
- field.SetInt(val)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- val, err := strconv.ParseUint(value, 0, typ.Bits())
- if err != nil {
- return err
- }
- field.SetUint(val)
- case reflect.Bool:
- val, err := strconv.ParseBool(value)
- if err != nil {
- return err
- }
- field.SetBool(val)
- case reflect.Float32, reflect.Float64:
- val, err := strconv.ParseFloat(value, typ.Bits())
- if err != nil {
- return err
- }
- field.SetFloat(val)
- case reflect.Slice:
- sl := reflect.MakeSlice(typ, 0, 0)
- if typ.Elem().Kind() == reflect.Uint8 {
- sl = reflect.ValueOf([]byte(value))
- } else if strings.TrimSpace(value) != "" {
- vals := strings.Split(value, ",")
- sl = reflect.MakeSlice(typ, len(vals), len(vals))
- for i, val := range vals {
- err := processField(val, sl.Index(i))
- if err != nil {
- return err
- }
- }
- }
- field.Set(sl)
- case reflect.Map:
- mp := reflect.MakeMap(typ)
- if strings.TrimSpace(value) != "" {
- pairs := strings.Split(value, ",")
- for _, pair := range pairs {
- kvpair := strings.Split(pair, ":")
- if len(kvpair) != 2 {
- return fmt.Errorf("invalid map item: %q", pair)
- }
- k := reflect.New(typ.Key()).Elem()
- err := processField(kvpair[0], k)
- if err != nil {
- return err
- }
- v := reflect.New(typ.Elem()).Elem()
- err = processField(kvpair[1], v)
- if err != nil {
- return err
- }
- mp.SetMapIndex(k, v)
- }
- }
- field.Set(mp)
- }
-
- return nil
-}
-
-type ProcessConfig struct {
- ValuesBy SeparatorType
-}
-
-const (
- CAP_GROW_STEP = 8
-)
-
-func processFieldWithConfig(value string, field reflect.Value, config ProcessConfig) error {
- typ := field.Type()
-
- decoder := decoderFrom(field)
- if decoder != nil {
- return decoder.Decode(value)
- }
- // look for Set method if Decode not defined
- setter := setterFrom(field)
- if setter != nil {
- return setter.Set(value)
- }
-
- if t := textUnmarshaler(field); t != nil {
- return t.UnmarshalText([]byte(value))
- }
-
- if b := binaryUnmarshaler(field); b != nil {
- return b.UnmarshalBinary([]byte(value))
- }
-
- if typ.Kind() == reflect.Ptr {
- typ = typ.Elem()
- if field.IsNil() {
- field.Set(reflect.New(typ))
- }
- field = field.Elem()
- }
-
- switch typ.Kind() {
- case reflect.String:
- field.SetString(value)
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- var (
- val int64
- err error
- )
- if field.Kind() == reflect.Int64 && typ.PkgPath() == "time" && typ.Name() == "Duration" {
- var d time.Duration
- d, err = time.ParseDuration(value)
- val = int64(d)
- } else {
- val, err = strconv.ParseInt(value, 0, typ.Bits())
- }
- if err != nil {
- return errors.WithStack(err)
- }
-
- field.SetInt(val)
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- val, err := strconv.ParseUint(value, 0, typ.Bits())
- if err != nil {
- return errors.WithStack(err)
- }
- field.SetUint(val)
- case reflect.Bool:
- val, err := strconv.ParseBool(value)
- if err != nil {
- return errors.WithStack(err)
- }
- field.SetBool(val)
- case reflect.Float32, reflect.Float64:
- val, err := strconv.ParseFloat(value, typ.Bits())
- if err != nil {
- return errors.WithStack(err)
- }
- field.SetFloat(val)
- case reflect.Slice:
- if config.ValuesBy == INDEX {
- if field.IsNil() || field.IsZero() {
- sl := reflect.MakeSlice(typ, 0, 0)
- if typ.Elem().Kind() == reflect.Uint8 {
- return errors.WithStack(errors.New("Not support uint8 slice field to be processed with index values_by tag"))
- } else if strings.TrimSpace(value) != "" {
- sl = reflect.MakeSlice(typ, 1, CAP_GROW_STEP)
- err := processFieldWithConfig(value, sl.Index(0), config)
- if err != nil {
- return errors.WithStack(err)
- }
- }
- field.Set(sl)
- } else {
- size := field.Len()
- if size >= int(math.Floor(float64(field.Cap())*0.75)) {
- field.Grow(CAP_GROW_STEP)
- }
- field.SetLen(size + 1)
- if typ.Elem().Kind() == reflect.Uint8 {
- return errors.WithStack(errors.New("Not support uint8 slice field to be processed with index values_by tag"))
- } else if strings.TrimSpace(value) != "" {
- err := processFieldWithConfig(value, field.Index(size), config)
- if err != nil {
- return errors.WithStack(err)
- }
- }
- }
- } else {
- sl := reflect.MakeSlice(typ, 0, 0)
- if typ.Elem().Kind() == reflect.Uint8 {
- sl = reflect.ValueOf([]byte(value))
- } else if strings.TrimSpace(value) != "" {
- vals := strings.Split(value, ",")
- sl = reflect.MakeSlice(typ, len(vals), len(vals))
- for i, val := range vals {
- err := processFieldWithConfig(val, sl.Index(i), config)
- if err != nil {
- return errors.WithStack(err)
- }
- }
- }
- field.Set(sl)
- }
- case reflect.Map:
- mp := reflect.MakeMap(typ)
- if strings.TrimSpace(value) != "" {
- pairs := strings.Split(value, ",")
- for _, pair := range pairs {
- kvpair := strings.Split(pair, ":")
- if len(kvpair) != 2 {
- return fmt.Errorf("invalid map item: %q", pair)
- }
- k := reflect.New(typ.Key()).Elem()
- err := processFieldWithConfig(kvpair[0], k, config)
- if err != nil {
- return errors.WithStack(err)
- }
- v := reflect.New(typ.Elem()).Elem()
- err = processFieldWithConfig(kvpair[1], v, config)
- if err != nil {
- return errors.WithStack(err)
- }
- mp.SetMapIndex(k, v)
- }
- }
- field.Set(mp)
- }
-
- return nil
-}
-
-func interfaceFrom(field reflect.Value, fn func(interface{}, *bool)) {
- // it may be impossible for a struct field to fail this check
- if !field.CanInterface() {
- return
- }
- var ok bool
- fn(field.Interface(), &ok)
- if !ok && field.CanAddr() {
- fn(field.Addr().Interface(), &ok)
- }
-}
-
-func decoderFrom(field reflect.Value) (d Decoder) {
- interfaceFrom(field, func(v interface{}, ok *bool) { d, *ok = v.(Decoder) })
- return d
-}
-
-func setterFrom(field reflect.Value) (s Setter) {
- interfaceFrom(field, func(v interface{}, ok *bool) { s, *ok = v.(Setter) })
- return s
-}
-
-func textUnmarshaler(field reflect.Value) (t encoding.TextUnmarshaler) {
- interfaceFrom(field, func(v interface{}, ok *bool) { t, *ok = v.(encoding.TextUnmarshaler) })
- return t
-}
-
-func binaryUnmarshaler(field reflect.Value) (b encoding.BinaryUnmarshaler) {
- interfaceFrom(field, func(v interface{}, ok *bool) { b, *ok = v.(encoding.BinaryUnmarshaler) })
- return b
-}
-
-func isTrue(s string) bool {
- b, _ := strconv.ParseBool(s)
- return b
-}
diff --git a/toolkit/envconfig/envconfig_1.8_test.go b/toolkit/envconfig/envconfig_1.8_test.go
deleted file mode 100644
index 0ef0790f..00000000
--- a/toolkit/envconfig/envconfig_1.8_test.go
+++ /dev/null
@@ -1,69 +0,0 @@
-//go:build go1.8
-// +build go1.8
-
-package envconfig
-
-import (
- "errors"
- "net/url"
- "os"
- "testing"
-)
-
-type SpecWithURL struct {
- UrlValue url.URL
- UrlPointer *url.URL
-}
-
-func TestParseURL(t *testing.T) {
- var s SpecWithURL
-
- os.Clearenv()
- os.Setenv("ENV_CONFIG_URLVALUE", "https://github.com/kelseyhightower/envconfig")
- os.Setenv("ENV_CONFIG_URLPOINTER", "https://github.com/kelseyhightower/envconfig")
-
- err := Process("env_config", &s)
- if err != nil {
- t.Fatal("unexpected error:", err)
- }
-
- u, err := url.Parse("https://github.com/kelseyhightower/envconfig")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-
- if s.UrlValue != *u {
- t.Errorf("expected %q, got %q", u, s.UrlValue.String())
- }
-
- if *s.UrlPointer != *u {
- t.Errorf("expected %q, got %q", u, s.UrlPointer)
- }
-}
-
-func TestParseURLError(t *testing.T) {
- var s SpecWithURL
-
- os.Clearenv()
- os.Setenv("ENV_CONFIG_URLPOINTER", "http_://foo")
-
- err := Process("env_config", &s)
-
- v, ok := err.(*ParseError)
- if !ok {
- t.Fatalf("expected ParseError, got %T %v", err, err)
- }
- if v.FieldName != "UrlPointer" {
- t.Errorf("expected %s, got %v", "UrlPointer", v.FieldName)
- }
-
- expectedUnerlyingError := url.Error{
- Op: "parse",
- URL: "http_://foo",
- Err: errors.New("first path segment in URL cannot contain colon"),
- }
-
- if v.Err.Error() != expectedUnerlyingError.Error() {
- t.Errorf("expected %q, got %q", expectedUnerlyingError, v.Err)
- }
-}
diff --git a/toolkit/envconfig/envconfig_test.go b/toolkit/envconfig/envconfig_test.go
deleted file mode 100644
index b0eea96d..00000000
--- a/toolkit/envconfig/envconfig_test.go
+++ /dev/null
@@ -1,936 +0,0 @@
-// Copyright (c) 2013 Kelsey Hightower. All rights reserved.
-// Use of this source code is governed by the MIT License that can be found in
-// the LICENSE file.
-
-package envconfig
-
-import (
- "flag"
- "fmt"
- "net/url"
- "os"
- "strings"
- "testing"
- "time"
-
- "github.com/pkg/errors"
-)
-
-type HonorDecodeInStruct struct {
- Value string
-}
-
-func (h *HonorDecodeInStruct) Decode(env string) error {
- h.Value = "decoded"
- return nil
-}
-
-type CustomURL struct {
- Value *url.URL
-}
-
-func (cu *CustomURL) UnmarshalBinary(data []byte) error {
- u, err := url.Parse(string(data))
- cu.Value = u
- return err
-}
-
-type Specification struct {
- Embedded `desc:"can we document a struct"`
- EmbeddedButIgnored `ignored:"true"`
- Debug bool
- Port int
- Rate float32
- User string
- TTL uint32
- Timeout time.Duration
- AdminUsers []string
- Users []string `values_by:"index" required:"true"`
- Fruits []string `values_by:"index" default:"apple"`
- MagicNumbers []int
- EmptyNumbers []int
- ByteSlice []byte
- ColorCodes map[string]int
- MultiWordVar string
- MultiWordVarWithAutoSplit uint32 `split_words:"true"`
- MultiWordACRWithAutoSplit uint32 `split_words:"true"`
- SomePointer *string
- SomePointerWithDefault *string `default:"foo2baz" desc:"foorbar is the word"`
- MultiWordVarWithAlt string `envconfig:"MULTI_WORD_VAR_WITH_ALT" desc:"what alt"`
- MultiWordVarWithLowerCaseAlt string `envconfig:"multi_word_var_with_lower_case_alt"`
- NoPrefixWithAlt string `envconfig:"SERVICE_HOST"`
- DefaultVar string `default:"foobar"`
- RequiredVar string `required:"True"`
- NoPrefixDefault string `envconfig:"BROKER" default:"127.0.0.1"`
- RequiredDefault string `required:"true" default:"foo2bar"`
- Ignored string `ignored:"true"`
- NestedSpecification struct {
- Property string `envconfig:"inner"`
- PropertyWithDefault string `default:"fuzzybydefault"`
- } `envconfig:"outer"`
- AfterNested string
- DecodeStruct HonorDecodeInStruct `envconfig:"honor"`
- Datetime time.Time
- MapField map[string]string `default:"one:two,three:four"`
- UrlValue CustomURL
- UrlPointer *CustomURL
-}
-
-type Embedded struct {
- Enabled bool `desc:"some embedded value"`
- EmbeddedPort int
- MultiWordVar string
- MultiWordVarWithAlt string `envconfig:"MULTI_WITH_DIFFERENT_ALT"`
- EmbeddedAlt string `envconfig:"EMBEDDED_WITH_ALT"`
- EmbeddedIgnored string `ignored:"true"`
-}
-
-type EmbeddedButIgnored struct {
- FirstEmbeddedButIgnored string
- SecondEmbeddedButIgnored string
-}
-
-func TestProcess(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- os.Setenv("ENV_CONFIG_DEBUG", "true")
- os.Setenv("ENV_CONFIG_PORT", "8080")
- os.Setenv("ENV_CONFIG_RATE", "0.5")
- os.Setenv("ENV_CONFIG_USER", "Kelsey")
- os.Setenv("ENV_CONFIG_TIMEOUT", "2m")
- os.Setenv("ENV_CONFIG_ADMINUSERS", "John,Adam,Will")
- os.Setenv("ENV_CONFIG_MAGICNUMBERS", "5,10,20")
- os.Setenv("ENV_CONFIG_EMPTYNUMBERS", "")
- os.Setenv("ENV_CONFIG_BYTESLICE", "this is a test value")
- os.Setenv("ENV_CONFIG_COLORCODES", "red:1,green:2,blue:3")
- os.Setenv("SERVICE_HOST", "127.0.0.1")
- os.Setenv("ENV_CONFIG_TTL", "30")
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- os.Setenv("ENV_CONFIG_IGNORED", "was-not-ignored")
- os.Setenv("ENV_CONFIG_OUTER_INNER", "iamnested")
- os.Setenv("ENV_CONFIG_AFTERNESTED", "after")
- os.Setenv("ENV_CONFIG_HONOR", "honor")
- os.Setenv("ENV_CONFIG_DATETIME", "2016-08-16T18:57:05Z")
- os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT", "24")
- os.Setenv("ENV_CONFIG_MULTI_WORD_ACR_WITH_AUTO_SPLIT", "25")
- os.Setenv("ENV_CONFIG_URLVALUE", "https://github.com/kelseyhightower/envconfig")
- os.Setenv("ENV_CONFIG_URLPOINTER", "https://github.com/kelseyhightower/envconfig")
- err := Process("env_config", &s)
- if err != nil {
- t.Error(err.Error())
- }
- if s.NoPrefixWithAlt != "127.0.0.1" {
- t.Errorf("expected %v, got %v", "127.0.0.1", s.NoPrefixWithAlt)
- }
- if !s.Debug {
- t.Errorf("expected %v, got %v", true, s.Debug)
- }
- if s.Port != 8080 {
- t.Errorf("expected %d, got %v", 8080, s.Port)
- }
- if s.Rate != 0.5 {
- t.Errorf("expected %f, got %v", 0.5, s.Rate)
- }
- if s.TTL != 30 {
- t.Errorf("expected %d, got %v", 30, s.TTL)
- }
- if s.User != "Kelsey" {
- t.Errorf("expected %s, got %s", "Kelsey", s.User)
- }
- if s.Timeout != 2*time.Minute {
- t.Errorf("expected %s, got %s", 2*time.Minute, s.Timeout)
- }
- if s.RequiredVar != "foo" {
- t.Errorf("expected %s, got %s", "foo", s.RequiredVar)
- }
- if len(s.AdminUsers) != 3 ||
- s.AdminUsers[0] != "John" ||
- s.AdminUsers[1] != "Adam" ||
- s.AdminUsers[2] != "Will" {
- t.Errorf("expected %#v, got %#v", []string{"John", "Adam", "Will"}, s.AdminUsers)
- }
- if len(s.MagicNumbers) != 3 ||
- s.MagicNumbers[0] != 5 ||
- s.MagicNumbers[1] != 10 ||
- s.MagicNumbers[2] != 20 {
- t.Errorf("expected %#v, got %#v", []int{5, 10, 20}, s.MagicNumbers)
- }
- if len(s.EmptyNumbers) != 0 {
- t.Errorf("expected %#v, got %#v", []int{}, s.EmptyNumbers)
- }
- expected := "this is a test value"
- if string(s.ByteSlice) != expected {
- t.Errorf("expected %v, got %v", expected, string(s.ByteSlice))
- }
- if s.Ignored != "" {
- t.Errorf("expected empty string, got %#v", s.Ignored)
- }
-
- if len(s.ColorCodes) != 3 ||
- s.ColorCodes["red"] != 1 ||
- s.ColorCodes["green"] != 2 ||
- s.ColorCodes["blue"] != 3 {
- t.Errorf(
- "expected %#v, got %#v",
- map[string]int{
- "red": 1,
- "green": 2,
- "blue": 3,
- },
- s.ColorCodes,
- )
- }
-
- if s.NestedSpecification.Property != "iamnested" {
- t.Errorf("expected '%s' string, got %#v", "iamnested", s.NestedSpecification.Property)
- }
-
- if s.NestedSpecification.PropertyWithDefault != "fuzzybydefault" {
- t.Errorf("expected default '%s' string, got %#v", "fuzzybydefault", s.NestedSpecification.PropertyWithDefault)
- }
-
- if s.AfterNested != "after" {
- t.Errorf("expected default '%s' string, got %#v", "after", s.AfterNested)
- }
-
- if s.DecodeStruct.Value != "decoded" {
- t.Errorf("expected default '%s' string, got %#v", "decoded", s.DecodeStruct.Value)
- }
-
- if expected := time.Date(2016, 8, 16, 18, 57, 05, 0, time.UTC); !s.Datetime.Equal(expected) {
- t.Errorf("expected %s, got %s", expected.Format(time.RFC3339), s.Datetime.Format(time.RFC3339))
- }
-
- if s.MultiWordVarWithAutoSplit != 24 {
- t.Errorf("expected %q, got %q", 24, s.MultiWordVarWithAutoSplit)
- }
-
- if s.MultiWordACRWithAutoSplit != 25 {
- t.Errorf("expected %d, got %d", 25, s.MultiWordACRWithAutoSplit)
- }
-
- u, err := url.Parse("https://github.com/kelseyhightower/envconfig")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
-
- if *s.UrlValue.Value != *u {
- t.Errorf("expected %q, got %q", u, s.UrlValue.Value.String())
- }
-
- if *s.UrlPointer.Value != *u {
- t.Errorf("expected %q, got %q", u, s.UrlPointer.Value.String())
- }
-}
-
-func TestProcessValuesBy(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- os.Setenv("ENV_CONFIG_USERS_1", "Adam2")
- os.Setenv("ENV_CONFIG_USERS_2", "Will3")
- os.Setenv("ENV_CONFIG_USERS_3", "Will4")
- os.Setenv("ENV_CONFIG_USERS_4", "Will5")
- os.Setenv("ENV_CONFIG_USERS_5", "Will6")
- os.Setenv("ENV_CONFIG_USERS_6", "Will7")
- os.Setenv("ENV_CONFIG_USERS_7", "Will8")
- os.Setenv("ENV_CONFIG_USERS_8", "Will9")
- os.Setenv("ENV_CONFIG_USERS_9", "Will10")
- os.Setenv("ENV_CONFIG_USERS_10", "Will11")
- _ = Process("env_config", &s)
- if len(s.Users) != 11 {
- t.Errorf("expected %#v, got %#v", []string{"John1", "Adam2", "Will3", "Will4", "Will5", "Will6", "Will7", "Will8", "Will9", "Will10", "Will11"}, s.Users)
- }
-}
-
-func TestProcessValuesByRequired(t *testing.T) {
- var s Specification
- os.Clearenv()
- err := Process("env_config", &s)
- if err == nil {
- t.Errorf("should have error")
- }
- if err.Error() != "required key ENV_CONFIG_USERS missing value" {
- t.Errorf("error not right")
- }
-}
-
-func TestProcessValuesByDefault(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- err := Process("env_config", &s)
- if err != nil {
- t.Errorf("should not have error: %s", err)
- }
- if s.Fruits[0] != "apple" {
- t.Errorf("fruits should have one and only one element apple")
- }
-}
-
-func TestParseErrorBool(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_DEBUG", "string")
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- err := Process("env_config", &s)
- v := &ParseError{}
- ok := errors.As(err, &v)
- if !ok {
- t.Errorf("expected ParseError, got %v", v)
- }
- if v.FieldName != "Debug" {
- t.Errorf("expected %s, got %v", "Debug", v.FieldName)
- }
- if s.Debug != false {
- t.Errorf("expected %v, got %v", false, s.Debug)
- }
-}
-
-func TestParseErrorFloat32(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_RATE", "string")
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- err := Process("env_config", &s)
- v := &ParseError{}
- ok := errors.As(err, &v)
- if !ok {
- t.Errorf("expected ParseError, got %v", v)
- }
- if v.FieldName != "Rate" {
- t.Errorf("expected %s, got %v", "Rate", v.FieldName)
- }
- if s.Rate != 0 {
- t.Errorf("expected %v, got %v", 0, s.Rate)
- }
-}
-
-func TestParseErrorInt(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_PORT", "string")
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- err := Process("env_config", &s)
- v := &ParseError{}
- ok := errors.As(err, &v)
- if !ok {
- t.Errorf("expected ParseError, got %v", v)
- }
- if v.FieldName != "Port" {
- t.Errorf("expected %s, got %v", "Port", v.FieldName)
- }
- if s.Port != 0 {
- t.Errorf("expected %v, got %v", 0, s.Port)
- }
-}
-
-func TestParseErrorUint(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_TTL", "-30")
- err := Process("env_config", &s)
- v := &ParseError{}
- ok := errors.As(err, &v)
- if !ok {
- t.Errorf("expected ParseError, got %v", v)
- }
- if v.FieldName != "TTL" {
- t.Errorf("expected %s, got %v", "TTL", v.FieldName)
- }
- if s.TTL != 0 {
- t.Errorf("expected %v, got %v", 0, s.TTL)
- }
-}
-
-func TestParseErrorSplitWords(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT", "shakespeare")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- err := Process("env_config", &s)
- v := &ParseError{}
- ok := errors.As(err, &v)
- if !ok {
- t.Errorf("expected ParseError, got %v", v)
- }
- if v.FieldName != "MultiWordVarWithAutoSplit" {
- t.Errorf("expected %s, got %v", "", v.FieldName)
- }
- if s.MultiWordVarWithAutoSplit != 0 {
- t.Errorf("expected %v, got %v", 0, s.MultiWordVarWithAutoSplit)
- }
-}
-
-func TestErrInvalidSpecification(t *testing.T) {
- m := make(map[string]string)
- err := Process("env_config", &m)
- if err != ErrInvalidSpecification {
- t.Errorf("expected %v, got %v", ErrInvalidSpecification, err)
- }
-}
-
-func TestUnsetVars(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("USER", "foo")
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- // If the var is not defined the non-prefixed version should not be used
- // unless the struct tag says so
- if s.User != "" {
- t.Errorf("expected %q, got %q", "", s.User)
- }
-}
-
-func TestAlternateVarNames(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_MULTI_WORD_VAR", "foo")
- os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_ALT", "bar")
- os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_LOWER_CASE_ALT", "baz")
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- // Setting the alt version of the var in the environment has no effect if
- // the struct tag is not supplied
- if s.MultiWordVar != "" {
- t.Errorf("expected %q, got %q", "", s.MultiWordVar)
- }
-
- // Setting the alt version of the var in the environment correctly sets
- // the value if the struct tag IS supplied
- if s.MultiWordVarWithAlt != "bar" {
- t.Errorf("expected %q, got %q", "bar", s.MultiWordVarWithAlt)
- }
-
- // Alt value is not case sensitive and is treated as all uppercase
- if s.MultiWordVarWithLowerCaseAlt != "baz" {
- t.Errorf("expected %q, got %q", "baz", s.MultiWordVarWithLowerCaseAlt)
- }
-}
-
-func TestRequiredVar(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foobar")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- if s.RequiredVar != "foobar" {
- t.Errorf("expected %s, got %s", "foobar", s.RequiredVar)
- }
-}
-
-func TestRequiredMissing(t *testing.T) {
- var s Specification
- os.Clearenv()
-
- err := Process("env_config", &s)
- if err == nil {
- t.Error("no failure when missing required variable")
- }
-}
-
-func TestBlankDefaultVar(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "requiredvalue")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- if s.DefaultVar != "foobar" {
- t.Errorf("expected %s, got %s", "foobar", s.DefaultVar)
- }
-
- if *s.SomePointerWithDefault != "foo2baz" {
- t.Errorf("expected %s, got %s", "foo2baz", *s.SomePointerWithDefault)
- }
-}
-
-func TestNonBlankDefaultVar(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_DEFAULTVAR", "nondefaultval")
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "requiredvalue")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- if s.DefaultVar != "nondefaultval" {
- t.Errorf("expected %s, got %s", "nondefaultval", s.DefaultVar)
- }
-}
-
-func TestExplicitBlankDefaultVar(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_DEFAULTVAR", "")
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- if s.DefaultVar != "" {
- t.Errorf("expected %s, got %s", "\"\"", s.DefaultVar)
- }
-}
-
-func TestAlternateNameDefaultVar(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("BROKER", "betterbroker")
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- if s.NoPrefixDefault != "betterbroker" {
- t.Errorf("expected %q, got %q", "betterbroker", s.NoPrefixDefault)
- }
-
- os.Clearenv()
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- if s.NoPrefixDefault != "127.0.0.1" {
- t.Errorf("expected %q, got %q", "127.0.0.1", s.NoPrefixDefault)
- }
-}
-
-func TestRequiredDefault(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- if s.RequiredDefault != "foo2bar" {
- t.Errorf("expected %q, got %q", "foo2bar", s.RequiredDefault)
- }
-}
-
-func TestPointerFieldBlank(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- if s.SomePointer != nil {
- t.Errorf("expected , got %q", *s.SomePointer)
- }
-}
-
-func TestEmptyMapFieldOverride(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- os.Setenv("ENV_CONFIG_MAPFIELD", "")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- if s.MapField == nil {
- t.Error("expected empty map, got ")
- }
-
- if len(s.MapField) != 0 {
- t.Errorf("expected empty map, got map of size %d", len(s.MapField))
- }
-}
-
-func TestMustProcess(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_DEBUG", "true")
- os.Setenv("ENV_CONFIG_PORT", "8080")
- os.Setenv("ENV_CONFIG_RATE", "0.5")
- os.Setenv("ENV_CONFIG_USER", "Kelsey")
- os.Setenv("SERVICE_HOST", "127.0.0.1")
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- MustProcess("env_config", &s)
-
- defer func() {
- if err := recover(); err != nil {
- return
- }
-
- t.Error("expected panic")
- }()
- m := make(map[string]string)
- MustProcess("env_config", &m)
-}
-
-func TestEmbeddedStruct(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "required")
- os.Setenv("ENV_CONFIG_ENABLED", "true")
- os.Setenv("ENV_CONFIG_EMBEDDEDPORT", "1234")
- os.Setenv("ENV_CONFIG_MULTIWORDVAR", "foo")
- os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_ALT", "bar")
- os.Setenv("ENV_CONFIG_MULTI_WITH_DIFFERENT_ALT", "baz")
- os.Setenv("ENV_CONFIG_EMBEDDED_WITH_ALT", "foobar")
- os.Setenv("ENV_CONFIG_SOMEPOINTER", "foobaz")
- os.Setenv("ENV_CONFIG_EMBEDDED_IGNORED", "was-not-ignored")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
- if !s.Enabled {
- t.Errorf("expected %v, got %v", true, s.Enabled)
- }
- if s.EmbeddedPort != 1234 {
- t.Errorf("expected %d, got %v", 1234, s.EmbeddedPort)
- }
- if s.MultiWordVar != "foo" {
- t.Errorf("expected %s, got %s", "foo", s.MultiWordVar)
- }
- if s.Embedded.MultiWordVar != "foo" {
- t.Errorf("expected %s, got %s", "foo", s.Embedded.MultiWordVar)
- }
- if s.MultiWordVarWithAlt != "bar" {
- t.Errorf("expected %s, got %s", "bar", s.MultiWordVarWithAlt)
- }
- if s.Embedded.MultiWordVarWithAlt != "baz" {
- t.Errorf("expected %s, got %s", "baz", s.Embedded.MultiWordVarWithAlt)
- }
- if s.EmbeddedAlt != "foobar" {
- t.Errorf("expected %s, got %s", "foobar", s.EmbeddedAlt)
- }
- if *s.SomePointer != "foobaz" {
- t.Errorf("expected %s, got %s", "foobaz", *s.SomePointer)
- }
- if s.EmbeddedIgnored != "" {
- t.Errorf("expected empty string, got %#v", s.Ignored)
- }
-}
-
-func TestEmbeddedButIgnoredStruct(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "required")
- os.Setenv("ENV_CONFIG_FIRSTEMBEDDEDBUTIGNORED", "was-not-ignored")
- os.Setenv("ENV_CONFIG_SECONDEMBEDDEDBUTIGNORED", "was-not-ignored")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
- if s.FirstEmbeddedButIgnored != "" {
- t.Errorf("expected empty string, got %#v", s.Ignored)
- }
- if s.SecondEmbeddedButIgnored != "" {
- t.Errorf("expected empty string, got %#v", s.Ignored)
- }
-}
-
-func TestNonPointerFailsProperly(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "snap")
-
- err := Process("env_config", s)
- if err != ErrInvalidSpecification {
- t.Errorf("non-pointer should fail with ErrInvalidSpecification, was instead %s", err)
- }
-}
-
-func TestCustomValueFields(t *testing.T) {
- var s struct {
- Foo string
- Bar bracketed
- Baz quoted
- Struct setterStruct
- }
-
- // Set would panic when the receiver is nil,
- // so make sure it has an initial value to replace.
- s.Baz = quoted{new(bracketed)}
-
- os.Clearenv()
- os.Setenv("ENV_CONFIG_FOO", "foo")
- os.Setenv("ENV_CONFIG_BAR", "bar")
- os.Setenv("ENV_CONFIG_BAZ", "baz")
- os.Setenv("ENV_CONFIG_STRUCT", "inner")
-
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- if want := "foo"; s.Foo != want {
- t.Errorf("foo: got %#q, want %#q", s.Foo, want)
- }
-
- if want := "[bar]"; s.Bar.String() != want {
- t.Errorf("bar: got %#q, want %#q", s.Bar, want)
- }
-
- if want := `["baz"]`; s.Baz.String() != want {
- t.Errorf(`baz: got %#q, want %#q`, s.Baz, want)
- }
-
- if want := `setterstruct{"inner"}`; s.Struct.Inner != want {
- t.Errorf(`Struct.Inner: got %#q, want %#q`, s.Struct.Inner, want)
- }
-}
-
-func TestCustomPointerFields(t *testing.T) {
- var s struct {
- Foo string
- Bar *bracketed
- Baz *quoted
- Struct *setterStruct
- }
-
- // Set would panic when the receiver is nil,
- // so make sure they have initial values to replace.
- s.Bar = new(bracketed)
- s.Baz = "ed{new(bracketed)}
-
- os.Clearenv()
- os.Setenv("ENV_CONFIG_FOO", "foo")
- os.Setenv("ENV_CONFIG_BAR", "bar")
- os.Setenv("ENV_CONFIG_BAZ", "baz")
- os.Setenv("ENV_CONFIG_STRUCT", "inner")
-
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
-
- if want := "foo"; s.Foo != want {
- t.Errorf("foo: got %#q, want %#q", s.Foo, want)
- }
-
- if want := "[bar]"; s.Bar.String() != want {
- t.Errorf("bar: got %#q, want %#q", s.Bar, want)
- }
-
- if want := `["baz"]`; s.Baz.String() != want {
- t.Errorf(`baz: got %#q, want %#q`, s.Baz, want)
- }
-
- if want := `setterstruct{"inner"}`; s.Struct.Inner != want {
- t.Errorf(`Struct.Inner: got %#q, want %#q`, s.Struct.Inner, want)
- }
-}
-
-func TestEmptyPrefixUsesFieldNames(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("REQUIREDVAR", "foo")
- os.Setenv("USERS_0", "John1")
- err := Process("", &s)
- if err != nil {
- t.Errorf("Process failed: %s", err)
- }
-
- if s.RequiredVar != "foo" {
- t.Errorf(
- `RequiredVar not populated correctly: expected "foo", got %q`,
- s.RequiredVar,
- )
- }
-}
-
-func TestNestedStructVarName(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "required")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- val := "found with only short name"
- os.Setenv("INNER", val)
- if err := Process("env_config", &s); err != nil {
- t.Error(err.Error())
- }
- if s.NestedSpecification.Property != val {
- t.Errorf("expected %s, got %s", val, s.NestedSpecification.Property)
- }
-}
-
-func TestTextUnmarshalerError(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- os.Setenv("ENV_CONFIG_DATETIME", "I'M NOT A DATE")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- err := Process("env_config", &s)
-
- v := &ParseError{}
- ok := errors.As(err, &v)
- if !ok {
- t.Errorf("expected ParseError, got %v", v)
- }
- if v.FieldName != "Datetime" {
- t.Errorf("expected %s, got %v", "Datetime", v.FieldName)
- }
-
- expectedLowLevelError := time.ParseError{
- Layout: time.RFC3339,
- Value: "I'M NOT A DATE",
- LayoutElem: "2006",
- ValueElem: "I'M NOT A DATE",
- }
-
- if v.Err.Error() != expectedLowLevelError.Error() {
- t.Errorf("expected %s, got %s", expectedLowLevelError, v.Err)
- }
-}
-
-func TestBinaryUnmarshalerError(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- os.Setenv("ENV_CONFIG_URLPOINTER", "http://%41:8080/")
- os.Setenv("ENV_CONFIG_USERS_0", "John1")
- err := Process("env_config", &s)
-
- v := &ParseError{}
- ok := errors.As(err, &v)
- if !ok {
- t.Fatalf("expected ParseError, got %T %v", err, err)
- }
- if v.FieldName != "UrlPointer" {
- t.Errorf("expected %s, got %v", "UrlPointer", v.FieldName)
- }
-
- // To be compatible with go 1.5 and lower we should do a very basic check,
- // because underlying error message varies in go 1.5 and go 1.6+.
- ue := &url.Error{}
- ok = errors.As(v.Err, &ue)
- if !ok {
- t.Errorf("expected error type to be \"*url.Error\", got %T", v.Err)
- }
-
- if ue.Op != "parse" {
- t.Errorf("expected error op to be \"parse\", got %q", ue.Op)
- }
-}
-
-func TestCheckDisallowedOnlyAllowed(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_DEBUG", "true")
- os.Setenv("UNRELATED_ENV_VAR", "true")
- err := CheckDisallowed("env_config", &s)
- if err != nil {
- t.Errorf("expected no error, got %s", err)
- }
-}
-
-func TestCheckDisallowedMispelled(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_DEBUG", "true")
- os.Setenv("ENV_CONFIG_ZEBUG", "false")
- err := CheckDisallowed("env_config", &s)
- if experr := "unknown environment variable ENV_CONFIG_ZEBUG"; err.Error() != experr {
- t.Errorf("expected %s, got %s", experr, err)
- }
-}
-
-func TestCheckDisallowedIgnored(t *testing.T) {
- var s Specification
- os.Clearenv()
- os.Setenv("ENV_CONFIG_DEBUG", "true")
- os.Setenv("ENV_CONFIG_IGNORED", "false")
- err := CheckDisallowed("env_config", &s)
- if experr := "unknown environment variable ENV_CONFIG_IGNORED"; err.Error() != experr {
- t.Errorf("expected %s, got %s", experr, err)
- }
-}
-
-func TestErrorMessageForRequiredAltVar(t *testing.T) {
- var s struct {
- Foo string `envconfig:"BAR" required:"true"`
- }
-
- os.Clearenv()
- err := Process("env_config", &s)
-
- if err == nil {
- t.Error("no failure when missing required variable")
- }
-
- if !strings.Contains(err.Error(), " BAR ") {
- t.Errorf("expected error message to contain BAR, got \"%v\"", err)
- }
-}
-
-type bracketed string
-
-func (b *bracketed) Set(value string) error {
- *b = bracketed("[" + value + "]")
- return nil
-}
-
-func (b bracketed) String() string {
- return string(b)
-}
-
-// quoted is used to test the precedence of Decode over Set.
-// The sole field is a flag.Value rather than a setter to validate that
-// all flag.Value implementations are also Setter implementations.
-type quoted struct{ flag.Value }
-
-func (d quoted) Decode(value string) error {
- return d.Set(`"` + value + `"`)
-}
-
-type setterStruct struct {
- Inner string
-}
-
-func (ss *setterStruct) Set(value string) error {
- ss.Inner = fmt.Sprintf("setterstruct{%q}", value)
- return nil
-}
-
-func BenchmarkGatherInfo(b *testing.B) {
- os.Clearenv()
- os.Setenv("ENV_CONFIG_DEBUG", "true")
- os.Setenv("ENV_CONFIG_PORT", "8080")
- os.Setenv("ENV_CONFIG_RATE", "0.5")
- os.Setenv("ENV_CONFIG_USER", "Kelsey")
- os.Setenv("ENV_CONFIG_TIMEOUT", "2m")
- os.Setenv("ENV_CONFIG_ADMINUSERS", "John,Adam,Will")
- os.Setenv("ENV_CONFIG_MAGICNUMBERS", "5,10,20")
- os.Setenv("ENV_CONFIG_COLORCODES", "red:1,green:2,blue:3")
- os.Setenv("SERVICE_HOST", "127.0.0.1")
- os.Setenv("ENV_CONFIG_TTL", "30")
- os.Setenv("ENV_CONFIG_REQUIREDVAR", "foo")
- os.Setenv("ENV_CONFIG_IGNORED", "was-not-ignored")
- os.Setenv("ENV_CONFIG_OUTER_INNER", "iamnested")
- os.Setenv("ENV_CONFIG_AFTERNESTED", "after")
- os.Setenv("ENV_CONFIG_HONOR", "honor")
- os.Setenv("ENV_CONFIG_DATETIME", "2016-08-16T18:57:05Z")
- os.Setenv("ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT", "24")
- for i := 0; i < b.N; i++ {
- var s Specification
- gatherInfo("env_config", &s)
- }
-}
diff --git a/toolkit/envconfig/testdata/custom.txt b/toolkit/envconfig/testdata/custom.txt
deleted file mode 100644
index 04d2f5d0..00000000
--- a/toolkit/envconfig/testdata/custom.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-ENV_CONFIG_ENABLED=some.embedded.value
-ENV_CONFIG_EMBEDDEDPORT=
-ENV_CONFIG_MULTIWORDVAR=
-ENV_CONFIG_MULTI_WITH_DIFFERENT_ALT=
-ENV_CONFIG_EMBEDDED_WITH_ALT=
-ENV_CONFIG_DEBUG=
-ENV_CONFIG_PORT=
-ENV_CONFIG_RATE=
-ENV_CONFIG_USER=
-ENV_CONFIG_TTL=
-ENV_CONFIG_TIMEOUT=
-ENV_CONFIG_ADMINUSERS=
-ENV_CONFIG_MAGICNUMBERS=
-ENV_CONFIG_EMPTYNUMBERS=
-ENV_CONFIG_BYTESLICE=
-ENV_CONFIG_COLORCODES=
-ENV_CONFIG_MULTIWORDVAR=
-ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT=
-ENV_CONFIG_MULTI_WORD_ACR_WITH_AUTO_SPLIT=
-ENV_CONFIG_SOMEPOINTER=
-ENV_CONFIG_SOMEPOINTERWITHDEFAULT=foorbar.is.the.word
-ENV_CONFIG_MULTI_WORD_VAR_WITH_ALT=what.alt
-ENV_CONFIG_MULTI_WORD_VAR_WITH_LOWER_CASE_ALT=
-ENV_CONFIG_SERVICE_HOST=
-ENV_CONFIG_DEFAULTVAR=
-ENV_CONFIG_REQUIREDVAR=
-ENV_CONFIG_BROKER=
-ENV_CONFIG_REQUIREDDEFAULT=
-ENV_CONFIG_OUTER_INNER=
-ENV_CONFIG_OUTER_PROPERTYWITHDEFAULT=
-ENV_CONFIG_AFTERNESTED=
-ENV_CONFIG_HONOR=
-ENV_CONFIG_DATETIME=
-ENV_CONFIG_MAPFIELD=
-ENV_CONFIG_URLVALUE=
-ENV_CONFIG_URLPOINTER=
diff --git a/toolkit/envconfig/testdata/default_list.txt b/toolkit/envconfig/testdata/default_list.txt
deleted file mode 100644
index fb0eced7..00000000
--- a/toolkit/envconfig/testdata/default_list.txt
+++ /dev/null
@@ -1,183 +0,0 @@
-This.application.is.configured.via.the.environment..The.following.environment
-variables.can.be.used:
-
-ENV_CONFIG_ENABLED
-..[description].some.embedded.value
-..[type]........True.or.False
-..[default].....
-..[required]....
-ENV_CONFIG_EMBEDDEDPORT
-..[description].
-..[type]........Integer
-..[default].....
-..[required]....
-ENV_CONFIG_MULTIWORDVAR
-..[description].
-..[type]........String
-..[default].....
-..[required]....
-ENV_CONFIG_MULTI_WITH_DIFFERENT_ALT
-..[description].
-..[type]........String
-..[default].....
-..[required]....
-ENV_CONFIG_EMBEDDED_WITH_ALT
-..[description].
-..[type]........String
-..[default].....
-..[required]....
-ENV_CONFIG_DEBUG
-..[description].
-..[type]........True.or.False
-..[default].....
-..[required]....
-ENV_CONFIG_PORT
-..[description].
-..[type]........Integer
-..[default].....
-..[required]....
-ENV_CONFIG_RATE
-..[description].
-..[type]........Float
-..[default].....
-..[required]....
-ENV_CONFIG_USER
-..[description].
-..[type]........String
-..[default].....
-..[required]....
-ENV_CONFIG_TTL
-..[description].
-..[type]........Unsigned.Integer
-..[default].....
-..[required]....
-ENV_CONFIG_TIMEOUT
-..[description].
-..[type]........Duration
-..[default].....
-..[required]....
-ENV_CONFIG_ADMINUSERS
-..[description].
-..[type]........Comma-separated.list.of.String
-..[default].....
-..[required]....
-ENV_CONFIG_MAGICNUMBERS
-..[description].
-..[type]........Comma-separated.list.of.Integer
-..[default].....
-..[required]....
-ENV_CONFIG_EMPTYNUMBERS
-..[description].
-..[type]........Comma-separated.list.of.Integer
-..[default].....
-..[required]....
-ENV_CONFIG_BYTESLICE
-..[description].
-..[type]........String
-..[default].....
-..[required]....
-ENV_CONFIG_COLORCODES
-..[description].
-..[type]........Comma-separated.list.of.String:Integer.pairs
-..[default].....
-..[required]....
-ENV_CONFIG_MULTIWORDVAR
-..[description].
-..[type]........String
-..[default].....
-..[required]....
-ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT
-..[description].
-..[type]........Unsigned.Integer
-..[default].....
-..[required]....
-ENV_CONFIG_MULTI_WORD_ACR_WITH_AUTO_SPLIT
-..[description].
-..[type]........Unsigned.Integer
-..[default].....
-..[required]....
-ENV_CONFIG_SOMEPOINTER
-..[description].
-..[type]........String
-..[default].....
-..[required]....
-ENV_CONFIG_SOMEPOINTERWITHDEFAULT
-..[description].foorbar.is.the.word
-..[type]........String
-..[default].....foo2baz
-..[required]....
-ENV_CONFIG_MULTI_WORD_VAR_WITH_ALT
-..[description].what.alt
-..[type]........String
-..[default].....
-..[required]....
-ENV_CONFIG_MULTI_WORD_VAR_WITH_LOWER_CASE_ALT
-..[description].
-..[type]........String
-..[default].....
-..[required]....
-ENV_CONFIG_SERVICE_HOST
-..[description].
-..[type]........String
-..[default].....
-..[required]....
-ENV_CONFIG_DEFAULTVAR
-..[description].
-..[type]........String
-..[default].....foobar
-..[required]....
-ENV_CONFIG_REQUIREDVAR
-..[description].
-..[type]........String
-..[default].....
-..[required]....true
-ENV_CONFIG_BROKER
-..[description].
-..[type]........String
-..[default].....127.0.0.1
-..[required]....
-ENV_CONFIG_REQUIREDDEFAULT
-..[description].
-..[type]........String
-..[default].....foo2bar
-..[required]....true
-ENV_CONFIG_OUTER_INNER
-..[description].
-..[type]........String
-..[default].....
-..[required]....
-ENV_CONFIG_OUTER_PROPERTYWITHDEFAULT
-..[description].
-..[type]........String
-..[default].....fuzzybydefault
-..[required]....
-ENV_CONFIG_AFTERNESTED
-..[description].
-..[type]........String
-..[default].....
-..[required]....
-ENV_CONFIG_HONOR
-..[description].
-..[type]........HonorDecodeInStruct
-..[default].....
-..[required]....
-ENV_CONFIG_DATETIME
-..[description].
-..[type]........Time
-..[default].....
-..[required]....
-ENV_CONFIG_MAPFIELD
-..[description].
-..[type]........Comma-separated.list.of.String:String.pairs
-..[default].....one:two,three:four
-..[required]....
-ENV_CONFIG_URLVALUE
-..[description].
-..[type]........CustomURL
-..[default].....
-..[required]....
-ENV_CONFIG_URLPOINTER
-..[description].
-..[type]........CustomURL
-..[default].....
-..[required]....
diff --git a/toolkit/envconfig/testdata/default_table.txt b/toolkit/envconfig/testdata/default_table.txt
deleted file mode 100644
index 65c9b445..00000000
--- a/toolkit/envconfig/testdata/default_table.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-This.application.is.configured.via.the.environment..The.following.environment
-variables.can.be.used:
-
-KEY..............................................TYPE............................................DEFAULT...............REQUIRED....DESCRIPTION
-ENV_CONFIG_ENABLED...............................True.or.False.....................................................................some.embedded.value
-ENV_CONFIG_EMBEDDEDPORT..........................Integer...........................................................................
-ENV_CONFIG_MULTIWORDVAR..........................String............................................................................
-ENV_CONFIG_MULTI_WITH_DIFFERENT_ALT..............String............................................................................
-ENV_CONFIG_EMBEDDED_WITH_ALT.....................String............................................................................
-ENV_CONFIG_DEBUG.................................True.or.False.....................................................................
-ENV_CONFIG_PORT..................................Integer...........................................................................
-ENV_CONFIG_RATE..................................Float.............................................................................
-ENV_CONFIG_USER..................................String............................................................................
-ENV_CONFIG_TTL...................................Unsigned.Integer..................................................................
-ENV_CONFIG_TIMEOUT...............................Duration..........................................................................
-ENV_CONFIG_ADMINUSERS............................Comma-separated.list.of.String....................................................
-ENV_CONFIG_MAGICNUMBERS..........................Comma-separated.list.of.Integer...................................................
-ENV_CONFIG_EMPTYNUMBERS..........................Comma-separated.list.of.Integer...................................................
-ENV_CONFIG_BYTESLICE.............................String............................................................................
-ENV_CONFIG_COLORCODES............................Comma-separated.list.of.String:Integer.pairs......................................
-ENV_CONFIG_MULTIWORDVAR..........................String............................................................................
-ENV_CONFIG_MULTI_WORD_VAR_WITH_AUTO_SPLIT........Unsigned.Integer..................................................................
-ENV_CONFIG_MULTI_WORD_ACR_WITH_AUTO_SPLIT........Unsigned.Integer..................................................................
-ENV_CONFIG_SOMEPOINTER...........................String............................................................................
-ENV_CONFIG_SOMEPOINTERWITHDEFAULT................String..........................................foo2baz...........................foorbar.is.the.word
-ENV_CONFIG_MULTI_WORD_VAR_WITH_ALT...............String............................................................................what.alt
-ENV_CONFIG_MULTI_WORD_VAR_WITH_LOWER_CASE_ALT....String............................................................................
-ENV_CONFIG_SERVICE_HOST..........................String............................................................................
-ENV_CONFIG_DEFAULTVAR............................String..........................................foobar............................
-ENV_CONFIG_REQUIREDVAR...........................String................................................................true........
-ENV_CONFIG_BROKER................................String..........................................127.0.0.1.........................
-ENV_CONFIG_REQUIREDDEFAULT.......................String..........................................foo2bar...............true........
-ENV_CONFIG_OUTER_INNER...........................String............................................................................
-ENV_CONFIG_OUTER_PROPERTYWITHDEFAULT.............String..........................................fuzzybydefault....................
-ENV_CONFIG_AFTERNESTED...........................String............................................................................
-ENV_CONFIG_HONOR.................................HonorDecodeInStruct...............................................................
-ENV_CONFIG_DATETIME..............................Time..............................................................................
-ENV_CONFIG_MAPFIELD..............................Comma-separated.list.of.String:String.pairs.....one:two,three:four................
-ENV_CONFIG_URLVALUE..............................CustomURL.........................................................................
-ENV_CONFIG_URLPOINTER............................CustomURL.........................................................................
diff --git a/toolkit/envconfig/testdata/fault.txt b/toolkit/envconfig/testdata/fault.txt
deleted file mode 100644
index b525ff12..00000000
--- a/toolkit/envconfig/testdata/fault.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
-{.Key}
diff --git a/toolkit/envconfig/usage.go b/toolkit/envconfig/usage.go
deleted file mode 100644
index aebadb03..00000000
--- a/toolkit/envconfig/usage.go
+++ /dev/null
@@ -1,164 +0,0 @@
-// Copyright (c) 2016 Kelsey Hightower and others. All rights reserved.
-// Use of this source code is governed by the MIT License that can be found in
-// the LICENSE file.
-
-package envconfig
-
-import (
- "encoding"
- "fmt"
- "io"
- "os"
- "reflect"
- "strconv"
- "strings"
- "text/tabwriter"
- "text/template"
-)
-
-const (
- // DefaultListFormat constant to use to display usage in a list format
- DefaultListFormat = `This application is configured via the environment. The following environment
-variables can be used:
-{{range .}}
-{{usage_key .}}
- [description] {{usage_description .}}
- [type] {{usage_type .}}
- [default] {{usage_default .}}
- [required] {{usage_required .}}{{end}}
-`
- // DefaultTableFormat constant to use to display usage in a tabular format
- DefaultTableFormat = `This application is configured via the environment. The following environment
-variables can be used:
-
-KEY TYPE DEFAULT REQUIRED DESCRIPTION
-{{range .}}{{usage_key .}} {{usage_type .}} {{usage_default .}} {{usage_required .}} {{usage_description .}}
-{{end}}`
-)
-
-var (
- decoderType = reflect.TypeOf((*Decoder)(nil)).Elem()
- setterType = reflect.TypeOf((*Setter)(nil)).Elem()
- textUnmarshalerType = reflect.TypeOf((*encoding.TextUnmarshaler)(nil)).Elem()
- binaryUnmarshalerType = reflect.TypeOf((*encoding.BinaryUnmarshaler)(nil)).Elem()
-)
-
-func implementsInterface(t reflect.Type) bool {
- return t.Implements(decoderType) ||
- reflect.PtrTo(t).Implements(decoderType) ||
- t.Implements(setterType) ||
- reflect.PtrTo(t).Implements(setterType) ||
- t.Implements(textUnmarshalerType) ||
- reflect.PtrTo(t).Implements(textUnmarshalerType) ||
- t.Implements(binaryUnmarshalerType) ||
- reflect.PtrTo(t).Implements(binaryUnmarshalerType)
-}
-
-// toTypeDescription converts Go types into a human readable description
-func toTypeDescription(t reflect.Type) string {
- switch t.Kind() {
- case reflect.Array, reflect.Slice:
- if t.Elem().Kind() == reflect.Uint8 {
- return "String"
- }
- return fmt.Sprintf("Comma-separated list of %s", toTypeDescription(t.Elem()))
- case reflect.Map:
- return fmt.Sprintf(
- "Comma-separated list of %s:%s pairs",
- toTypeDescription(t.Key()),
- toTypeDescription(t.Elem()),
- )
- case reflect.Ptr:
- return toTypeDescription(t.Elem())
- case reflect.Struct:
- if implementsInterface(t) && t.Name() != "" {
- return t.Name()
- }
- return ""
- case reflect.String:
- name := t.Name()
- if name != "" && name != "string" {
- return name
- }
- return "String"
- case reflect.Bool:
- name := t.Name()
- if name != "" && name != "bool" {
- return name
- }
- return "True or False"
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- name := t.Name()
- if name != "" && !strings.HasPrefix(name, "int") {
- return name
- }
- return "Integer"
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- name := t.Name()
- if name != "" && !strings.HasPrefix(name, "uint") {
- return name
- }
- return "Unsigned Integer"
- case reflect.Float32, reflect.Float64:
- name := t.Name()
- if name != "" && !strings.HasPrefix(name, "float") {
- return name
- }
- return "Float"
- }
- return fmt.Sprintf("%+v", t)
-}
-
-// Usage writes usage information to stdout using the default header and table format
-func Usage(prefix string, spec interface{}) error {
- // The default is to output the usage information as a table
- // Create tabwriter instance to support table output
- tabs := tabwriter.NewWriter(os.Stdout, 1, 0, 4, ' ', 0)
-
- err := Usagef(prefix, spec, tabs, DefaultTableFormat)
- tabs.Flush()
- return err
-}
-
-// Usagef writes usage information to the specified io.Writer using the specified template specification
-func Usagef(prefix string, spec interface{}, out io.Writer, format string) error {
-
- // Specify the default usage template functions
- functions := template.FuncMap{
- "usage_key": func(v varInfo) string { return v.Key },
- "usage_description": func(v varInfo) string { return v.Tags.Get("desc") },
- "usage_type": func(v varInfo) string { return toTypeDescription(v.Field.Type()) },
- "usage_default": func(v varInfo) string { return v.Tags.Get("default") },
- "usage_required": func(v varInfo) (string, error) {
- req := v.Tags.Get("required")
- if req != "" {
- reqB, err := strconv.ParseBool(req)
- if err != nil {
- return "", err
- }
- if reqB {
- req = "true"
- }
- }
- return req, nil
- },
- }
-
- tmpl, err := template.New("envconfig").Funcs(functions).Parse(format)
- if err != nil {
- return err
- }
-
- return Usaget(prefix, spec, out, tmpl)
-}
-
-// Usaget writes usage information to the specified io.Writer using the specified template
-func Usaget(prefix string, spec interface{}, out io.Writer, tmpl *template.Template) error {
- // gather first
- infos, err := gatherInfo(prefix, spec)
- if err != nil {
- return err
- }
-
- return tmpl.Execute(out, infos)
-}
diff --git a/toolkit/envconfig/usage_test.go b/toolkit/envconfig/usage_test.go
deleted file mode 100644
index c34b3dc1..00000000
--- a/toolkit/envconfig/usage_test.go
+++ /dev/null
@@ -1,155 +0,0 @@
-// Copyright (c) 2016 Kelsey Hightower and others. All rights reserved.
-// Use of this source code is governed by the MIT License that can be found in
-// the LICENSE file.
-
-package envconfig
-
-import (
- "bytes"
- "io"
- "io/ioutil"
- "log"
- "os"
- "strings"
- "testing"
- "text/tabwriter"
-)
-
-var testUsageTableResult, testUsageListResult, testUsageCustomResult, testUsageBadFormatResult string
-
-func TestMain(m *testing.M) {
-
- // Load the expected test results from a text file
- data, err := ioutil.ReadFile("testdata/default_table.txt")
- if err != nil {
- log.Fatal(err)
- }
- testUsageTableResult = string(data)
-
- data, err = ioutil.ReadFile("testdata/default_list.txt")
- if err != nil {
- log.Fatal(err)
- }
- testUsageListResult = string(data)
-
- data, err = ioutil.ReadFile("testdata/custom.txt")
- if err != nil {
- log.Fatal(err)
- }
- testUsageCustomResult = string(data)
-
- data, err = ioutil.ReadFile("testdata/fault.txt")
- if err != nil {
- log.Fatal(err)
- }
- testUsageBadFormatResult = string(data)
-
- retCode := m.Run()
- os.Exit(retCode)
-}
-
-func compareUsage(want, got string, t *testing.T) {
- got = strings.ReplaceAll(got, " ", ".")
- if want != got {
- shortest := len(want)
- if len(got) < shortest {
- shortest = len(got)
- }
- if len(want) != len(got) {
- t.Errorf("expected result length of %d, found %d", len(want), len(got))
- }
- for i := 0; i < shortest; i++ {
- if want[i] != got[i] {
- t.Errorf("difference at index %d, expected '%c' (%v), found '%c' (%v)\n",
- i, want[i], want[i], got[i], got[i])
- break
- }
- }
- t.Errorf("Complete Expected:\n'%s'\nComplete Found:\n'%s'\n", want, got)
- }
-}
-
-func TestUsageDefault(t *testing.T) {
- var s Specification
- os.Clearenv()
- save := os.Stdout
- r, w, _ := os.Pipe()
- os.Stdout = w
- err := Usage("env_config", &s)
- outC := make(chan string)
- // copy the output in a separate goroutine so printing can't block indefinitely
- go func() {
- var buf bytes.Buffer
- io.Copy(&buf, r)
- outC <- buf.String()
- }()
- w.Close()
- os.Stdout = save // restoring the real stdout
- out := <-outC
-
- if err != nil {
- t.Error(err.Error())
- }
- compareUsage(testUsageTableResult, out, t)
-}
-
-func TestUsageTable(t *testing.T) {
- var s Specification
- os.Clearenv()
- buf := new(bytes.Buffer)
- tabs := tabwriter.NewWriter(buf, 1, 0, 4, ' ', 0)
- err := Usagef("env_config", &s, tabs, DefaultTableFormat)
- tabs.Flush()
- if err != nil {
- t.Error(err.Error())
- }
- compareUsage(testUsageTableResult, buf.String(), t)
-}
-
-func TestUsageList(t *testing.T) {
- var s Specification
- os.Clearenv()
- buf := new(bytes.Buffer)
- err := Usagef("env_config", &s, buf, DefaultListFormat)
- if err != nil {
- t.Error(err.Error())
- }
- compareUsage(testUsageListResult, buf.String(), t)
-}
-
-func TestUsageCustomFormat(t *testing.T) {
- var s Specification
- os.Clearenv()
- buf := new(bytes.Buffer)
- err := Usagef("env_config", &s, buf, "{{range .}}{{usage_key .}}={{usage_description .}}\n{{end}}")
- if err != nil {
- t.Error(err.Error())
- }
- compareUsage(testUsageCustomResult, buf.String(), t)
-}
-
-func TestUsageUnknownKeyFormat(t *testing.T) {
- var s Specification
- unknownError := "template: envconfig:1:2: executing \"envconfig\" at <.UnknownKey>"
- os.Clearenv()
- buf := new(bytes.Buffer)
- err := Usagef("env_config", &s, buf, "{{.UnknownKey}}")
- if err == nil {
- t.Errorf("expected 'unknown key' error, but got no error")
- }
- if !strings.Contains(err.Error(), unknownError) {
- t.Errorf("expected '%s', but got '%s'", unknownError, err.Error())
- }
-}
-
-func TestUsageBadFormat(t *testing.T) {
- var s Specification
- os.Clearenv()
- // If you don't use two {{}} then you get a lieteral
- buf := new(bytes.Buffer)
- err := Usagef("env_config", &s, buf, "{{range .}}{.Key}\n{{end}}")
- if err != nil {
- t.Error(err.Error())
- }
- compareUsage(testUsageBadFormatResult, buf.String(), t)
-}
diff --git a/toolkit/errorx/errorx.go b/toolkit/errorx/errorx.go
deleted file mode 100644
index d1011925..00000000
--- a/toolkit/errorx/errorx.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package errorx
-
-import (
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
-)
-
-func Panic(msg string) {
- zlogger.Panic().Msg(msg)
-}
-
-func Wrap(err error) error {
- if err == nil {
- return nil
- }
- zlogger.Error().Err(err).Msg(err.Error())
- return err
-}
diff --git a/toolkit/executils/executils.go b/toolkit/executils/executils.go
deleted file mode 100644
index e0584b02..00000000
--- a/toolkit/executils/executils.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package executils
-
-import (
- "os"
- "os/exec"
-)
-
-//go:generate mockgen -destination ../../mock/mock_executils_runner.go -package mock -source=./executils.go
-
-// Runner is mainly for executing shell command
-type Runner interface {
- Run(string, ...string) error
- Start(string, ...string) (*exec.Cmd, error)
- Output(string, ...string) ([]byte, error)
-}
-
-// CmdRunner implements Runner interface
-type CmdRunner struct{}
-
-func (r CmdRunner) Output(command string, args ...string) ([]byte, error) {
- return exec.Command(command, args...).Output()
-}
-
-// Run executes commands
-func (r CmdRunner) Run(command string, args ...string) error {
- cmd := exec.Command(command, args...)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- return cmd.Run()
-}
-
-// Start starts the specified command but does not wait for it to complete.
-func (r CmdRunner) Start(command string, args ...string) (*exec.Cmd, error) {
- cmd := exec.Command(command, args...)
- cmd.Stdout = os.Stdout
- cmd.Stderr = os.Stderr
- return cmd, cmd.Start()
-}
diff --git a/toolkit/executils/executils_test.go b/toolkit/executils/executils_test.go
deleted file mode 100644
index b112daca..00000000
--- a/toolkit/executils/executils_test.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package executils
-
-import (
- "fmt"
- "os"
- "testing"
-)
-
-func TestCmdRunner_Run(t *testing.T) {
- var runner CmdRunner
- cs := []string{"-test.run=TestHelperProcess", "--"}
- runner.Run(os.Args[0], cs...)
-}
-
-func TestCmdRunner_Start(t *testing.T) {
- var runner CmdRunner
- cs := []string{"-test.run=TestHelperProcess", "--"}
- cmd, _ := runner.Start(os.Args[0], cs...)
- if err := cmd.Wait(); err != nil {
- panic(err)
- }
-}
-
-func TestHelperProcess(*testing.T) {
- fmt.Println("testing helper process")
-}
diff --git a/toolkit/fileutils/fileutils.go b/toolkit/fileutils/fileutils.go
deleted file mode 100644
index b156935d..00000000
--- a/toolkit/fileutils/fileutils.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package fileutils
-
-import (
- "bufio"
- "io"
- "os"
-
- "github.com/mholt/archiver/v3"
- "github.com/pkg/errors"
-)
-
-// CreateDirectory dir didn't exist, then create dir, otherwise do nothing.
-func CreateDirectory(dir string) (err error) {
- var info os.FileInfo
- if info, err = os.Stat(dir); err != nil {
- if os.IsNotExist(err) {
- if err = os.MkdirAll(dir, 0755); err != nil {
- return
- }
- }
- } else {
- if !info.IsDir() {
- return errors.New("not a directory: " + dir)
- }
- }
- return
-}
-
-func File2lines(filePath string) ([]string, error) {
- f, err := os.Open(filePath)
- if err != nil {
- return nil, err
- }
- defer f.Close()
- return LinesFromReader(f)
-}
-
-func LinesFromReader(r io.Reader) ([]string, error) {
- var lines []string
- scanner := bufio.NewScanner(r)
- for scanner.Scan() {
- lines = append(lines, scanner.Text())
- }
- if err := scanner.Err(); err != nil {
- return nil, err
- }
-
- return lines, nil
-}
-
-func Archive(output string, sources ...string) error {
- return archiver.Archive(sources, output)
-}
diff --git a/toolkit/fileutils/fileutils_test.go b/toolkit/fileutils/fileutils_test.go
deleted file mode 100644
index c1f2cf0e..00000000
--- a/toolkit/fileutils/fileutils_test.go
+++ /dev/null
@@ -1,42 +0,0 @@
-package fileutils
-
-import (
- "testing"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
-)
-
-func TestCreateDirectory(t *testing.T) {
- type args struct {
- dir string
- }
- tests := []struct {
- name string
- args args
- wantErr bool
- }{
- {
- name: "",
- args: args{
- dir: pathutils.Abs("testfiles"),
- },
- // it should have error because testfiles has already existed as a file not a directory
- wantErr: true,
- },
- {
- name: "",
- args: args{
- dir: "/TestCreateDirectory",
- },
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if err := CreateDirectory(tt.args.dir); (err != nil) != tt.wantErr {
- t.Errorf("CreateDirectory() error = %v, wantErr %v", err, tt.wantErr)
- }
- //defer os.RemoveAll(dir)
- })
- }
-}
diff --git a/toolkit/fileutils/testfiles b/toolkit/fileutils/testfiles
deleted file mode 100644
index e69de29b..00000000
diff --git a/toolkit/form/.gitignore b/toolkit/form/.gitignore
deleted file mode 100644
index aa19ee28..00000000
--- a/toolkit/form/.gitignore
+++ /dev/null
@@ -1,28 +0,0 @@
-# Compiled Object files, Static and Dynamic libs (Shared Objects)
-*.o
-*.a
-*.so
-
-# Folders
-_obj
-_test
-
-# Architecture specific extensions/prefixes
-*.[568vq]
-[568vq].out
-
-*.cgo1.go
-*.cgo2.c
-_cgo_defun.c
-_cgo_gotypes.go
-_cgo_export.*
-
-_testmain.go
-
-*.exe
-*.test
-*.prof
-old.txt
-new.txt
-
-/.idea
\ No newline at end of file
diff --git a/toolkit/form/LICENSE b/toolkit/form/LICENSE
deleted file mode 100644
index 8d8aba15..00000000
--- a/toolkit/form/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2016 Go Playground
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/toolkit/form/Makefile b/toolkit/form/Makefile
deleted file mode 100644
index 0ff37bce..00000000
--- a/toolkit/form/Makefile
+++ /dev/null
@@ -1,13 +0,0 @@
-all: lint test bench
-
-lint:
- golangci-lint run --timeout 5m
-
-test:
- go test -covermode=atomic -race ./...
-
-bench:
- go test -bench=. -benchmem ./...
-
-.PHONY: test lint bench
-.DEFAULT_GOAL := all
diff --git a/toolkit/form/README.md b/toolkit/form/README.md
deleted file mode 100644
index 2e76aeea..00000000
--- a/toolkit/form/README.md
+++ /dev/null
@@ -1,323 +0,0 @@
-Package form
-============
-![Project status](https://img.shields.io/badge/version-4.2.1-green.svg)
-[![Build Status](https://github.com/go-playground/form/actions/workflows/workflow.yml/badge.svg)](https://github.com/go-playground/form/actions/workflows/workflow.yml)
-[![Coverage Status](https://coveralls.io/repos/github/go-playground/form/badge.svg?branch=master)](https://coveralls.io/github/go-playground/form?branch=master)
-[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/form)](https://goreportcard.com/report/github.com/go-playground/form)
-[![GoDoc](https://godoc.org/github.com/go-playground/form?status.svg)](https://godoc.org/github.com/go-playground/form)
-![License](https://img.shields.io/dub/l/vibe-d.svg)
-[![Gitter](https://badges.gitter.im/go-playground/form.svg)](https://gitter.im/go-playground/form?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
-
-Package form Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values.
-
-It has the following features:
-
-- Supports map of almost all types.
-- Supports both Numbered and Normal arrays eg. `"Array[0]"` and just `"Array"` with multiple values passed.
-- Slice honours the specified index. eg. if "Slice[2]" is the only Slice value passed down, it will be put at index 2; if slice isn't big enough it will be expanded.
-- Array honours the specified index. eg. if "Array[2]" is the only Array value passed down, it will be put at index 2; if array isn't big enough a warning will be printed and value ignored.
-- Only creates objects as necessary eg. if no `array` or `map` values are passed down, the `array` and `map` are left as their default values in the struct.
-- Allows for Custom Type registration.
-- Handles time.Time using RFC3339 time format by default, but can easily be changed by registering a Custom Type, see below.
-- Handles Encoding & Decoding of almost all Go types eg. can Decode into struct, array, map, int... and Encode a struct, array, map, int...
-
-Common Questions
-
-- Does it support encoding.TextUnmarshaler? No because TextUnmarshaler only accepts []byte but posted values can have multiple values, so is not suitable.
-- Mixing `array/slice` with `array[idx]/slice[idx]`, in which order are they parsed? `array/slice` then `array[idx]/slice[idx]`
-
-Supported Types ( out of the box )
-----------
-
-* `string`
-* `bool`
-* `int`, `int8`, `int16`, `int32`, `int64`
-* `uint`, `uint8`, `uint16`, `uint32`, `uint64`
-* `float32`, `float64`
-* `struct` and `anonymous struct`
-* `interface{}`
-* `time.Time` - by default using RFC3339
-* a `pointer` to one of the above types
-* `slice`, `array`
-* `map`
-* `custom types` can override any of the above types
-* many other types may be supported inherently
-
-**NOTE**: `map`, `struct` and `slice` nesting are ad infinitum.
-
-Installation
-------------
-
-Use go get.
-
- go get github.com/go-playground/form
-
-Then import the form package into your own code.
-
- import "github.com/go-playground/form/v4"
-
-Usage
------
-
-- Use symbol `.` for separating fields/structs. (eg. `structfield.field`)
-- Use `[index or key]` for access to index of a slice/array or key for map. (eg. `arrayfield[0]`, `mapfield[keyvalue]`)
-
-```html
-
-```
-
-Examples
--------
-
-Decoding
-```go
-package main
-
-import (
- "fmt"
- "log"
- "net/url"
-
- "github.com/go-playground/form/v4"
-)
-
-// Address contains address information
-type Address struct {
- Name string
- Phone string
-}
-
-// User contains user information
-type User struct {
- Name string
- Age uint8
- Gender string
- Address []Address
- Active bool `form:"active"`
- MapExample map[string]string
- NestedMap map[string]map[string]string
- NestedArray [][]string
-}
-
-// use a single instance of Decoder, it caches struct info
-var decoder *form.Decoder
-
-func main() {
- decoder = form.NewDecoder()
-
- // this simulates the results of http.Request's ParseForm() function
- values := parseForm()
-
- var user User
-
- // must pass a pointer
- err := decoder.Decode(&user, values)
- if err != nil {
- log.Panic(err)
- }
-
- fmt.Printf("%#v\n", user)
-}
-
-// this simulates the results of http.Request's ParseForm() function
-func parseForm() url.Values {
- return url.Values{
- "Name": []string{"joeybloggs"},
- "Age": []string{"3"},
- "Gender": []string{"Male"},
- "Address[0].Name": []string{"26 Here Blvd."},
- "Address[0].Phone": []string{"9(999)999-9999"},
- "Address[1].Name": []string{"26 There Blvd."},
- "Address[1].Phone": []string{"1(111)111-1111"},
- "active": []string{"true"},
- "MapExample[key]": []string{"value"},
- "NestedMap[key][key]": []string{"value"},
- "NestedArray[0][0]": []string{"value"},
- }
-}
-```
-
-Encoding
-```go
-package main
-
-import (
- "fmt"
- "log"
-
- "github.com/go-playground/form/v4"
-)
-
-// Address contains address information
-type Address struct {
- Name string
- Phone string
-}
-
-// User contains user information
-type User struct {
- Name string
- Age uint8
- Gender string
- Address []Address
- Active bool `form:"active"`
- MapExample map[string]string
- NestedMap map[string]map[string]string
- NestedArray [][]string
-}
-
-// use a single instance of Encoder, it caches struct info
-var encoder *form.Encoder
-
-func main() {
- encoder = form.NewEncoder()
-
- user := User{
- Name: "joeybloggs",
- Age: 3,
- Gender: "Male",
- Address: []Address{
- {Name: "26 Here Blvd.", Phone: "9(999)999-9999"},
- {Name: "26 There Blvd.", Phone: "1(111)111-1111"},
- },
- Active: true,
- MapExample: map[string]string{"key": "value"},
- NestedMap: map[string]map[string]string{"key": {"key": "value"}},
- NestedArray: [][]string{{"value"}},
- }
-
- // must pass a pointer
- values, err := encoder.Encode(&user)
- if err != nil {
- log.Panic(err)
- }
-
- fmt.Printf("%#v\n", values)
-}
-```
-
-Registering Custom Types
---------------
-
-Decoder
-```go
-decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {
- return time.Parse("2006-01-02", vals[0])
-}, time.Time{})
-```
-ADDITIONAL: if a struct type is registered, the function will only be called if a url.Value exists for
-the struct and not just the struct fields eg. url.Values{"User":"Name%3Djoeybloggs"} will call the
-custom type function with 'User' as the type, however url.Values{"User.Name":"joeybloggs"} will not.
-
-
-Encoder
-```go
-encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {
- return []string{x.(time.Time).Format("2006-01-02")}, nil
-}, time.Time{})
-```
-
-Ignoring Fields
---------------
-you can tell form to ignore fields using `-` in the tag
-```go
-type MyStruct struct {
- Field string `form:"-"`
-}
-```
-
-Omitempty
---------------
-you can tell form to omit empty fields using `,omitempty` or `FieldName,omitempty` in the tag
-```go
-type MyStruct struct {
- Field string `form:",omitempty"`
- Field2 string `form:"CustomFieldName,omitempty"`
-}
-```
-
-Notes
-------
-To maximize compatibility with other systems the Encoder attempts
-to avoid using array indexes in url.Values if at all possible.
-
-eg.
-```go
-// A struct field of
-Field []string{"1", "2", "3"}
-
-// will be output a url.Value as
-"Field": []string{"1", "2", "3"}
-
-and not
-"Field[0]": []string{"1"}
-"Field[1]": []string{"2"}
-"Field[2]": []string{"3"}
-
-// however there are times where it is unavoidable, like with pointers
-i := int(1)
-Field []*string{nil, nil, &i}
-
-// to avoid index 1 and 2 must use index
-"Field[2]": []string{"1"}
-```
-
-Benchmarks
-------
-###### Run on M1 MacBook Pro using go version go1.20.6 darwin/amd64
-
-NOTE: the 1 allocation and B/op in the first 4 decodes is actually the struct allocating when passing it in, so primitives are actually zero allocation.
-
-```go
-go test -run=NONE -bench=. -benchmem=true ./...
-goos: darwin
-goarch: arm64
-pkg: github.com/go-playground/form/v4/benchmarks
-BenchmarkSimpleUserDecodeStruct-8 8704111 121.1 ns/op 64 B/op 1 allocs/op
-BenchmarkSimpleUserDecodeStructParallel-8 35916134 32.89 ns/op 64 B/op 1 allocs/op
-BenchmarkSimpleUserEncodeStruct-8 3746173 320.7 ns/op 485 B/op 10 allocs/op
-BenchmarkSimpleUserEncodeStructParallel-8 7293147 180.0 ns/op 485 B/op 10 allocs/op
-BenchmarkPrimitivesDecodeStructAllPrimitivesTypes-8 2993259 400.5 ns/op 96 B/op 1 allocs/op
-BenchmarkPrimitivesDecodeStructAllPrimitivesTypesParallel-8 13023300 97.70 ns/op 96 B/op 1 allocs/op
-BenchmarkPrimitivesEncodeStructAllPrimitivesTypes-8 643202 1767 ns/op 2977 B/op 35 allocs/op
-BenchmarkPrimitivesEncodeStructAllPrimitivesTypesParallel-8 1000000 1202 ns/op 2978 B/op 35 allocs/op
-BenchmarkComplexArrayDecodeStructAllTypes-8 172630 6822 ns/op 2008 B/op 121 allocs/op
-BenchmarkComplexArrayDecodeStructAllTypesParallel-8 719788 1735 ns/op 2009 B/op 121 allocs/op
-BenchmarkComplexArrayEncodeStructAllTypes-8 197052 5839 ns/op 7087 B/op 104 allocs/op
-BenchmarkComplexArrayEncodeStructAllTypesParallel-8 348039 3247 ns/op 7089 B/op 104 allocs/op
-BenchmarkComplexMapDecodeStructAllTypes-8 139246 8550 ns/op 5313 B/op 130 allocs/op
-BenchmarkComplexMapDecodeStructAllTypesParallel-8 409018 3143 ns/op 5317 B/op 130 allocs/op
-BenchmarkComplexMapEncodeStructAllTypes-8 208833 5515 ns/op 4257 B/op 103 allocs/op
-BenchmarkComplexMapEncodeStructAllTypesParallel-8 523833 2182 ns/op 4258 B/op 103 allocs/op
-BenchmarkDecodeNestedStruct-8 807690 1408 ns/op 344 B/op 14 allocs/op
-BenchmarkDecodeNestedStructParallel-8 3409441 359.6 ns/op 344 B/op 14 allocs/op
-BenchmarkEncodeNestedStruct-8 1488520 803.6 ns/op 653 B/op 16 allocs/op
-BenchmarkEncodeNestedStructParallel-8 3570204 346.6 ns/op 653 B/op 16 allocs/op
-```
-
-Competitor benchmarks can be found [here](https://github.com/go-playground/form/blob/master/benchmarks/benchmarks.md)
-
-Complimentary Software
-----------------------
-
-Here is a list of software that compliments using this library post decoding.
-
-* [Validator](https://github.com/go-playground/validator) - Go Struct and Field validation, including Cross Field, Cross Struct, Map, Slice and Array diving.
-* [mold](https://github.com/go-playground/mold) - Is a general library to help modify or set data within data structures and other objects.
-
-License
-------
-Distributed under MIT License, please see license file in code for more details.
diff --git a/toolkit/form/_examples/decoder-embedded/main.go b/toolkit/form/_examples/decoder-embedded/main.go
deleted file mode 100644
index c75fab2b..00000000
--- a/toolkit/form/_examples/decoder-embedded/main.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package main
-
-import (
- "fmt"
- "log"
- "net/url"
-
- "github.com/go-playground/form/v4"
-)
-
-// A ...
-type A struct {
- Field string
-}
-
-// B ...
-type B struct {
- A
- Field string
-}
-
-// use a single instance of Decoder, it caches struct info
-var decoder *form.Decoder
-
-func main() {
- decoder = form.NewDecoder()
-
- // this simulates the results of http.Request's ParseForm() function
- values := parseFormB()
-
- var b B
-
- // must pass a pointer
- err := decoder.Decode(&b, values)
- if err != nil {
- log.Panic(err)
- }
-
- fmt.Printf("%#v\n", b)
-
- values = parseFormAB()
-
- // must pass a pointer
- err = decoder.Decode(&b, values)
- if err != nil {
- log.Panic(err)
- }
-
- fmt.Printf("%#v\n", b)
-}
-
-// this simulates the results of http.Request's ParseForm() function
-func parseFormB() url.Values {
- return url.Values{
- "Field": []string{"B FieldVal"},
- }
-}
-
-// this simulates the results of http.Request's ParseForm() function
-func parseFormAB() url.Values {
- return url.Values{
- "Field": []string{"B FieldVal"},
- "A.Field": []string{"A FieldVal"},
- }
-}
diff --git a/toolkit/form/_examples/decoder/full.go b/toolkit/form/_examples/decoder/full.go
deleted file mode 100644
index 6028dbf4..00000000
--- a/toolkit/form/_examples/decoder/full.go
+++ /dev/null
@@ -1,173 +0,0 @@
-package main
-
-import (
- "fmt"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/form"
- "log"
- "net/url"
-)
-
-//
-
-// Address contains address information
-type Address struct {
- Name string
- Phone string
-}
-
-// User contains user information
-type User struct {
- Name string
- Age uint8
- Gender string
- Address []Address
- Active bool `form:"active"`
- MapExample map[string]string
- NestedMap map[string]map[string]string
- NestedArray [][]string
- Extra map[string]interface{} `form:"+"`
- Others struct {
- Other1 int
- Other2 int
- Others map[string]interface{} `form:"+"`
- }
- Leader *User
-}
-
-// use a single instance of Decoder, it caches struct info
-var decoder *form.Decoder
-
-func main() {
- decoder = form.NewDecoder()
- decoder.SetNamespacePrefix("[")
- decoder.SetNamespaceSuffix("]")
-
- // this simulates the results of http.Request's ParseForm() function
- values := parseForm2()
-
- for i := 0; i < 1; i++ {
- var user User
-
- // must pass a pointer
- err := decoder.Decode(&user, values)
- if err != nil {
- log.Panic(err)
- }
-
- fmt.Printf("%#v\n", user)
- }
-
- //userMap := make(map[string]interface{})
- //// must pass a pointer
- //err := decoder.Decode(&userMap, values)
- //if err != nil {
- // log.Panic(err)
- //}
- //
- //fmt.Printf("%#v\n", userMap)
-
-}
-
-// this simulates the results of http.Request's ParseForm() function
-func parseForm() url.Values {
- return url.Values{
- "Name": []string{"joeybloggs"},
- "Age": []string{"3"},
- "Gender": []string{"Male"},
- "Address[0].Name": []string{"26 Here Blvd."},
- "Address[0].Phone": []string{"9(999)999-9999"},
- "Address[1].Name": []string{"26 There Blvd."},
- "Address[1].Phone": []string{"1(111)111-1111"},
- "active": []string{"true"},
- "MapExample[key]": []string{"value"},
- "MapExample[key1]": []string{"value1"},
- "NestedMap[key][key]": []string{"value"},
- "NestedMap[key][key1]": []string{"value1"},
- "NestedArray[0][0]": []string{"value"},
- "other1": []string{"1"},
- "other2": []string{"2"},
- "Leader.Name": []string{"jack"},
- "Others.Other1": []string{"1"},
- "Others.Other2": []string{"2"},
- "Others[Other3]": []string{"3"},
- "Others[Other4]": []string{"4"},
- "a": []string{"5"},
- "b": []string{"b"},
- "c[0]": []string{"c"},
- "c[1]": []string{"d"},
- "c[2]": []string{"e"},
- "d[key]": []string{"value"},
- "d[key1]": []string{"value1"},
- "e[key][key]": []string{"value"},
- "e[key][key1]": []string{"value1"},
- "f[0][0]": []string{"value"},
- "f[0][1]": []string{"value1"},
- "f[1][0]": []string{"value10"},
- }
-}
-
-func parseForm2() url.Values {
- return url.Values{
- "Name": []string{"joeybloggs"},
- "Age": []string{"3"},
- "Gender": []string{"Male"},
- "Address[0][Name]": []string{"26 Here Blvd."},
- "Address[0][Phone]": []string{"9(999)999-9999"},
- "Address[1][Name]": []string{"26 There Blvd."},
- "Address[1][Phone]": []string{"1(111)111-1111"},
- "active": []string{"true"},
- "MapExample[key]": []string{"value"},
- "NestedMap[key][key]": []string{"value"},
- "NestedArray[0][0]": []string{"value"},
- "other1": []string{"1"},
- "other2": []string{"2"},
- "Leader.Name": []string{"jack"},
- "Leader[Name]": []string{"jack"},
- "Others[Other1]": []string{"1"},
- "Others[Other2]": []string{"2"},
- "Others[Other3]": []string{"3"},
- "Others[Other4]": []string{"4"},
- "a": []string{"5"},
- "b": []string{"b"},
- "c[0]": []string{"c"},
- "c[1]": []string{"d"},
- "c[2]": []string{"e"},
- "d[key]": []string{"value"},
- "d[key1]": []string{"value1"},
- "e[key][key]": []string{"value"},
- "e[key][key1]": []string{"value1"},
- "f[0][0]": []string{"value"},
- "f[0][1]": []string{"value1"},
- "f[1][0]": []string{"value10"},
- }
-}
-
-type A struct {
- B string `form:"b,c"`
-}
-
-type ParameterWrapper struct {
- Parameter A `json:"parameter" form:"parameter"`
-}
-
-func parseForm3() url.Values {
- // parameter[product_code]
- return url.Values{
- //"parameter[b]": []string{"b"},
- "parameter[c]": []string{"b"},
- }
-}
diff --git a/toolkit/form/_examples/encoder-embedded/main.go b/toolkit/form/_examples/encoder-embedded/main.go
deleted file mode 100644
index a0a6e8a3..00000000
--- a/toolkit/form/_examples/encoder-embedded/main.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package main
-
-import (
- "fmt"
- "log"
-
- "github.com/go-playground/form/v4"
-)
-
-// A ...
-type A struct {
- Field string
-}
-
-// B ...
-type B struct {
- A
- Field string
-}
-
-// use a single instance of Encoder, it caches struct info
-var encoder *form.Encoder
-
-func main() {
-
- type A struct {
- Field string
- }
-
- type B struct {
- A
- Field string
- }
-
- b := B{
- A: A{
- Field: "A Val",
- },
- Field: "B Val",
- }
-
- encoder = form.NewEncoder()
-
- v, err := encoder.Encode(b)
- if err != nil {
- log.Panic(err)
- }
-
- fmt.Printf("%#v\n", v)
-
- encoder.SetAnonymousMode(form.AnonymousSeparate)
- v, err = encoder.Encode(b)
- if err != nil {
- log.Panic(err)
- }
-
- fmt.Printf("%#v\n", v)
-}
diff --git a/toolkit/form/_examples/encoder-omitempty/main.go b/toolkit/form/_examples/encoder-omitempty/main.go
deleted file mode 100644
index 9a529007..00000000
--- a/toolkit/form/_examples/encoder-omitempty/main.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package main
-
-import (
- "fmt"
- "log"
-
- "github.com/go-playground/form/v4"
-)
-
-// Test ...
-type Test struct {
- String string `form:",omitempty"`
- Array []string
- Map map[string]string
-}
-
-// use a single instance of Encoder, it caches struct info
-var encoder *form.Encoder
-
-func main() {
- var t Test
-
- encoder = form.NewEncoder()
-
- values, err := encoder.Encode(t)
- if err != nil {
- log.Panic(err)
- }
-
- fmt.Printf("%#v\n", values)
-
- t.String = "String Val"
- t.Array = []string{"arr1"}
-
- values, err = encoder.Encode(t)
- if err != nil {
- log.Panic(err)
- }
-
- fmt.Printf("%#v\n", values)
-}
diff --git a/toolkit/form/_examples/encoder/full.go b/toolkit/form/_examples/encoder/full.go
deleted file mode 100644
index e3a1fd17..00000000
--- a/toolkit/form/_examples/encoder/full.go
+++ /dev/null
@@ -1,55 +0,0 @@
-package main
-
-import (
- "fmt"
- "log"
-
- "github.com/go-playground/form/v4"
-)
-
-// Address contains address information
-type Address struct {
- Name string
- Phone string
-}
-
-// User contains user information
-type User struct {
- Name string
- Age uint8
- Gender string
- Address []Address
- Active bool `form:"active"`
- MapExample map[string]string
- NestedMap map[string]map[string]string
- NestedArray [][]string
-}
-
-// use a single instance of Encoder, it caches struct info
-var encoder *form.Encoder
-
-func main() {
- encoder = form.NewEncoder()
-
- user := User{
- Name: "joeybloggs",
- Age: 3,
- Gender: "Male",
- Address: []Address{
- {Name: "26 Here Blvd.", Phone: "9(999)999-9999"},
- {Name: "26 There Blvd.", Phone: "1(111)111-1111"},
- },
- Active: true,
- MapExample: map[string]string{"key": "value"},
- NestedMap: map[string]map[string]string{"key": {"key": "value"}},
- NestedArray: [][]string{{"value"}},
- }
-
- // must pass a pointer
- values, err := encoder.Encode(&user)
- if err != nil {
- log.Panic(err)
- }
-
- fmt.Printf("%#v\n", values)
-}
diff --git a/toolkit/form/benchmarks/benchmarks.md b/toolkit/form/benchmarks/benchmarks.md
deleted file mode 100644
index 70f78671..00000000
--- a/toolkit/form/benchmarks/benchmarks.md
+++ /dev/null
@@ -1,31 +0,0 @@
-## Benchmarks
-
-All Benchmarks Last Run Nov 17, 2019
-
-Run on MacBook Pro (15-inch, 2017) using go version go1.13.4 darwin/amd64
-go test -run=NONE -bench=. -benchmem=true
-
-### go-playground/form
-```go
-BenchmarkSimpleUserDecodeStruct-8 4447569 255 ns/op 64 B/op 1 allocs/op
-BenchmarkSimpleUserDecodeStructParallel-8 14087551 77.2 ns/op 64 B/op 1 allocs/op
-BenchmarkSimpleUserEncodeStruct-8 1863354 645 ns/op 485 B/op 10 allocs/op
-BenchmarkSimpleUserEncodeStructParallel-8 5554753 208 ns/op 485 B/op 10 allocs/op
-BenchmarkPrimitivesDecodeStructAllPrimitivesTypes-8 1345276 881 ns/op 96 B/op 1 allocs/op
-BenchmarkPrimitivesDecodeStructAllPrimitivesTypesParallel-8 4729965 259 ns/op 96 B/op 1 allocs/op
-BenchmarkPrimitivesEncodeStructAllPrimitivesTypes-8 303967 3331 ns/op 2977 B/op 35 allocs/op
-BenchmarkPrimitivesEncodeStructAllPrimitivesTypesParallel-8 1094600 1077 ns/op 2978 B/op 35 allocs/op
-BenchmarkComplexArrayDecodeStructAllTypes-8 76928 14567 ns/op 2248 B/op 121 allocs/op
-BenchmarkComplexArrayDecodeStructAllTypesParallel-8 292060 4355 ns/op 2249 B/op 121 allocs/op
-BenchmarkComplexArrayEncodeStructAllTypes-8 94536 11334 ns/op 7113 B/op 104 allocs/op
-BenchmarkComplexArrayEncodeStructAllTypesParallel-8 298318 3633 ns/op 7112 B/op 104 allocs/op
-BenchmarkComplexMapDecodeStructAllTypes-8 58084 18635 ns/op 5306 B/op 130 allocs/op
-BenchmarkComplexMapDecodeStructAllTypesParallel-8 187159 5454 ns/op 5308 B/op 130 allocs/op
-BenchmarkComplexMapEncodeStructAllTypes-8 101962 11763 ns/op 6971 B/op 129 allocs/op
-BenchmarkComplexMapEncodeStructAllTypesParallel-8 312925 4185 ns/op 6970 B/op 129 allocs/op
-BenchmarkDecodeNestedStruct-8 469940 2547 ns/op 384 B/op 14 allocs/op
-BenchmarkDecodeNestedStructParallel-8 1486963 810 ns/op 384 B/op 14 allocs/op
-BenchmarkEncodeNestedStruct-8 796798 1501 ns/op 693 B/op 16 allocs/op
-BenchmarkEncodeNestedStructParallel-8 2290203 520 ns/op 693 B/op 16 allocs/op
-
-```
\ No newline at end of file
diff --git a/toolkit/form/benchmarks/benchmarks_test.go b/toolkit/form/benchmarks/benchmarks_test.go
deleted file mode 100644
index 4d4940d7..00000000
--- a/toolkit/form/benchmarks/benchmarks_test.go
+++ /dev/null
@@ -1,653 +0,0 @@
-package benchmarks
-
-import (
- "net/url"
- "testing"
-
- "github.com/go-playground/form/v4"
-)
-
-// Simple Benchmarks
-
-type User struct {
- FirstName string `form:"fname" schema:"fname" formam:"fname"`
- LastName string `form:"lname" schema:"lname" formam:"lname"`
- Email string `form:"email" schema:"email" formam:"email"`
- Age uint8 `form:"age" schema:"age" formam:"age"`
-}
-
-func getUserStructValues() url.Values {
- return url.Values{
- "fname": []string{"Joey"},
- "lname": []string{"Bloggs"},
- "email": []string{"joeybloggs@gmail.com"},
- "age": []string{"32"},
- }
-}
-
-func getUserStruct() *User {
- return &User{
- FirstName: "Joey",
- LastName: "Bloggs",
- Email: "joeybloggs@gmail.com",
- Age: 32,
- }
-}
-
-func BenchmarkSimpleUserDecodeStruct(b *testing.B) {
-
- values := getUserStructValues()
- decoder := form.NewDecoder()
-
- b.ReportAllocs()
- for n := 0; n < b.N; n++ {
- var test User
- if err := decoder.Decode(&test, values); err != nil {
- b.Error(err)
- }
- }
-}
-
-func BenchmarkSimpleUserDecodeStructParallel(b *testing.B) {
-
- values := getUserStructValues()
- decoder := form.NewDecoder()
-
- b.ReportAllocs()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- var test User
- if err := decoder.Decode(&test, values); err != nil {
- b.Error(err)
- }
- }
- })
-}
-func BenchmarkSimpleUserEncodeStruct(b *testing.B) {
-
- test := getUserStruct()
- encoder := form.NewEncoder()
-
- b.ReportAllocs()
- for n := 0; n < b.N; n++ {
- if _, err := encoder.Encode(&test); err != nil {
- b.Error(err)
- }
- }
-}
-
-func BenchmarkSimpleUserEncodeStructParallel(b *testing.B) {
-
- test := getUserStruct()
- encoder := form.NewEncoder()
-
- b.ReportAllocs()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- if _, err := encoder.Encode(&test); err != nil {
- b.Error(err)
- }
- }
- })
-}
-
-// Primitives ALL types
-
-type PrimitivesStruct struct {
- String string
- Int int
- Int8 int8
- Int16 int16
- Int32 int32
- Int64 int64
- Uint uint
- Uint8 uint8
- Uint16 uint16
- Uint32 uint32
- Uint64 uint64
- Float32 float32
- Float64 float64
- Bool bool
-}
-
-func getPrimitivesStructValues() url.Values {
- return url.Values{
- "String": []string{"joeybloggs"},
- "Int": []string{"1"},
- "Int8": []string{"2"},
- "Int16": []string{"3"},
- "Int32": []string{"4"},
- "Int64": []string{"5"},
- "Uint": []string{"1"},
- "Uint8": []string{"2"},
- "Uint16": []string{"3"},
- "Uint32": []string{"4"},
- "Uint64": []string{"5"},
- "Float32": []string{"1.1"},
- "Float64": []string{"5.0"},
- "Bool": []string{"true"},
- }
-}
-
-func getPrimitivesStruct() *PrimitivesStruct {
- return &PrimitivesStruct{
- String: "joeybloggs",
- Int: 1,
- Int8: 2,
- Int16: 3,
- Int32: 4,
- Int64: 5,
- Uint: 1,
- Uint8: 2,
- Uint16: 3,
- Uint32: 4,
- Uint64: 5,
- Float32: 1.1,
- Float64: 5.0,
- Bool: true,
- }
-}
-
-func BenchmarkPrimitivesDecodeStructAllPrimitivesTypes(b *testing.B) {
- values := getPrimitivesStructValues()
- decoder := form.NewDecoder()
-
- b.ReportAllocs()
- for n := 0; n < b.N; n++ {
- var test PrimitivesStruct
- if err := decoder.Decode(&test, values); err != nil {
- b.Error(err)
- }
- }
-}
-
-func BenchmarkPrimitivesDecodeStructAllPrimitivesTypesParallel(b *testing.B) {
- values := getPrimitivesStructValues()
- decoder := form.NewDecoder()
-
- b.ReportAllocs()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- var test PrimitivesStruct
- if err := decoder.Decode(&test, values); err != nil {
- b.Error(err)
- }
- }
- })
-}
-
-func BenchmarkPrimitivesEncodeStructAllPrimitivesTypes(b *testing.B) {
- test := getPrimitivesStruct()
- encoder := form.NewEncoder()
-
- b.ReportAllocs()
- for n := 0; n < b.N; n++ {
- if _, err := encoder.Encode(&test); err != nil {
- b.Error(err)
- }
- }
-}
-
-func BenchmarkPrimitivesEncodeStructAllPrimitivesTypesParallel(b *testing.B) {
- test := getPrimitivesStruct()
- encoder := form.NewEncoder()
-
- b.ReportAllocs()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- if _, err := encoder.Encode(&test); err != nil {
- b.Error(err)
- }
- }
- })
-}
-
-// Complex Array ALL types
-
-type ComplexArrayStruct struct {
- String []string
- StringPtr []*string
- Int []int
- IntPtr []*int
- Int8 []int8
- Int8Ptr []*int8
- Int16 []int16
- Int16Ptr []*int16
- Int32 []int32
- Int32Ptr []*int32
- Int64 []int64
- Int64Ptr []*int64
- Uint []uint
- UintPtr []*uint
- Uint8 []uint8
- Uint8Ptr []*uint8
- Uint16 []uint16
- Uint16Ptr []*uint16
- Uint32 []uint32
- Uint32Ptr []*uint32
- Uint64 []uint64
- Uint64Ptr []*uint64
- NestedInt [][]int
- NestedIntPtr [][]*int
-}
-
-func getComplexArrayStructValues() url.Values {
- return url.Values{
- "String": []string{"joeybloggs"},
- "StringPtr": []string{"joeybloggs"},
- "Int": []string{"1", "2"},
- "IntPtr": []string{"1", "2"},
- "Int8[0]": []string{"1"},
- "Int8[1]": []string{"2"},
- "Int8Ptr[0]": []string{"1"},
- "Int8Ptr[1]": []string{"2"},
- "Int16": []string{"1", "2"},
- "Int16Ptr": []string{"1", "2"},
- "Int32": []string{"1", "2"},
- "Int32Ptr": []string{"1", "2"},
- "Int64": []string{"1", "2"},
- "Int64Ptr": []string{"1", "2"},
- "Uint": []string{"1", "2"},
- "UintPtr": []string{"1", "2"},
- "Uint8[0]": []string{"1"},
- "Uint8[1]": []string{"2"},
- "Uint8Ptr[0]": []string{"1"},
- "Uint8Ptr[1]": []string{"2"},
- "Uint16": []string{"1", "2"},
- "Uint16Ptr": []string{"1", "2"},
- "Uint32": []string{"1", "2"},
- "Uint32Ptr": []string{"1", "2"},
- "Uint64": []string{"1", "2"},
- "Uint64Ptr": []string{"1", "2"},
- "NestedInt[0][0]": []string{"1"},
- "NestedIntPtr[0][1]": []string{"1"},
- }
-}
-
-func getComplexArrayStruct() *ComplexArrayStruct {
-
- s := "joeybloggs"
-
- i1 := int(1)
- i2 := int(2)
- i81 := int8(1)
- i82 := int8(2)
- i161 := int16(1)
- i162 := int16(2)
- i321 := int32(1)
- i322 := int32(2)
- i641 := int64(1)
- i642 := int64(2)
-
- ui1 := uint(1)
- ui2 := uint(2)
- ui81 := uint8(1)
- ui82 := uint8(2)
- ui161 := uint16(1)
- ui162 := uint16(2)
- ui321 := uint32(1)
- ui322 := uint32(2)
- ui641 := uint64(1)
- ui642 := uint64(2)
-
- return &ComplexArrayStruct{
- String: []string{s},
- StringPtr: []*string{&s},
- Int: []int{i1, i2},
- IntPtr: []*int{&i1, &i2},
- Int8: []int8{i81, i82},
- Int8Ptr: []*int8{&i81, &i82},
- Int16: []int16{i161, i162},
- Int16Ptr: []*int16{&i161, &i162},
- Int32: []int32{i321, i322},
- Int32Ptr: []*int32{&i321, &i322},
- Int64: []int64{i641, i642},
- Int64Ptr: []*int64{&i641, &i642},
- Uint: []uint{ui1, ui2},
- UintPtr: []*uint{&ui1, &ui2},
- Uint8: []uint8{ui81, ui82},
- Uint8Ptr: []*uint8{&ui81, &ui82},
- Uint16: []uint16{ui161, ui162},
- Uint16Ptr: []*uint16{&ui161, &ui162},
- Uint32: []uint32{ui321, ui322},
- Uint32Ptr: []*uint32{&ui321, &ui322},
- Uint64: []uint64{ui641, ui642},
- Uint64Ptr: []*uint64{&ui641, &ui642},
- NestedInt: [][]int{{i1}},
- NestedIntPtr: [][]*int{nil, {&i1}},
- }
-}
-
-func BenchmarkComplexArrayDecodeStructAllTypes(b *testing.B) {
- values := getComplexArrayStructValues()
- decoder := form.NewDecoder()
-
- b.ReportAllocs()
- for n := 0; n < b.N; n++ {
- var test ComplexArrayStruct
- if err := decoder.Decode(&test, values); err != nil {
- b.Error(err)
- }
- }
-}
-
-func BenchmarkComplexArrayDecodeStructAllTypesParallel(b *testing.B) {
- values := getComplexArrayStructValues()
- decoder := form.NewDecoder()
-
- b.ReportAllocs()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- var test ComplexArrayStruct
- if err := decoder.Decode(&test, values); err != nil {
- b.Error(err)
- }
- }
- })
-}
-
-func BenchmarkComplexArrayEncodeStructAllTypes(b *testing.B) {
- test := getComplexArrayStruct()
- encoder := form.NewEncoder()
-
- b.ReportAllocs()
- for n := 0; n < b.N; n++ {
- if _, err := encoder.Encode(&test); err != nil {
- b.Error(err)
- }
- }
-}
-
-func BenchmarkComplexArrayEncodeStructAllTypesParallel(b *testing.B) {
- test := getComplexArrayStruct()
- encoder := form.NewEncoder()
-
- b.ReportAllocs()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- if _, err := encoder.Encode(&test); err != nil {
- b.Error(err)
- }
- }
- })
-}
-
-// Complex Map ALL types
-
-type ComplexMapStruct struct {
- String map[string]string
- StringPtr map[*string]*string
- Int map[int]int
- IntPtr map[*int]*int
- Int8 map[int8]int8
- Int8Ptr map[*int8]*int8
- Int16 map[int16]int16
- Int16Ptr map[*int16]*int16
- Int32 map[int32]int32
- Int32Ptr map[*int32]*int32
- Int64 map[int64]int64
- Int64Ptr map[*int64]*int64
- Uint map[uint]uint
- UintPtr map[*uint]*uint
- Uint8 map[uint8]uint8
- Uint8Ptr map[*uint8]*uint8
- Uint16 map[uint16]uint16
- Uint16Ptr map[*uint16]*uint16
- Uint32 map[uint32]uint32
- Uint32Ptr map[*uint32]*uint32
- Uint64 map[uint64]uint64
- Uint64Ptr map[*uint64]*uint64
- NestedInt map[int]map[int]int
- NestedIntPtr map[*int]map[*int]*int
-}
-
-func getComplexMapStructValues() url.Values {
- return url.Values{
- "String[key]": []string{"value"},
- "StringPtr[key]": []string{"value"},
- "Int[0]": []string{"1"},
- "IntPtr[0]": []string{"1"},
- "Int8[0]": []string{"1"},
- "Int8Ptr[0]": []string{"1"},
- "Int16[0]": []string{"1"},
- "Int16Ptr[0]": []string{"1"},
- "Int32[0]": []string{"1"},
- "Int32Ptr[0]": []string{"1"},
- "Int64[0]": []string{"1"},
- "Int64Ptr[0]": []string{"1"},
- "Uint[0]": []string{"1"},
- "UintPtr[0]": []string{"1"},
- "Uint8[0]": []string{"1"},
- "Uint8Ptr[0]": []string{"1"},
- "Uint16[0]": []string{"1"},
- "Uint16Ptr[0]": []string{"1"},
- "Uint32[0]": []string{"1"},
- "Uint32Ptr[0]": []string{"1"},
- "Uint64[0]": []string{"1"},
- "Uint64Ptr[0]": []string{"1"},
- "NestedInt[1][2]": []string{"3"},
- "NestedIntPtr[1][2]": []string{"3"},
- }
-}
-
-func getComplexMapStruct() *ComplexMapStruct {
-
- key := "key"
- val := "value"
-
- i0 := int(0)
- i1 := int(1)
- i2 := int(2)
- i3 := int(3)
- i80 := int8(0)
- i81 := int8(1)
- i160 := int16(0)
- i161 := int16(1)
- i320 := int32(0)
- i321 := int32(1)
- i640 := int64(0)
- i641 := int64(1)
-
- ui0 := uint(0)
- ui1 := uint(1)
- ui80 := uint8(0)
- ui81 := uint8(1)
- ui160 := uint16(0)
- ui161 := uint16(1)
- ui320 := uint32(0)
- ui321 := uint32(1)
- ui640 := uint64(0)
- ui641 := uint64(1)
-
- return &ComplexMapStruct{
- String: map[string]string{key: val},
- StringPtr: map[*string]*string{&key: &val},
- Int: map[int]int{i0: i1},
- IntPtr: map[*int]*int{&i0: &i1},
- Int8: map[int8]int8{i80: i81},
- Int8Ptr: map[*int8]*int8{&i80: &i81},
- Int16: map[int16]int16{i160: i161},
- Int16Ptr: map[*int16]*int16{&i160: &i161},
- Int32: map[int32]int32{i320: i321},
- Int32Ptr: map[*int32]*int32{&i320: &i321},
- Int64: map[int64]int64{i640: i641},
- Int64Ptr: map[*int64]*int64{&i640: &i641},
- Uint: map[uint]uint{ui0: ui1},
- UintPtr: map[*uint]*uint{&ui0: &ui1},
- Uint8: map[uint8]uint8{ui80: ui81},
- Uint8Ptr: map[*uint8]*uint8{&ui80: &ui81},
- Uint16: map[uint16]uint16{ui160: ui161},
- Uint16Ptr: map[*uint16]*uint16{&ui160: &ui161},
- Uint32: map[uint32]uint32{ui320: ui321},
- Uint32Ptr: map[*uint32]*uint32{&ui320: &ui321},
- Uint64: map[uint64]uint64{ui640: ui641},
- Uint64Ptr: map[*uint64]*uint64{&ui640: &ui641},
- NestedInt: map[int]map[int]int{i1: {i2: i3}},
- NestedIntPtr: map[*int]map[*int]*int{&i1: {&i2: &i3}},
- }
-}
-
-func BenchmarkComplexMapDecodeStructAllTypes(b *testing.B) {
- values := getComplexMapStructValues()
- decoder := form.NewDecoder()
-
- b.ReportAllocs()
- for n := 0; n < b.N; n++ {
- var test ComplexMapStruct
- if err := decoder.Decode(&test, values); err != nil {
- b.Error(err)
- }
- }
-}
-
-func BenchmarkComplexMapDecodeStructAllTypesParallel(b *testing.B) {
- values := getComplexMapStructValues()
- decoder := form.NewDecoder()
-
- b.ReportAllocs()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- var test ComplexMapStruct
- if err := decoder.Decode(&test, values); err != nil {
- b.Error(err)
- }
- }
- })
-}
-
-func BenchmarkComplexMapEncodeStructAllTypes(b *testing.B) {
- test := getComplexMapStruct()
- encoder := form.NewEncoder()
-
- b.ReportAllocs()
- for n := 0; n < b.N; n++ {
- if _, err := encoder.Encode(&test); err != nil {
- b.Error(err)
- }
- }
-}
-
-func BenchmarkComplexMapEncodeStructAllTypesParallel(b *testing.B) {
- test := getComplexMapStruct()
- encoder := form.NewEncoder()
-
- b.ReportAllocs()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- if _, err := encoder.Encode(&test); err != nil {
- b.Error(err)
- }
- }
- })
-}
-
-// NestedStruct Benchmarks
-
-type Nested2 struct {
- Value string
- Nested2 *Nested2
-}
-
-type Nested struct {
- Value string
-}
-
-type NestedStruct struct {
- Nested
- NestedArray []Nested
- NestedPtrArray []*Nested
- Nested2 Nested2
-}
-
-func getNestedStructValues() url.Values {
- return url.Values{
- // Nested Field
- "Value": []string{"value"},
- // Nested Array
- "NestedArray[0].Value": []string{"value"},
- "NestedArray[1].Value": []string{"value"},
- // Nested Array Ptr
- "NestedPtrArray[0].Value": []string{"value"},
- "NestedPtrArray[1].Value": []string{"value"},
- // Nested 2
- "Nested2.Value": []string{"value"},
- "Nested2.Nested2.Value": []string{"value"},
- }
-}
-
-func getNestedStruct() *NestedStruct {
-
- nested := Nested{
- Value: "value",
- }
-
- nested2 := Nested2{
- Value: "value",
- Nested2: &Nested2{Value: "value"},
- }
-
- return &NestedStruct{
- Nested: nested,
- NestedArray: []Nested{nested, nested},
- NestedPtrArray: []*Nested{&nested, &nested},
- Nested2: nested2,
- }
-}
-
-func BenchmarkDecodeNestedStruct(b *testing.B) {
-
- values := getNestedStructValues()
- decoder := form.NewDecoder()
-
- b.ReportAllocs()
- for n := 0; n < b.N; n++ {
- var test NestedStruct
- if err := decoder.Decode(&test, values); err != nil {
- b.Error(err)
- }
- }
-}
-
-func BenchmarkDecodeNestedStructParallel(b *testing.B) {
-
- values := getNestedStructValues()
- decoder := form.NewDecoder()
-
- b.ReportAllocs()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- var test NestedStruct
- if err := decoder.Decode(&test, values); err != nil {
- b.Error(err)
- }
- }
- })
-}
-
-func BenchmarkEncodeNestedStruct(b *testing.B) {
-
- test := getNestedStruct()
- encoder := form.NewEncoder()
-
- b.ReportAllocs()
- for n := 0; n < b.N; n++ {
- if _, err := encoder.Encode(&test); err != nil {
- b.Error(err)
- }
- }
-}
-
-func BenchmarkEncodeNestedStructParallel(b *testing.B) {
-
- test := getNestedStruct()
- encoder := form.NewEncoder()
-
- b.ReportAllocs()
- b.RunParallel(func(pb *testing.PB) {
- for pb.Next() {
- if _, err := encoder.Encode(&test); err != nil {
- b.Error(err)
- }
- }
- })
-}
diff --git a/toolkit/form/cache.go b/toolkit/form/cache.go
deleted file mode 100644
index 2e31ecce..00000000
--- a/toolkit/form/cache.go
+++ /dev/null
@@ -1,142 +0,0 @@
-package form
-
-import (
- "github.com/goccy/go-reflect"
- "sort"
- "strings"
- "sync"
- "sync/atomic"
-)
-
-type cacheFields []cachedField
-
-func (s cacheFields) Len() int {
- return len(s)
-}
-
-func (s cacheFields) Less(i, j int) bool {
- return !s[i].isAnonymous
-}
-
-func (s cacheFields) Swap(i, j int) {
- s[i], s[j] = s[j], s[i]
-}
-
-type cachedField struct {
- idx int
- name string
- isAnonymous bool
- isOmitEmpty bool
- isExtra bool
-}
-
-type cachedStruct struct {
- fields cacheFields
-}
-
-type structCacheMap struct {
- m atomic.Value // map[reflect.Type]*cachedStruct
- lock sync.Mutex
- tagFn TagNameFunc
-}
-
-// TagNameFunc allows for adding of a custom tag name parser
-type TagNameFunc func(field reflect.StructField) string
-
-func newStructCacheMap() *structCacheMap {
-
- sc := new(structCacheMap)
- sc.m.Store(make(map[reflect.Type]*cachedStruct))
-
- return sc
-}
-
-func (s *structCacheMap) Get(key reflect.Type) (value *cachedStruct, ok bool) {
- value, ok = s.m.Load().(map[reflect.Type]*cachedStruct)[key]
- return
-}
-
-func (s *structCacheMap) Set(key reflect.Type, value *cachedStruct) {
-
- m := s.m.Load().(map[reflect.Type]*cachedStruct)
-
- nm := make(map[reflect.Type]*cachedStruct, len(m)+1)
- for k, v := range m {
- nm[k] = v
- }
- nm[key] = value
- s.m.Store(nm)
-}
-
-func (s *structCacheMap) parseStruct(mode Mode, current reflect.Value, key reflect.Type, tagName string) *cachedStruct {
-
- s.lock.Lock()
-
- // could have been multiple trying to access, but once first is done this ensures struct
- // isn't parsed again.
- cs, ok := s.Get(key)
- if ok {
- s.lock.Unlock()
- return cs
- }
-
- typ := current.Type()
- cs = &cachedStruct{fields: make([]cachedField, 0, 4)} // init 4, betting most structs decoding into have at aleast 4 fields.
-
- numFields := current.NumField()
-
- var fld reflect.StructField
- var name string
- var idx int
- var isOmitEmpty bool
- var isExtra bool
-
- for i := 0; i < numFields; i++ {
- isOmitEmpty = false
- isExtra = false
- fld = typ.Field(i)
-
- if fld.PkgPath != blank && !fld.Anonymous {
- continue
- }
-
- if s.tagFn != nil {
- name = s.tagFn(fld)
- } else {
- name = fld.Tag.Get(tagName)
- }
-
- if name == ignore {
- continue
- }
-
- if mode == ModeExplicit && len(name) == 0 {
- continue
- }
-
- // check for omitempty
- if idx = strings.LastIndexByte(name, ','); idx != -1 {
- isOmitEmpty = name[idx+1:] == "omitempty"
- if isOmitEmpty {
- name = name[:idx]
- }
- }
-
- if len(name) == 0 {
- name = fld.Name
- }
-
- if name == "+" {
- isExtra = true
- }
-
- cs.fields = append(cs.fields, cachedField{idx: i, name: name, isAnonymous: fld.Anonymous, isOmitEmpty: isOmitEmpty, isExtra: isExtra})
- }
-
- sort.Sort(cs.fields)
- s.Set(typ, cs)
-
- s.lock.Unlock()
-
- return cs
-}
diff --git a/toolkit/form/cache_test.go b/toolkit/form/cache_test.go
deleted file mode 100644
index 79a43799..00000000
--- a/toolkit/form/cache_test.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package form
-
-import (
- "github.com/goccy/go-reflect"
- "testing"
-
- . "github.com/go-playground/assert/v2"
-)
-
-// NOTES:
-// - Run "go test" to run tests
-// - Run "gocov test | gocov report" to report on test converage by file
-// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called
-//
-// or
-//
-// -- may be a good idea to change to output path to somewherelike /tmp
-// go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html
-//
-//
-// go test -cpuprofile cpu.out
-// ./validator.test -test.bench=. -test.cpuprofile=cpu.prof
-// go tool pprof validator.test cpu.prof
-//
-//
-// go test -memprofile mem.out
-
-func TestDecoderMultipleSimultaniousParseStructRequests(t *testing.T) {
-
- sc := newStructCacheMap()
-
- type Struct struct {
- Array []int
- }
-
- proceed := make(chan struct{})
-
- var test Struct
-
- sv := reflect.ValueOf(test)
- typ := sv.Type()
-
- for i := 0; i < 200; i++ {
- go func() {
- <-proceed
- s := sc.parseStruct(ModeImplicit, sv, typ, "form")
- NotEqual(t, s, nil)
- }()
- }
-
- close(proceed)
-}
diff --git a/toolkit/form/decoder.go b/toolkit/form/decoder.go
deleted file mode 100644
index 25716ca1..00000000
--- a/toolkit/form/decoder.go
+++ /dev/null
@@ -1,814 +0,0 @@
-package form
-
-import (
- "fmt"
- "github.com/goccy/go-reflect"
- "github.com/samber/lo"
- "log"
- "net/url"
- "slices"
- "strconv"
- "strings"
- "time"
-)
-
-const (
- errArraySize = "Array size of '%d' is larger than the maximum currently set on the decoder of '%d'. To increase this limit please see, SetMaxArraySize(size uint)"
- errMissingStartBracket = "Invalid formatting for key '%s' missing '[' bracket"
- errMissingEndBracket = "Invalid formatting for key '%s' missing ']' bracket"
-)
-
-type decoder struct {
- d *Decoder
- errs DecodeErrors
- dm dataMap
- values url.Values
- decoded []string
- maxKeyLen int
- namespace []byte
-}
-
-func (d *decoder) setError(namespace []byte, err error) {
- if d.errs == nil {
- d.errs = make(DecodeErrors)
- }
- d.errs[string(namespace)] = err
-}
-
-func (d *decoder) findAlias(ns string) *recursiveData {
- for i := 0; i < len(d.dm); i++ {
- if d.dm[i].alias == ns {
- return d.dm[i]
- }
- }
- return nil
-}
-
-func (d *decoder) append(k string, i, idx int, isNum bool) {
- var rd *recursiveData
- if rd = d.findAlias(k[:idx]); rd == nil {
-
- l := len(d.dm) + 1
-
- if l > cap(d.dm) {
- dm := make(dataMap, l)
- copy(dm, d.dm)
- rd = new(recursiveData)
- rd.sliceLen = defaultSliceLen
- dm[len(d.dm)] = rd
- d.dm = dm
- } else {
- l = len(d.dm)
- d.dm = d.dm[:l+1]
- rd = d.dm[l]
- rd.sliceLen = defaultSliceLen
- rd.keys = rd.keys[0:0]
- }
-
- rd.alias = k[:idx]
- }
-
- // is map + key
- ke := key{
- ivalue: -1,
- value: k[idx+1 : i],
- searchValue: k[idx : i+1],
- }
-
- // is key is number, most likely array key, keep track of just in case an array/slice.
- if isNum {
-
- // no need to check for error, it will always pass
- // as we have done the checking to ensure
- // the value is a number ahead of time.
- var err error
- ke.ivalue, err = strconv.Atoi(ke.value)
- if err != nil {
- ke.ivalue = -1
- }
-
- if ke.ivalue > rd.sliceLen {
- rd.sliceLen = ke.ivalue
-
- }
- }
-
- rd.keys = append(rd.keys, ke)
-}
-
-func (d *decoder) parseMapData() {
- // already parsed
- if len(d.dm) > 0 {
- return
- }
-
- d.maxKeyLen = 0
- d.dm = d.dm[0:0]
-
- var i int
- var idx int
- var insideBracket bool
- var isNum bool
-
- for k := range d.values {
- if len(k) > d.maxKeyLen {
- d.maxKeyLen = len(k)
- }
-
- for i = 0; i < len(k); i++ {
-
- switch k[i] {
- case '[':
- idx = i
- insideBracket = true
- isNum = true
- case ']':
-
- if !insideBracket {
- log.Panicf(errMissingStartBracket, k)
- }
-
- d.append(k, i, idx, isNum)
-
- insideBracket = false
- default:
- // checking if not a number, 0-9 is 48-57 in byte, see for yourself fmt.Println('0', '1', '2', '3', '4', '5', '6', '7', '8', '9')
- if insideBracket && (k[i] > 57 || k[i] < 48) {
- isNum = false
- }
- }
- }
-
- // if still inside bracket, that means no ending bracket was ever specified
- if insideBracket {
- log.Panicf(errMissingEndBracket, k)
- }
- }
-}
-
-func (d *decoder) traverseStruct(v reflect.Value, typ reflect.Type, namespace []byte) (set bool) {
- l := len(namespace)
- first := l == 0
-
- // anonymous structs will still work for caching as the whole definition is stored
- // including tags
- s, ok := d.d.structCache.Get(typ)
- if !ok {
- s = d.d.structCache.parseStruct(d.d.mode, v, typ, d.d.tagName)
- }
-
- var extraField *cachedField
- for _, f := range s.fields {
- if f.isExtra {
- f := f
- extraField = &f
- continue
- }
- namespace = namespace[:l]
-
- if f.isAnonymous {
- if d.setFieldByType(v.Field(f.idx), namespace, 0) {
- set = true
- }
- }
-
- names := strings.Split(f.name, ",")
- for _, name := range names {
- namespace = namespace[:l]
- if first {
- name := "[" + name + "]"
- namespace = append(namespace, name...)
- } else {
- namespace = append(namespace, d.d.namespacePrefix...)
- namespace = append(namespace, name...)
- namespace = append(namespace, d.d.namespaceSuffix...)
- }
-
- if d.setFieldByType(v.Field(f.idx), namespace, 0) {
- set = true
- break
- }
- }
- }
-
- if extraField != nil {
- namespace = namespace[:l]
- if d.setFieldByType(v.Field(extraField.idx), namespace, 0) {
- set = true
- }
- }
-
- return
-}
-
-func (d *decoder) populateMap(v reflect.Value, namespace []byte) (set bool) {
- var rd *recursiveData
-
- d.parseMapData()
-
- // no natural map support so skip directly to dm lookup
- if rd = d.findAlias(string(namespace)); rd == nil {
- return
- }
-
- var existing bool
- var kv key
- var mp reflect.Value
- var mk reflect.Value
-
- typ := v.Type()
- if typ.Kind() != reflect.Map {
- typ = reflect.TypeOf(map[string]interface{}{})
- }
-
- if v.IsNil() {
- mp = reflect.MakeMap(typ)
- } else {
- existing = true
- mp = v
- }
-
- for i := 0; i < len(rd.keys); i++ {
- newVal := reflect.New(typ.Elem()).Elem()
- mk = reflect.New(typ.Key()).Elem()
- kv = rd.keys[i]
-
- if err := d.getMapKey(kv.value, mk, namespace); err != nil {
- d.setError(namespace, err)
- continue
- }
-
- if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) {
- set = true
- mp.SetMapIndex(mk, newVal)
- }
- }
-
- if !set || existing {
- return
- }
-
- v.Set(mp)
- return
-}
-
-func (d *decoder) populateSlice(ok bool, arr []string, v reflect.Value, namespace []byte) (set bool) {
- d.parseMapData()
- // slice elements could be mixed eg. number and non-numbers Value[0]=[]string{"10"} and Value=[]string{"10","20"}
-
- if ok && len(arr) > 0 {
- var varr reflect.Value
-
- var ol int
- l := len(arr)
-
- if v.IsNil() {
- varr = reflect.MakeSlice(v.Type(), len(arr), len(arr))
- } else {
-
- ol = v.Len()
- l += ol
-
- if v.Cap() <= l {
- varr = reflect.MakeSlice(v.Type(), l, l)
- } else {
- // preserve predefined capacity, possibly for reuse after decoding
- varr = reflect.MakeSlice(v.Type(), l, v.Cap())
- }
- reflect.Copy(varr, v)
- }
-
- for i := ol; i < l; i++ {
- newVal := reflect.New(v.Type().Elem()).Elem()
-
- if d.setFieldByType(newVal, namespace, i-ol) {
- set = true
- varr.Index(i).Set(newVal)
- }
- }
-
- v.Set(varr)
- }
-
- // maybe it's an numbered array i.e. Phone[0].Number
- if rd := d.findAlias(string(namespace)); rd != nil {
-
- var varr reflect.Value
- var kv key
-
- sl := rd.sliceLen + 1
-
- // checking below for maxArraySize, but if array exists and already
- // has sufficient capacity allocated then we do not check as the code
- // obviously allows a capacity greater than the maxArraySize.
-
- if v.IsNil() {
-
- if sl > d.d.maxArraySize {
- d.setError(namespace, fmt.Errorf(errArraySize, sl, d.d.maxArraySize))
- return
- }
-
- if v.Type().Kind() != reflect.Slice {
- varr = reflect.MakeSlice(reflect.TypeOf(make([]interface{}, 0)), sl, sl)
- } else {
- varr = reflect.MakeSlice(v.Type(), sl, sl)
- }
-
- } else if v.Len() < sl {
-
- if v.Cap() <= sl {
-
- if sl > d.d.maxArraySize {
- d.setError(namespace, fmt.Errorf(errArraySize, sl, d.d.maxArraySize))
- return
- }
-
- varr = reflect.MakeSlice(v.Type(), sl, sl)
- } else {
- varr = reflect.MakeSlice(v.Type(), sl, v.Cap())
- }
-
- reflect.Copy(varr, v)
-
- } else {
- varr = v
- }
-
- for i := 0; i < len(rd.keys); i++ {
-
- kv = rd.keys[i]
- newVal := reflect.New(varr.Type().Elem()).Elem()
-
- if kv.ivalue == -1 {
- d.setError(namespace, fmt.Errorf("invalid slice index '%s'", kv.value))
- continue
- }
-
- if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) {
- set = true
- varr.Index(kv.ivalue).Set(newVal)
- }
- }
-
- if !set {
- return
- }
-
- v.Set(varr)
- }
- return
-}
-
-func (d *decoder) setFieldByType(current reflect.Value, namespace []byte, idx int) (set bool) {
- if slices.Contains(d.decoded, string(namespace)) {
- return false
- }
-
- var err error
- v, kind := ExtractType(current)
-
- arr, ok := d.values[string(namespace)]
-
- if d.d.customTypeFuncs != nil {
-
- if ok {
- if cf, ok := d.d.customTypeFuncs[v.Type()]; ok {
- val, err := cf(arr[idx:])
- if err != nil {
- d.setError(namespace, err)
- return
- }
-
- v.Set(reflect.ValueOf(val))
- set = true
- return
- }
- }
- }
- switch kind {
- case reflect.Interface:
- if !ok {
- rds := lo.Filter[*recursiveData](d.dm, func(item *recursiveData, index int) bool {
- return item.alias == string(namespace)
- })
- if len(rds) > 0 {
- rd := rds[0]
- if rd.sliceLen >= 0 {
- set = d.populateSlice(ok, arr, v, namespace)
- } else {
- set = d.populateMap(v, namespace)
- }
- }
- return
- } else if idx == len(arr) {
- return
- }
- v.Set(reflect.ValueOf(arr[idx]))
- set = true
-
- case reflect.Ptr:
- newVal := reflect.New(v.Type().Elem())
- if set = d.setFieldByType(newVal.Elem(), namespace, idx); set {
- v.Set(newVal)
- }
-
- case reflect.String:
- if !ok || idx == len(arr) {
- return
- }
- v.SetString(arr[idx])
- set = true
-
- case reflect.Uint, reflect.Uint64:
- if !ok || idx == len(arr) || len(arr[idx]) == 0 {
- return
- }
- var u64 uint64
- if u64, err = strconv.ParseUint(arr[idx], 10, 64); err != nil {
- d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
- return
- }
- v.SetUint(u64)
- set = true
-
- case reflect.Uint8:
- if !ok || idx == len(arr) || len(arr[idx]) == 0 {
- return
- }
- var u64 uint64
- if u64, err = strconv.ParseUint(arr[idx], 10, 8); err != nil {
- d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
- return
- }
- v.SetUint(u64)
- set = true
-
- case reflect.Uint16:
- if !ok || idx == len(arr) || len(arr[idx]) == 0 {
- return
- }
- var u64 uint64
- if u64, err = strconv.ParseUint(arr[idx], 10, 16); err != nil {
- d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
- return
- }
- v.SetUint(u64)
- set = true
-
- case reflect.Uint32:
- if !ok || idx == len(arr) || len(arr[idx]) == 0 {
- return
- }
- var u64 uint64
- if u64, err = strconv.ParseUint(arr[idx], 10, 32); err != nil {
- d.setError(namespace, fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
- return
- }
- v.SetUint(u64)
- set = true
-
- case reflect.Int, reflect.Int64:
- if !ok || idx == len(arr) || len(arr[idx]) == 0 {
- return
- }
- var i64 int64
- if i64, err = strconv.ParseInt(arr[idx], 10, 64); err != nil {
- d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
- return
- }
- v.SetInt(i64)
- set = true
-
- case reflect.Int8:
- if !ok || idx == len(arr) || len(arr[idx]) == 0 {
- return
- }
- var i64 int64
- if i64, err = strconv.ParseInt(arr[idx], 10, 8); err != nil {
- d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
- return
- }
- v.SetInt(i64)
- set = true
-
- case reflect.Int16:
- if !ok || idx == len(arr) || len(arr[idx]) == 0 {
- return
- }
- var i64 int64
- if i64, err = strconv.ParseInt(arr[idx], 10, 16); err != nil {
- d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
- return
- }
- v.SetInt(i64)
- set = true
-
- case reflect.Int32:
- if !ok || idx == len(arr) || len(arr[idx]) == 0 {
- return
- }
- var i64 int64
- if i64, err = strconv.ParseInt(arr[idx], 10, 32); err != nil {
- d.setError(namespace, fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
- return
- }
- v.SetInt(i64)
- set = true
-
- case reflect.Float32:
- if !ok || idx == len(arr) || len(arr[idx]) == 0 {
- return
- }
- var f float64
- if f, err = strconv.ParseFloat(arr[idx], 32); err != nil {
- d.setError(namespace, fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
- return
- }
- v.SetFloat(f)
- set = true
-
- case reflect.Float64:
- if !ok || idx == len(arr) || len(arr[idx]) == 0 {
- return
- }
- var f float64
- if f, err = strconv.ParseFloat(arr[idx], 64); err != nil {
- d.setError(namespace, fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
- return
- }
- v.SetFloat(f)
- set = true
-
- case reflect.Bool:
- if !ok || idx == len(arr) {
- return
- }
- var b bool
- if b, err = parseBool(arr[idx]); err != nil {
- d.setError(namespace, fmt.Errorf("Invalid Boolean Value '%s' Type '%v' Namespace '%s'", arr[idx], v.Type(), string(namespace)))
- return
- }
- v.SetBool(b)
- set = true
-
- case reflect.Slice:
- set = d.populateSlice(ok, arr, v, namespace)
-
- case reflect.Array:
- d.parseMapData()
-
- // array elements could be mixed eg. number and non-numbers Value[0]=[]string{"10"} and Value=[]string{"10","20"}
-
- if ok && len(arr) > 0 {
- var varr reflect.Value
- l := len(arr)
- overCapacity := v.Len() < l
- if overCapacity {
- // more values than array capacity, ignore values over capacity as it's possible some would just want
- // to grab the first x number of elements; in the future strict mode logic should return an error
- fmt.Println("warning number of post form array values is larger than array capacity, ignoring overflow values")
- }
- varr = reflect.Indirect(reflect.New(reflect.ArrayOf(v.Len(), v.Type().Elem())))
- reflect.Copy(varr, v)
-
- if v.Len() < len(arr) {
- l = v.Len()
- }
- for i := 0; i < l; i++ {
- newVal := reflect.New(v.Type().Elem()).Elem()
-
- if d.setFieldByType(newVal, namespace, i) {
- set = true
- varr.Index(i).Set(newVal)
- }
- }
- v.Set(varr)
- }
-
- // maybe it's an numbered array i.e. Phone[0].Number
- if rd := d.findAlias(string(namespace)); rd != nil {
- var varr reflect.Value
- var kv key
-
- overCapacity := rd.sliceLen >= v.Len()
- if overCapacity {
- // more values than array capacity, ignore values over capacity as it's possible some would just want
- // to grab the first x number of elements; in the future strict mode logic should return an error
- fmt.Println("warning number of post form array values is larger than array capacity, ignoring overflow values")
- }
- varr = reflect.Indirect(reflect.New(reflect.ArrayOf(v.Len(), v.Type().Elem())))
- reflect.Copy(varr, v)
-
- for i := 0; i < len(rd.keys); i++ {
- kv = rd.keys[i]
- if kv.ivalue >= v.Len() {
- continue
- }
- newVal := reflect.New(varr.Type().Elem()).Elem()
-
- if kv.ivalue == -1 {
- d.setError(namespace, fmt.Errorf("invalid array index '%s'", kv.value))
- continue
- }
-
- if d.setFieldByType(newVal, append(namespace, kv.searchValue...), 0) {
- set = true
- varr.Index(kv.ivalue).Set(newVal)
- }
- }
-
- if !set {
- return
- }
- v.Set(varr)
- }
-
- case reflect.Map:
- set = d.populateMap(v, namespace)
-
- case reflect.Struct:
- typ := v.Type()
-
- // if we get here then no custom time function declared so use RFC3339 by default
- if typ == timeType {
-
- if !ok || len(arr[idx]) == 0 {
- return
- }
-
- t, err := time.Parse(time.RFC3339, arr[idx])
- if err != nil {
- d.setError(namespace, err)
- }
-
- v.Set(reflect.ValueOf(t))
- set = true
- return
- }
-
- d.parseMapData()
-
- // we must be recursing infinitly...but that's ok we caught it on the very first overun.
- if len(namespace) > d.maxKeyLen {
- return
- }
-
- set = d.traverseStruct(v, typ, namespace)
- }
-
- if set {
- d.decoded = append(d.decoded, string(namespace))
- }
- return
-}
-
-func (d *decoder) getMapKey(key string, current reflect.Value, namespace []byte) (err error) {
-
- v, kind := ExtractType(current)
-
- if d.d.customTypeFuncs != nil {
- if cf, ok := d.d.customTypeFuncs[v.Type()]; ok {
-
- val, er := cf([]string{key})
- if er != nil {
- err = er
- return
- }
-
- v.Set(reflect.ValueOf(val))
- return
- }
- }
-
- switch kind {
- case reflect.Interface:
- // If interface would have been set on the struct before decoding,
- // say to a struct value we would not get here but kind would be struct.
- v.Set(reflect.ValueOf(key))
- return
- case reflect.Ptr:
- newVal := reflect.New(v.Type().Elem())
- if err = d.getMapKey(key, newVal.Elem(), namespace); err == nil {
- v.Set(newVal)
- }
-
- case reflect.String:
- v.SetString(key)
-
- case reflect.Uint, reflect.Uint64:
-
- u64, e := strconv.ParseUint(key, 10, 64)
- if e != nil {
- err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
- return
- }
-
- v.SetUint(u64)
-
- case reflect.Uint8:
-
- u64, e := strconv.ParseUint(key, 10, 8)
- if e != nil {
- err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
- return
- }
-
- v.SetUint(u64)
-
- case reflect.Uint16:
-
- u64, e := strconv.ParseUint(key, 10, 16)
- if e != nil {
- err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
- return
- }
-
- v.SetUint(u64)
-
- case reflect.Uint32:
-
- u64, e := strconv.ParseUint(key, 10, 32)
- if e != nil {
- err = fmt.Errorf("Invalid Unsigned Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
- return
- }
-
- v.SetUint(u64)
-
- case reflect.Int, reflect.Int64:
-
- i64, e := strconv.ParseInt(key, 10, 64)
- if e != nil {
- err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
- return
- }
-
- v.SetInt(i64)
-
- case reflect.Int8:
-
- i64, e := strconv.ParseInt(key, 10, 8)
- if e != nil {
- err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
- return
- }
-
- v.SetInt(i64)
-
- case reflect.Int16:
-
- i64, e := strconv.ParseInt(key, 10, 16)
- if e != nil {
- err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
- return
- }
-
- v.SetInt(i64)
-
- case reflect.Int32:
-
- i64, e := strconv.ParseInt(key, 10, 32)
- if e != nil {
- err = fmt.Errorf("Invalid Integer Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
- return
- }
-
- v.SetInt(i64)
-
- case reflect.Float32:
-
- f, e := strconv.ParseFloat(key, 32)
- if e != nil {
- err = fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
- return
- }
-
- v.SetFloat(f)
-
- case reflect.Float64:
-
- f, e := strconv.ParseFloat(key, 64)
- if e != nil {
- err = fmt.Errorf("Invalid Float Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
- return
- }
-
- v.SetFloat(f)
-
- case reflect.Bool:
-
- b, e := parseBool(key)
- if e != nil {
- err = fmt.Errorf("Invalid Boolean Value '%s' Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
- return
- }
-
- v.SetBool(b)
-
- default:
- err = fmt.Errorf("Unsupported Map Key '%s', Type '%v' Namespace '%s'", key, v.Type(), string(namespace))
- }
-
- return
-}
diff --git a/toolkit/form/decoder_test.go b/toolkit/form/decoder_test.go
deleted file mode 100644
index b05001c3..00000000
--- a/toolkit/form/decoder_test.go
+++ /dev/null
@@ -1,1938 +0,0 @@
-package form
-
-import (
- "errors"
- "fmt"
- "net/url"
- "github.com/goccy/go-reflect"
- "strings"
- "testing"
- "time"
-
- . "github.com/go-playground/assert/v2"
-)
-
-// NOTES:
-// - Run "go test" to run tests
-// - Run "gocov test | gocov report" to report on test converage by file
-// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called
-//
-// or
-//
-// -- may be a good idea to change to output path to somewherelike /tmp
-// go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html
-//
-//
-// go test -cpuprofile cpu.out
-// ./validator.test -test.bench=. -test.cpuprofile=cpu.prof
-// go tool pprof validator.test cpu.prof
-//
-//
-// go test -memprofile mem.out
-
-func TestDecoderInt(t *testing.T) {
-
- type TestInt struct {
- Int int
- Int8 int8
- Int16 int16
- Int32 int32
- Int64 int64
- IntPtr *int
- Int8Ptr *int8
- Int16Ptr *int16
- Int32Ptr *int32
- Int64Ptr *int64
- IntArray []int
- IntPtrArray []*int
- IntArrayArray [][]int
- IntPtrArrayArray [][]*int
- IntMap map[int]int
- IntPtrMap map[*int]*int
- NoURLValue int
- IntNoValues int
- Int8NoValues int8
- Int16NoValues int16
- Int32NoValues int32
- Int64NoValues int64
- }
-
- values := url.Values{
- "Int": []string{"3"},
- "Int8": []string{"3"},
- "Int16": []string{"3"},
- "Int32": []string{"3"},
- "Int64": []string{"3"},
- "IntPtr": []string{"3"},
- "Int8Ptr": []string{"3"},
- "Int16Ptr": []string{"3"},
- "Int32Ptr": []string{"3"},
- "Int64Ptr": []string{"3"},
- "IntArray": []string{"1", "2", "3"},
- "IntPtrArray[0]": []string{"1"},
- "IntPtrArray[2]": []string{"3"},
- "IntArrayArray[0][0]": []string{"1"},
- "IntArrayArray[0][2]": []string{"3"},
- "IntArrayArray[2][0]": []string{"1"},
- "IntPtrArrayArray[0][0]": []string{"1"},
- "IntPtrArrayArray[0][2]": []string{"3"},
- "IntPtrArrayArray[2][0]": []string{"1"},
- "IntMap[1]": []string{"3"},
- "IntPtrMap[1]": []string{"3"},
- }
-
- var test TestInt
-
- test.IntArray = make([]int, 4)
-
- decoder := NewDecoder()
- errs := decoder.Decode(&test, values)
- Equal(t, errs, nil)
-
- Equal(t, test.Int, int(3))
- Equal(t, test.Int8, int8(3))
- Equal(t, test.Int16, int16(3))
- Equal(t, test.Int32, int32(3))
- Equal(t, test.Int64, int64(3))
-
- Equal(t, *test.IntPtr, int(3))
- Equal(t, *test.Int8Ptr, int8(3))
- Equal(t, *test.Int16Ptr, int16(3))
- Equal(t, *test.Int32Ptr, int32(3))
- Equal(t, *test.Int64Ptr, int64(3))
-
- Equal(t, len(test.IntArray), 7)
- Equal(t, test.IntArray[0], int(0))
- Equal(t, test.IntArray[1], int(0))
- Equal(t, test.IntArray[2], int(0))
- Equal(t, test.IntArray[3], int(0))
- Equal(t, test.IntArray[4], int(1))
- Equal(t, test.IntArray[5], int(2))
- Equal(t, test.IntArray[6], int(3))
-
- Equal(t, len(test.IntPtrArray), 3)
- Equal(t, *test.IntPtrArray[0], int(1))
- Equal(t, test.IntPtrArray[1], nil)
- Equal(t, *test.IntPtrArray[2], int(3))
-
- Equal(t, len(test.IntArrayArray), 3)
- Equal(t, len(test.IntArrayArray[0]), 3)
- Equal(t, len(test.IntArrayArray[1]), 0)
- Equal(t, len(test.IntArrayArray[2]), 1)
- Equal(t, test.IntArrayArray[0][0], int(1))
- Equal(t, test.IntArrayArray[0][1], int(0))
- Equal(t, test.IntArrayArray[0][2], int(3))
- Equal(t, test.IntArrayArray[2][0], int(1))
-
- Equal(t, len(test.IntPtrArrayArray), 3)
- Equal(t, len(test.IntPtrArrayArray[0]), 3)
- Equal(t, len(test.IntPtrArrayArray[1]), 0)
- Equal(t, len(test.IntPtrArrayArray[2]), 1)
- Equal(t, *test.IntPtrArrayArray[0][0], int(1))
- Equal(t, test.IntPtrArrayArray[0][1], nil)
- Equal(t, *test.IntPtrArrayArray[0][2], int(3))
- Equal(t, *test.IntPtrArrayArray[2][0], int(1))
-
- Equal(t, len(test.IntMap), 1)
- Equal(t, len(test.IntPtrMap), 1)
-
- v, ok := test.IntMap[1]
- Equal(t, ok, true)
- Equal(t, v, int(3))
-
- Equal(t, test.NoURLValue, int(0))
-
- Equal(t, test.IntNoValues, int(0))
- Equal(t, test.Int8NoValues, int8(0))
- Equal(t, test.Int16NoValues, int16(0))
- Equal(t, test.Int32NoValues, int32(0))
- Equal(t, test.Int64NoValues, int64(0))
-}
-
-func TestDecoderUint(t *testing.T) {
-
- type TestUint struct {
- Uint uint
- Uint8 uint8
- Uint16 uint16
- Uint32 uint32
- Uint64 uint64
- UintPtr *uint
- Uint8Ptr *uint8
- Uint16Ptr *uint16
- Uint32Ptr *uint32
- Uint64Ptr *uint64
- UintArray []uint
- UintPtrArray []*uint
- UintArrayArray [][]uint
- UintPtrArrayArray [][]*uint
- UintMap map[uint]uint
- UintPtrMap map[*uint]*uint
- NoURLValue uint
- UintNoValues uint
- Uint8NoValues uint8
- Uint16NoValues uint16
- Uint32NoValues uint32
- Uint64NoValues uint64
- }
-
- values := url.Values{
- "Uint": []string{"3"},
- "Uint8": []string{"3"},
- "Uint16": []string{"3"},
- "Uint32": []string{"3"},
- "Uint64": []string{"3"},
- "UintPtr": []string{"3"},
- "Uint8Ptr": []string{"3"},
- "Uint16Ptr": []string{"3"},
- "Uint32Ptr": []string{"3"},
- "Uint64Ptr": []string{"3"},
- "UintArray": []string{"1", "2", "3"},
- "UintPtrArray[0]": []string{"1"},
- "UintPtrArray[2]": []string{"3"},
- "UintArrayArray[0][0]": []string{"1"},
- "UintArrayArray[0][2]": []string{"3"},
- "UintArrayArray[2][0]": []string{"1"},
- "UintPtrArrayArray[0][0]": []string{"1"},
- "UintPtrArrayArray[0][2]": []string{"3"},
- "UintPtrArrayArray[2][0]": []string{"1"},
- "UintMap[1]": []string{"3"},
- "UintPtrMap[1]": []string{"3"},
- }
-
- var test TestUint
-
- test.UintArray = make([]uint, 4)
-
- decoder := NewDecoder()
- errs := decoder.Decode(&test, values)
- Equal(t, errs, nil)
-
- Equal(t, test.Uint, uint(3))
- Equal(t, test.Uint8, uint8(3))
- Equal(t, test.Uint16, uint16(3))
- Equal(t, test.Uint32, uint32(3))
- Equal(t, test.Uint64, uint64(3))
-
- Equal(t, *test.UintPtr, uint(3))
- Equal(t, *test.Uint8Ptr, uint8(3))
- Equal(t, *test.Uint16Ptr, uint16(3))
- Equal(t, *test.Uint32Ptr, uint32(3))
- Equal(t, *test.Uint64Ptr, uint64(3))
-
- Equal(t, len(test.UintArray), 7)
- Equal(t, test.UintArray[0], uint(0))
- Equal(t, test.UintArray[1], uint(0))
- Equal(t, test.UintArray[2], uint(0))
- Equal(t, test.UintArray[3], uint(0))
- Equal(t, test.UintArray[4], uint(1))
- Equal(t, test.UintArray[5], uint(2))
- Equal(t, test.UintArray[6], uint(3))
-
- Equal(t, len(test.UintPtrArray), 3)
- Equal(t, *test.UintPtrArray[0], uint(1))
- Equal(t, test.UintPtrArray[1], nil)
- Equal(t, *test.UintPtrArray[2], uint(3))
-
- Equal(t, len(test.UintArrayArray), 3)
- Equal(t, len(test.UintArrayArray[0]), 3)
- Equal(t, len(test.UintArrayArray[1]), 0)
- Equal(t, len(test.UintArrayArray[2]), 1)
- Equal(t, test.UintArrayArray[0][0], uint(1))
- Equal(t, test.UintArrayArray[0][1], uint(0))
- Equal(t, test.UintArrayArray[0][2], uint(3))
- Equal(t, test.UintArrayArray[2][0], uint(1))
-
- Equal(t, len(test.UintPtrArrayArray), 3)
- Equal(t, len(test.UintPtrArrayArray[0]), 3)
- Equal(t, len(test.UintPtrArrayArray[1]), 0)
- Equal(t, len(test.UintPtrArrayArray[2]), 1)
- Equal(t, *test.UintPtrArrayArray[0][0], uint(1))
- Equal(t, test.UintPtrArrayArray[0][1], nil)
- Equal(t, *test.UintPtrArrayArray[0][2], uint(3))
- Equal(t, *test.UintPtrArrayArray[2][0], uint(1))
-
- Equal(t, len(test.UintMap), 1)
- Equal(t, len(test.UintPtrMap), 1)
-
- v, ok := test.UintMap[1]
- Equal(t, ok, true)
- Equal(t, v, uint(3))
-
- Equal(t, test.NoURLValue, uint(0))
-
- Equal(t, test.UintNoValues, uint(0))
- Equal(t, test.Uint8NoValues, uint8(0))
- Equal(t, test.Uint16NoValues, uint16(0))
- Equal(t, test.Uint32NoValues, uint32(0))
- Equal(t, test.Uint64NoValues, uint64(0))
-}
-
-func TestDecoderString(t *testing.T) {
-
- type TestString struct {
- String string
- StringPtr *string
- StringArray []string
- StringPtrArray []*string
- StringArrayArray [][]string
- StringPtrArrayArray [][]*string
- StringMap map[string]string
- StringPtrMap map[*string]*string
- NoURLValue string
- }
-
- values := url.Values{
- "String": []string{"3"},
- "StringPtr": []string{"3"},
- "StringArray": []string{"1", "2", "3"},
- "StringPtrArray[0]": []string{"1"},
- "StringPtrArray[2]": []string{"3"},
- "StringArrayArray[0][0]": []string{"1"},
- "StringArrayArray[0][2]": []string{"3"},
- "StringArrayArray[2][0]": []string{"1"},
- "StringPtrArrayArray[0][0]": []string{"1"},
- "StringPtrArrayArray[0][2]": []string{"3"},
- "StringPtrArrayArray[2][0]": []string{"1"},
- "StringMap[1]": []string{"3"},
- "StringPtrMap[1]": []string{"3"},
- }
-
- var test TestString
-
- test.StringArray = make([]string, 4)
-
- decoder := NewDecoder()
- errs := decoder.Decode(&test, values)
- Equal(t, errs, nil)
-
- Equal(t, test.String, "3")
-
- Equal(t, *test.StringPtr, "3")
-
- Equal(t, len(test.StringArray), 7)
- Equal(t, test.StringArray[0], "")
- Equal(t, test.StringArray[1], "")
- Equal(t, test.StringArray[2], "")
- Equal(t, test.StringArray[3], "")
- Equal(t, test.StringArray[4], "1")
- Equal(t, test.StringArray[5], "2")
- Equal(t, test.StringArray[6], "3")
-
- Equal(t, len(test.StringPtrArray), 3)
- Equal(t, *test.StringPtrArray[0], "1")
- Equal(t, test.StringPtrArray[1], nil)
- Equal(t, *test.StringPtrArray[2], "3")
-
- Equal(t, len(test.StringArrayArray), 3)
- Equal(t, len(test.StringArrayArray[0]), 3)
- Equal(t, len(test.StringArrayArray[1]), 0)
- Equal(t, len(test.StringArrayArray[2]), 1)
- Equal(t, test.StringArrayArray[0][0], "1")
- Equal(t, test.StringArrayArray[0][1], "")
- Equal(t, test.StringArrayArray[0][2], "3")
- Equal(t, test.StringArrayArray[2][0], "1")
-
- Equal(t, len(test.StringPtrArrayArray), 3)
- Equal(t, len(test.StringPtrArrayArray[0]), 3)
- Equal(t, len(test.StringPtrArrayArray[1]), 0)
- Equal(t, len(test.StringPtrArrayArray[2]), 1)
- Equal(t, *test.StringPtrArrayArray[0][0], "1")
- Equal(t, test.StringPtrArrayArray[0][1], nil)
- Equal(t, *test.StringPtrArrayArray[0][2], "3")
- Equal(t, *test.StringPtrArrayArray[2][0], "1")
-
- Equal(t, len(test.StringMap), 1)
- Equal(t, len(test.StringPtrMap), 1)
-
- v, ok := test.StringMap["1"]
- Equal(t, ok, true)
- Equal(t, v, "3")
-
- Equal(t, test.NoURLValue, "")
-}
-
-func TestDecoderFloat(t *testing.T) {
-
- type TestFloat struct {
- Float32 float32
- Float32Ptr *float32
- Float64 float64
- Float64Ptr *float64
- Float32Array []float32
- Float32PtrArray []*float32
- Float32ArrayArray [][]float32
- Float32PtrArrayArray [][]*float32
- Float32Map map[float32]float32
- Float32PtrMap map[*float32]*float32
- Float32NoValue float32
- Float64NoValue float64
- }
-
- values := url.Values{
- "Float32": []string{"3.3"},
- "Float32Ptr": []string{"3.3"},
- "Float64": []string{"3.3"},
- "Float64Ptr": []string{"3.3"},
- "Float32Array": []string{"1.1", "2.2", "3.3"},
- "Float32PtrArray[0]": []string{"1.1"},
- "Float32PtrArray[2]": []string{"3.3"},
- "Float32ArrayArray[0][0]": []string{"1.1"},
- "Float32ArrayArray[0][2]": []string{"3.3"},
- "Float32ArrayArray[2][0]": []string{"1.1"},
- "Float32PtrArrayArray[0][0]": []string{"1.1"},
- "Float32PtrArrayArray[0][2]": []string{"3.3"},
- "Float32PtrArrayArray[2][0]": []string{"1.1"},
- "Float32Map[1.1]": []string{"3.3"},
- "Float32PtrMap[1.1]": []string{"3.3"},
- }
-
- var test TestFloat
-
- test.Float32Array = make([]float32, 4)
-
- decoder := NewDecoder()
- errs := decoder.Decode(&test, values)
- Equal(t, errs, nil)
-
- Equal(t, test.Float32, float32(3.3))
- Equal(t, test.Float64, float64(3.3))
-
- Equal(t, *test.Float32Ptr, float32(3.3))
- Equal(t, *test.Float64Ptr, float64(3.3))
-
- Equal(t, len(test.Float32Array), 7)
- Equal(t, test.Float32Array[0], float32(0.0))
- Equal(t, test.Float32Array[1], float32(0.0))
- Equal(t, test.Float32Array[2], float32(0.0))
- Equal(t, test.Float32Array[3], float32(0.0))
- Equal(t, test.Float32Array[4], float32(1.1))
- Equal(t, test.Float32Array[5], float32(2.2))
- Equal(t, test.Float32Array[6], float32(3.3))
-
- Equal(t, len(test.Float32PtrArray), 3)
- Equal(t, *test.Float32PtrArray[0], float32(1.1))
- Equal(t, test.Float32PtrArray[1], nil)
- Equal(t, *test.Float32PtrArray[2], float32(3.3))
-
- Equal(t, len(test.Float32ArrayArray), 3)
- Equal(t, len(test.Float32ArrayArray[0]), 3)
- Equal(t, len(test.Float32ArrayArray[1]), 0)
- Equal(t, len(test.Float32ArrayArray[2]), 1)
- Equal(t, test.Float32ArrayArray[0][0], float32(1.1))
- Equal(t, test.Float32ArrayArray[0][1], float32(0.0))
- Equal(t, test.Float32ArrayArray[0][2], float32(3.3))
- Equal(t, test.Float32ArrayArray[2][0], float32(1.1))
-
- Equal(t, len(test.Float32PtrArrayArray), 3)
- Equal(t, len(test.Float32PtrArrayArray[0]), 3)
- Equal(t, len(test.Float32PtrArrayArray[1]), 0)
- Equal(t, len(test.Float32PtrArrayArray[2]), 1)
- Equal(t, *test.Float32PtrArrayArray[0][0], float32(1.1))
- Equal(t, test.Float32PtrArrayArray[0][1], nil)
- Equal(t, *test.Float32PtrArrayArray[0][2], float32(3.3))
- Equal(t, *test.Float32PtrArrayArray[2][0], float32(1.1))
-
- Equal(t, len(test.Float32Map), 1)
- Equal(t, len(test.Float32PtrMap), 1)
-
- v, ok := test.Float32Map[float32(1.1)]
- Equal(t, ok, true)
- Equal(t, v, float32(3.3))
-
- Equal(t, test.Float32NoValue, float32(0.0))
- Equal(t, test.Float64NoValue, float64(0.0))
-}
-
-func TestDecoderBool(t *testing.T) {
-
- type TestBool struct {
- Bool bool
- BoolPtr *bool
- BoolPtrNil *bool
- BoolPtrEmpty *bool
- BoolArray []bool
- BoolPtrArray []*bool
- BoolArrayArray [][]bool
- BoolPtrArrayArray [][]*bool
- BoolMap map[bool]bool
- BoolPtrMap map[*bool]*bool
- NoURLValue bool
- }
-
- values := url.Values{
- "Bool": []string{"true"},
- "BoolPtr": []string{"true"},
- "BoolPtrEmpty": []string{""},
- "BoolArray": []string{"off", "t", "on"},
- "BoolPtrArray[0]": []string{"true"},
- "BoolPtrArray[2]": []string{"T"},
- "BoolArrayArray[0][0]": []string{"TRUE"},
- "BoolArrayArray[0][2]": []string{"True"},
- "BoolArrayArray[2][0]": []string{"true"},
- "BoolPtrArrayArray[0][0]": []string{"true"},
- "BoolPtrArrayArray[0][2]": []string{"t"},
- "BoolPtrArrayArray[2][0]": []string{"1"},
- "BoolMap[true]": []string{"true"},
- "BoolPtrMap[t]": []string{"true"},
- }
-
- var test TestBool
-
- test.BoolArray = make([]bool, 4)
-
- decoder := NewDecoder()
- errs := decoder.Decode(&test, values)
- Equal(t, errs, nil)
-
- Equal(t, test.Bool, true)
-
- Equal(t, *test.BoolPtr, true)
- Equal(t, test.BoolPtrNil, nil)
- NotEqual(t, test.BoolPtrEmpty, nil)
- Equal(t, *test.BoolPtrEmpty, false)
-
- Equal(t, len(test.BoolArray), 7)
- Equal(t, test.BoolArray[0], false)
- Equal(t, test.BoolArray[1], false)
- Equal(t, test.BoolArray[2], false)
- Equal(t, test.BoolArray[3], false)
- Equal(t, test.BoolArray[4], false)
- Equal(t, test.BoolArray[5], true)
- Equal(t, test.BoolArray[6], true)
-
- Equal(t, len(test.BoolPtrArray), 3)
- Equal(t, *test.BoolPtrArray[0], true)
- Equal(t, test.BoolPtrArray[1], nil)
- Equal(t, *test.BoolPtrArray[2], true)
-
- Equal(t, len(test.BoolArrayArray), 3)
- Equal(t, len(test.BoolArrayArray[0]), 3)
- Equal(t, len(test.BoolArrayArray[1]), 0)
- Equal(t, len(test.BoolArrayArray[2]), 1)
- Equal(t, test.BoolArrayArray[0][0], true)
- Equal(t, test.BoolArrayArray[0][1], false)
- Equal(t, test.BoolArrayArray[0][2], true)
- Equal(t, test.BoolArrayArray[2][0], true)
-
- Equal(t, len(test.BoolPtrArrayArray), 3)
- Equal(t, len(test.BoolPtrArrayArray[0]), 3)
- Equal(t, len(test.BoolPtrArrayArray[1]), 0)
- Equal(t, len(test.BoolPtrArrayArray[2]), 1)
- Equal(t, *test.BoolPtrArrayArray[0][0], true)
- Equal(t, test.BoolPtrArrayArray[0][1], nil)
- Equal(t, *test.BoolPtrArrayArray[0][2], true)
- Equal(t, *test.BoolPtrArrayArray[2][0], true)
-
- Equal(t, len(test.BoolMap), 1)
- Equal(t, len(test.BoolPtrMap), 1)
-
- v, ok := test.BoolMap[true]
- Equal(t, ok, true)
- Equal(t, v, true)
-
- Equal(t, test.NoURLValue, false)
-}
-
-func TestDecoderEqualStructMapValue(t *testing.T) {
- type PhoneStruct struct {
- Number string
- }
-
- type PhoneMap map[string]string
-
- type TestStruct struct {
- PhoneStruct PhoneStruct `form:"Phone"`
- PhoneMap PhoneMap `form:"Phone"`
- }
-
- testCases := []struct {
- NamespacePrefix string
- NamespaceSuffix string
- Values url.Values
- }{{
- NamespacePrefix: ".",
- Values: url.Values{
- "Phone.Number": []string{"111"},
- "Phone[Number]": []string{"222"},
- },
- }, {
- NamespacePrefix: "[",
- NamespaceSuffix: "]",
- Values: url.Values{
- "Phone[Number]": []string{"111"},
- },
- }}
-
- for _, tc := range testCases {
- tc := tc
- t.Run(fmt.Sprintf("Namespace_%s%s", tc.NamespacePrefix, tc.NamespaceSuffix), func(t *testing.T) {
- decoder := NewDecoder()
- decoder.SetNamespacePrefix(tc.NamespacePrefix)
- decoder.SetNamespaceSuffix(tc.NamespaceSuffix)
-
- var test TestStruct
-
- err := decoder.Decode(&test, tc.Values)
- Equal(t, err, nil)
-
- Equal(t, test.PhoneStruct.Number, "111")
-
- if tc.NamespacePrefix == "." {
- Equal(t, test.PhoneMap["Number"], "222")
- } else {
- Equal(t, test.PhoneMap["Number"], "111")
- }
- })
- }
-}
-
-func TestDecoderStruct(t *testing.T) {
-
- type Phone struct {
- Number string
- }
-
- type TestMapKeys struct {
- MapIfaceKey map[interface{}]string
- MapFloat32Key map[float32]float32
- MapFloat64Key map[float64]float64
- MapNestedInt map[int]map[int]int
- MapInt8 map[int8]int8
- MapInt16 map[int16]int16
- MapInt32 map[int32]int32
- MapUint8 map[uint8]uint8
- MapUint16 map[uint16]uint16
- MapUint32 map[uint32]uint32
- }
-
- type TestStruct struct {
- Name string `form:"name"`
- Phone []Phone
- PhonePtr []*Phone
- NonNilPtr *Phone
- Ignore string `form:"-"`
- Anonymous struct {
- Value string
- Ignore string `form:"-"`
- unexposed string
- }
- Time time.Time
- TimePtr *time.Time
- Invalid interface{}
- ExistingMap map[string]string `form:"mp"`
- MapNoValue map[int]int
- TestMapKeys TestMapKeys
- NilArray []string
- TooSmallArray []string
- TooSmallCapOKArray []string
- ZeroLengthArray []string
- TooSmallNumberedArray []string
- TooSmallCapOKNumberedArray []string
- BigEnoughNumberedArray []string
- IfaceNonNil interface{}
- IfaceInvalid interface{}
- TimeMapKey map[time.Time]string
- ExistingArray []string
- ExistingArrayIndex []string
- }
-
- defaultValues := url.Values{
- "name": []string{"joeybloggs"},
- "Ignore": []string{"ignore"},
- "Time": []string{"2016-01-02"},
- "TimePtr": []string{"2016-01-02"},
- "mp[key]": []string{"value"},
- "NilArray": []string{"1", "2"},
- "TooSmallArray": []string{"1", "2"},
- "TooSmallCapOKArray": []string{"1", "2"},
- "ZeroLengthArray": []string{},
- "TooSmallNumberedArray[2]": []string{"2"},
- "TooSmallCapOKNumberedArray[2]": []string{"2"},
- "BigEnoughNumberedArray[2]": []string{"1"},
- "TimeMapKey[2016-01-02]": []string{"time"},
- "ExistingArray": []string{"arr2"},
- "ExistingArrayIndex[1]": []string{"arr2"},
- }
-
- testCases := []struct {
- NamespacePrefix string
- NamespaceSuffix string
- Values url.Values
- }{{
- NamespacePrefix: ".",
- Values: url.Values{
- "Phone[0].Number": []string{"1(111)111-1111"},
- "Phone[1].Number": []string{"9(999)999-9999"},
- "PhonePtr[0].Number": []string{"1(111)111-1111"},
- "PhonePtr[1].Number": []string{"9(999)999-9999"},
- "NonNilPtr.Number": []string{"9(999)999-9999"},
- "Anonymous.Value": []string{"Anon"},
- "TestMapKeys.MapIfaceKey[key]": []string{"3"},
- "TestMapKeys.MapFloat32Key[0.0]": []string{"3.3"},
- "TestMapKeys.MapFloat64Key[0.0]": []string{"3.3"},
- "TestMapKeys.MapNestedInt[1][2]": []string{"3"},
- "TestMapKeys.MapInt8[0]": []string{"3"},
- "TestMapKeys.MapInt16[0]": []string{"3"},
- "TestMapKeys.MapInt32[0]": []string{"3"},
- "TestMapKeys.MapUint8[0]": []string{"3"},
- "TestMapKeys.MapUint16[0]": []string{"3"},
- "TestMapKeys.MapUint32[0]": []string{"3"},
- },
- }, {
- NamespacePrefix: "[",
- NamespaceSuffix: "]",
- Values: url.Values{
- "Phone[0][Number]": []string{"1(111)111-1111"},
- "Phone[1][Number]": []string{"9(999)999-9999"},
- "PhonePtr[0][Number]": []string{"1(111)111-1111"},
- "PhonePtr[1][Number]": []string{"9(999)999-9999"},
- "NonNilPtr[Number]": []string{"9(999)999-9999"},
- "Anonymous[Value]": []string{"Anon"},
- "TestMapKeys[MapIfaceKey][key]": []string{"3"},
- "TestMapKeys[MapFloat32Key][0.0]": []string{"3.3"},
- "TestMapKeys[MapFloat64Key][0.0]": []string{"3.3"},
- "TestMapKeys[MapNestedInt][1][2]": []string{"3"},
- "TestMapKeys[MapInt8][0]": []string{"3"},
- "TestMapKeys[MapInt16][0]": []string{"3"},
- "TestMapKeys[MapInt32][0]": []string{"3"},
- "TestMapKeys[MapUint8][0]": []string{"3"},
- "TestMapKeys[MapUint16][0]": []string{"3"},
- "TestMapKeys[MapUint32][0]": []string{"3"},
- },
- }}
-
- for _, tc := range testCases {
- tc := tc
- t.Run(fmt.Sprintf("Namespace_%s%s", tc.NamespacePrefix, tc.NamespaceSuffix), func(t *testing.T) {
- decoder := NewDecoder()
- decoder.SetNamespacePrefix(tc.NamespacePrefix)
- decoder.SetNamespaceSuffix(tc.NamespaceSuffix)
-
- values := url.Values{}
-
- for key, vals := range defaultValues {
- values[key] = vals
- }
- for key, vals := range tc.Values {
- values[key] = vals
- }
-
- decoder.SetTagName("form")
- decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {
- return time.Parse("2006-01-02", vals[0])
- }, time.Time{})
-
- var test TestStruct
- test.ExistingMap = map[string]string{"existingkey": "existingvalue"}
- test.NonNilPtr = new(Phone)
- test.IfaceNonNil = new(Phone)
- test.IfaceInvalid = nil
- test.TooSmallArray = []string{"0"}
- test.TooSmallCapOKArray = make([]string, 0, 10)
- test.TooSmallNumberedArray = []string{"0"}
- test.TooSmallCapOKNumberedArray = make([]string, 0, 10)
- test.BigEnoughNumberedArray = make([]string, 3, 10)
- test.ExistingArray = []string{"arr1"}
- test.ExistingArrayIndex = []string{"arr1"}
-
- errs := decoder.Decode(&test, values)
- Equal(t, errs, nil)
-
- Equal(t, test.Name, "joeybloggs")
- Equal(t, test.Ignore, "")
- Equal(t, len(test.Phone), 2)
- Equal(t, test.Phone[0].Number, "1(111)111-1111")
- Equal(t, test.Phone[1].Number, "9(999)999-9999")
- Equal(t, len(test.PhonePtr), 2)
- Equal(t, test.PhonePtr[0].Number, "1(111)111-1111")
- Equal(t, test.PhonePtr[1].Number, "9(999)999-9999")
- Equal(t, test.NonNilPtr.Number, "9(999)999-9999")
- Equal(t, test.Anonymous.Value, "Anon")
- Equal(t, len(test.ExistingMap), 2)
- Equal(t, test.ExistingMap["existingkey"], "existingvalue")
- Equal(t, test.ExistingMap["key"], "value")
- Equal(t, len(test.NilArray), 2)
- Equal(t, test.NilArray[0], "1")
- Equal(t, test.NilArray[1], "2")
- Equal(t, len(test.TooSmallArray), 3)
- Equal(t, test.TooSmallArray[0], "0")
- Equal(t, test.TooSmallArray[1], "1")
- Equal(t, test.TooSmallArray[2], "2")
- Equal(t, len(test.ZeroLengthArray), 0)
- Equal(t, len(test.TooSmallNumberedArray), 3)
- Equal(t, test.TooSmallNumberedArray[0], "0")
- Equal(t, test.TooSmallNumberedArray[1], "")
- Equal(t, test.TooSmallNumberedArray[2], "2")
- Equal(t, len(test.BigEnoughNumberedArray), 3)
- Equal(t, cap(test.BigEnoughNumberedArray), 10)
- Equal(t, test.BigEnoughNumberedArray[0], "")
- Equal(t, test.BigEnoughNumberedArray[1], "")
- Equal(t, test.BigEnoughNumberedArray[2], "1")
- Equal(t, len(test.TooSmallCapOKArray), 2)
- Equal(t, cap(test.TooSmallCapOKArray), 10)
- Equal(t, test.TooSmallCapOKArray[0], "1")
- Equal(t, test.TooSmallCapOKArray[1], "2")
- Equal(t, len(test.TooSmallCapOKNumberedArray), 3)
- Equal(t, cap(test.TooSmallCapOKNumberedArray), 10)
- Equal(t, test.TooSmallCapOKNumberedArray[0], "")
- Equal(t, test.TooSmallCapOKNumberedArray[1], "")
- Equal(t, test.TooSmallCapOKNumberedArray[2], "2")
-
- Equal(t, len(test.ExistingArray), 2)
- Equal(t, test.ExistingArray[0], "arr1")
- Equal(t, test.ExistingArray[1], "arr2")
-
- Equal(t, len(test.ExistingArrayIndex), 2)
- Equal(t, test.ExistingArrayIndex[0], "arr1")
- Equal(t, test.ExistingArrayIndex[1], "arr2")
-
- tm, _ := time.Parse("2006-01-02", "2016-01-02")
- Equal(t, test.Time.Equal(tm), true)
- Equal(t, test.TimePtr.Equal(tm), true)
-
- NotEqual(t, test.TimeMapKey, nil)
- Equal(t, len(test.TimeMapKey), 1)
-
- _, ok := test.TimeMapKey[tm]
- Equal(t, ok, true)
-
- s := struct {
- Value string
- Ignore string `form:"-"`
- unexposed string
- }{}
-
- errs = decoder.Decode(&s, defaultValues)
- Equal(t, errs, nil)
- Equal(t, s.Value, "")
- Equal(t, s.Ignore, "")
- Equal(t, s.unexposed, "")
-
- Equal(t, test.TestMapKeys.MapIfaceKey["key"], "3")
- Equal(t, test.TestMapKeys.MapFloat32Key[float32(0.0)], float32(3.3))
- Equal(t, test.TestMapKeys.MapFloat64Key[float64(0.0)], float64(3.3))
-
- Equal(t, test.TestMapKeys.MapInt8[int8(0)], int8(3))
- Equal(t, test.TestMapKeys.MapInt16[int16(0)], int16(3))
- Equal(t, test.TestMapKeys.MapInt32[int32(0)], int32(3))
-
- Equal(t, test.TestMapKeys.MapUint8[uint8(0)], uint8(3))
- Equal(t, test.TestMapKeys.MapUint16[uint16(0)], uint16(3))
- Equal(t, test.TestMapKeys.MapUint32[uint32(0)], uint32(3))
-
- Equal(t, len(test.TestMapKeys.MapNestedInt), 1)
- Equal(t, len(test.TestMapKeys.MapNestedInt[1]), 1)
- Equal(t, test.TestMapKeys.MapNestedInt[1][2], 3)
- })
- }
-}
-
-func TestDecoderNativeTime(t *testing.T) {
-
- type TestError struct {
- Time time.Time
- TimeNoValue time.Time
- TimePtr *time.Time
- }
-
- values := url.Values{
- "Time": []string{"2006-01-02T15:04:05Z"},
- "TimeNoValue": []string{""},
- "TimePtr": []string{"2006-01-02T15:04:05Z"},
- }
-
- var test TestError
-
- decoder := NewDecoder()
-
- errs := decoder.Decode(&test, values)
- Equal(t, errs, nil)
-
- tm, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
- Equal(t, test.Time.Equal(tm), true)
- Equal(t, test.TimeNoValue.Equal(tm), false)
-
- NotEqual(t, test.TimePtr, nil)
- Equal(t, (*test.TimePtr).Equal(tm), true)
-}
-
-func TestDecoderErrors(t *testing.T) {
-
- type TestError struct {
- Bool bool `form:"bool"`
- Int int
- Int8 int8
- Int16 int16
- Int32 int32
- Uint uint
- Uint8 uint8
- Uint16 uint16
- Uint32 uint32
- Float32 float32
- Float64 float64
- String string
- Time time.Time
- MapBadIntKey map[int]int
- MapBadInt8Key map[int8]int8
- MapBadInt16Key map[int16]int16
- MapBadInt32Key map[int32]int32
- MapBadUintKey map[uint]uint
- MapBadUint8Key map[uint8]uint8
- MapBadUint16Key map[uint16]uint16
- MapBadUint32Key map[uint32]uint32
- MapBadFloat32Key map[float32]float32
- MapBadFloat64Key map[float64]float64
- MapBadBoolKey map[bool]bool
- MapBadKeyType map[complex64]int
- BadArrayValue []int
- BadMapKey map[time.Time]string
- OverflowNilArray []int
- OverFlowExistingArray []int
- BadArrayIndex []int
- }
-
- values := url.Values{
- "bool": []string{"uh-huh"},
- "Int": []string{"bad"},
- "Int8": []string{"bad"},
- "Int16": []string{"bad"},
- "Int32": []string{"bad"},
- "Uint": []string{"bad"},
- "Uint8": []string{"bad"},
- "Uint16": []string{"bad"},
- "Uint32": []string{"bad"},
- "Float32": []string{"bad"},
- "Float64": []string{"bad"},
- "String": []string{"str bad return val"},
- "Time": []string{"bad"},
- "MapBadIntKey[key]": []string{"1"},
- "MapBadInt8Key[key]": []string{"1"},
- "MapBadInt16Key[key]": []string{"1"},
- "MapBadInt32Key[key]": []string{"1"},
- "MapBadUintKey[key]": []string{"1"},
- "MapBadUint8Key[key]": []string{"1"},
- "MapBadUint16Key[key]": []string{"1"},
- "MapBadUint32Key[key]": []string{"1"},
- "MapBadFloat32Key[key]": []string{"1.1"},
- "MapBadFloat64Key[key]": []string{"1.1"},
- "MapBadBoolKey[uh-huh]": []string{"true"},
- "MapBadKeyType[1.4]": []string{"5"},
- "BadArrayValue[0]": []string{"badintval"},
- "BadMapKey[badtime]": []string{"badtime"},
- "OverflowNilArray[999]": []string{"idx 1000"},
- "OverFlowExistingArray[999]": []string{"idx 1000"},
- "BadArrayIndex[bad index]": []string{"bad idx"},
- }
-
- testCases := []struct {
- NamespacePrefix string
- NamespaceSuffix string
- }{{
- NamespacePrefix: ".",
- }, {
- NamespacePrefix: "[",
- NamespaceSuffix: "]",
- }}
-
- for _, tc := range testCases {
- tc := tc
- t.Run(fmt.Sprintf("Namespace_%s%s", tc.NamespacePrefix, tc.NamespaceSuffix), func(t *testing.T) {
- decoder := NewDecoder()
- decoder.SetNamespacePrefix(tc.NamespacePrefix)
- decoder.SetNamespaceSuffix(tc.NamespaceSuffix)
-
- decoder.SetMaxArraySize(4)
- decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {
- return nil, errors.New("Bad Type Conversion")
- }, "")
-
- test := TestError{
- OverFlowExistingArray: make([]int, 2),
- }
-
- errs := decoder.Decode(&test, values)
- NotEqual(t, errs, nil)
-
- e := errs.Error()
- NotEqual(t, e, "")
-
- err := errs.(DecodeErrors)
- Equal(t, len(err), 30)
-
- k := err["bool"]
- Equal(t, k.Error(), "Invalid Boolean Value 'uh-huh' Type 'bool' Namespace 'bool'")
-
- k = err["Int"]
- Equal(t, k.Error(), "Invalid Integer Value 'bad' Type 'int' Namespace 'Int'")
-
- k = err["Int8"]
- Equal(t, k.Error(), "Invalid Integer Value 'bad' Type 'int8' Namespace 'Int8'")
-
- k = err["Int16"]
- Equal(t, k.Error(), "Invalid Integer Value 'bad' Type 'int16' Namespace 'Int16'")
-
- k = err["Int32"]
- Equal(t, k.Error(), "Invalid Integer Value 'bad' Type 'int32' Namespace 'Int32'")
-
- k = err["Uint"]
- Equal(t, k.Error(), "Invalid Unsigned Integer Value 'bad' Type 'uint' Namespace 'Uint'")
-
- k = err["Uint8"]
- Equal(t, k.Error(), "Invalid Unsigned Integer Value 'bad' Type 'uint8' Namespace 'Uint8'")
-
- k = err["Uint16"]
- Equal(t, k.Error(), "Invalid Unsigned Integer Value 'bad' Type 'uint16' Namespace 'Uint16'")
-
- k = err["Uint32"]
- Equal(t, k.Error(), "Invalid Unsigned Integer Value 'bad' Type 'uint32' Namespace 'Uint32'")
-
- k = err["Float32"]
- Equal(t, k.Error(), "Invalid Float Value 'bad' Type 'float32' Namespace 'Float32'")
-
- k = err["Float64"]
- Equal(t, k.Error(), "Invalid Float Value 'bad' Type 'float64' Namespace 'Float64'")
-
- k = err["String"]
- Equal(t, k.Error(), "Bad Type Conversion")
-
- k = err["Time"]
- Equal(t, k.Error(), "parsing time \"bad\" as \"2006-01-02T15:04:05Z07:00\": cannot parse \"bad\" as \"2006\"")
-
- k = err["MapBadIntKey"]
- Equal(t, k.Error(), "Invalid Integer Value 'key' Type 'int' Namespace 'MapBadIntKey'")
-
- k = err["MapBadInt8Key"]
- Equal(t, k.Error(), "Invalid Integer Value 'key' Type 'int8' Namespace 'MapBadInt8Key'")
-
- k = err["MapBadInt16Key"]
- Equal(t, k.Error(), "Invalid Integer Value 'key' Type 'int16' Namespace 'MapBadInt16Key'")
-
- k = err["MapBadInt32Key"]
- Equal(t, k.Error(), "Invalid Integer Value 'key' Type 'int32' Namespace 'MapBadInt32Key'")
-
- k = err["MapBadUintKey"]
- Equal(t, k.Error(), "Invalid Unsigned Integer Value 'key' Type 'uint' Namespace 'MapBadUintKey'")
-
- k = err["MapBadUint8Key"]
- Equal(t, k.Error(), "Invalid Unsigned Integer Value 'key' Type 'uint8' Namespace 'MapBadUint8Key'")
-
- k = err["MapBadUint16Key"]
- Equal(t, k.Error(), "Invalid Unsigned Integer Value 'key' Type 'uint16' Namespace 'MapBadUint16Key'")
-
- k = err["MapBadUint32Key"]
- Equal(t, k.Error(), "Invalid Unsigned Integer Value 'key' Type 'uint32' Namespace 'MapBadUint32Key'")
-
- k = err["MapBadFloat32Key"]
- Equal(t, k.Error(), "Invalid Float Value 'key' Type 'float32' Namespace 'MapBadFloat32Key'")
-
- k = err["MapBadFloat64Key"]
- Equal(t, k.Error(), "Invalid Float Value 'key' Type 'float64' Namespace 'MapBadFloat64Key'")
-
- k = err["MapBadBoolKey"]
- Equal(t, k.Error(), "Invalid Boolean Value 'uh-huh' Type 'bool' Namespace 'MapBadBoolKey'")
-
- k = err["MapBadKeyType"]
- Equal(t, k.Error(), "Unsupported Map Key '1.4', Type 'complex64' Namespace 'MapBadKeyType'")
-
- k = err["BadArrayValue[0]"]
- Equal(t, k.Error(), "Invalid Integer Value 'badintval' Type 'int' Namespace 'BadArrayValue[0]'")
-
- k = err["OverflowNilArray"]
- Equal(t, k.Error(), "Array size of '1000' is larger than the maximum currently set on the decoder of '4'. To increase this limit please see, SetMaxArraySize(size uint)")
-
- k = err["OverFlowExistingArray"]
- Equal(t, k.Error(), "Array size of '1000' is larger than the maximum currently set on the decoder of '4'. To increase this limit please see, SetMaxArraySize(size uint)")
-
- k = err["BadArrayIndex"]
- Equal(t, k.Error(), "invalid slice index 'bad index'")
-
- type TestError2 struct {
- BadMapKey map[time.Time]string
- }
-
- values2 := url.Values{
- "BadMapKey[badtime]": []string{"badtime"},
- }
-
- var test2 TestError2
- decoder2 := NewDecoder()
- decoder2.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {
- return time.Parse("2006-01-02", vals[0])
- }, time.Time{})
-
- errs = decoder2.Decode(&test2, values2)
- NotEqual(t, errs, nil)
-
- e = errs.Error()
- NotEqual(t, e, "")
-
- k = err["BadMapKey"]
- Equal(t, k.Error(), "Unsupported Map Key 'badtime', Type 'time.Time' Namespace 'BadMapKey'")
- })
- }
-}
-
-func TestDecodeAllTypes(t *testing.T) {
-
- values := url.Values{
- "": []string{"3"},
- }
-
- decoder := NewDecoder()
-
- var i int
-
- errs := decoder.Decode(&i, values)
- Equal(t, errs, nil)
- Equal(t, i, 3)
-
- var i8 int
-
- errs = decoder.Decode(&i8, values)
- Equal(t, errs, nil)
- Equal(t, i8, 3)
-
- var i16 int
-
- errs = decoder.Decode(&i16, values)
- Equal(t, errs, nil)
- Equal(t, i16, 3)
-
- var i32 int
-
- errs = decoder.Decode(&i32, values)
- Equal(t, errs, nil)
- Equal(t, i32, 3)
-
- var i64 int
-
- errs = decoder.Decode(&i64, values)
- Equal(t, errs, nil)
- Equal(t, i64, 3)
-
- var ui int
-
- errs = decoder.Decode(&ui, values)
- Equal(t, errs, nil)
- Equal(t, ui, 3)
-
- var ui8 int
-
- errs = decoder.Decode(&ui8, values)
- Equal(t, errs, nil)
- Equal(t, ui8, 3)
-
- var ui16 int
-
- errs = decoder.Decode(&ui16, values)
- Equal(t, errs, nil)
- Equal(t, ui16, 3)
-
- var ui32 int
-
- errs = decoder.Decode(&ui32, values)
- Equal(t, errs, nil)
- Equal(t, ui32, 3)
-
- var ui64 int
-
- errs = decoder.Decode(&ui64, values)
- Equal(t, errs, nil)
- Equal(t, ui64, 3)
-
- values = url.Values{
- "": []string{"3.4"},
- }
-
- var f32 float32
-
- errs = decoder.Decode(&f32, values)
- Equal(t, errs, nil)
- Equal(t, f32, float32(3.4))
-
- var f64 float64
-
- errs = decoder.Decode(&f64, values)
- Equal(t, errs, nil)
- Equal(t, f64, float64(3.4))
-
- values = url.Values{
- "": []string{"true"},
- }
-
- var b bool
-
- errs = decoder.Decode(&b, values)
- Equal(t, errs, nil)
- Equal(t, b, true)
-
- tm, _ := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
-
- values = url.Values{
- "": []string{"2006-01-02T15:04:05Z"},
- }
-
- var dt time.Time
-
- errs = decoder.Decode(&dt, values)
-
- Equal(t, errs, nil)
- Equal(t, dt, tm)
-
- values = url.Values{
- "": []string{"arr1", "arr2"},
- }
-
- // basic array
- var arr []string
-
- errs = decoder.Decode(&arr, values)
- Equal(t, errs, nil)
- Equal(t, len(arr), 2)
- Equal(t, arr[0], "arr1")
- Equal(t, arr[1], "arr2")
-
- // pre-populated array
-
- // fmt.Println("Decoding...")
- errs = decoder.Decode(&arr, values)
- Equal(t, errs, nil)
- Equal(t, len(arr), 4)
- Equal(t, arr[0], "arr1")
- Equal(t, arr[1], "arr2")
- Equal(t, arr[2], "arr1")
- Equal(t, arr[3], "arr2")
-
- // basic array Ptr
- var arrPtr []*string
-
- errs = decoder.Decode(&arrPtr, values)
- Equal(t, errs, nil)
- Equal(t, len(arrPtr), 2)
- Equal(t, *arrPtr[0], "arr1")
- Equal(t, *arrPtr[1], "arr2")
-
- // pre-populated array Ptr
-
- // fmt.Println("Decoding...")
- errs = decoder.Decode(&arrPtr, values)
- Equal(t, errs, nil)
- Equal(t, len(arr), 4)
- Equal(t, *arrPtr[0], "arr1")
- Equal(t, *arrPtr[1], "arr2")
- Equal(t, *arrPtr[2], "arr1")
- Equal(t, *arrPtr[3], "arr2")
-
- // indexed array
-
- values = url.Values{
- "[0]": []string{"newVal1"},
- "[1]": []string{"newVal2"},
- }
-
- errs = decoder.Decode(&arr, values)
- Equal(t, errs, nil)
- Equal(t, len(arr), 4)
- Equal(t, arr[0], "newVal1")
- Equal(t, arr[1], "newVal2")
- Equal(t, arr[2], "arr1")
- Equal(t, arr[3], "arr2")
-
- values = url.Values{
- "[key1]": []string{"val1"},
- "[key2]": []string{"val2"},
- }
-
- // basic map
- var m map[string]string
-
- errs = decoder.Decode(&m, values)
- Equal(t, errs, nil)
- Equal(t, len(m), 2)
- Equal(t, m["key1"], "val1")
- Equal(t, m["key2"], "val2")
-
- // existing map
-
- errs = decoder.Decode(&m, values)
- Equal(t, errs, nil)
- Equal(t, len(m), 2)
- Equal(t, m["key1"], "val1")
- Equal(t, m["key2"], "val2")
-
- // basic map, adding more keys
-
- values = url.Values{
- "[key3]": []string{"val3"},
- }
- errs = decoder.Decode(&m, values)
- Equal(t, errs, nil)
- Equal(t, len(m), 3)
- Equal(t, m["key3"], "val3")
-
- // array of struct
-
- type Phone struct {
- Number string
- Label string
- }
-
- values = url.Values{
- "[0].Number": []string{"999"},
- "[1].Label": []string{"label2"},
- "[1].Number": []string{"111"},
- "[0].Label": []string{"label1"},
- }
-
- var phones []Phone
-
- errs = decoder.Decode(&phones, values)
- Equal(t, errs, nil)
- Equal(t, len(phones), 2)
- Equal(t, phones[0].Number, "999")
- Equal(t, phones[0].Label, "label1")
- Equal(t, phones[1].Number, "111")
- Equal(t, phones[1].Label, "label2")
-}
-
-func TestDecoderPanicsAndBadValues(t *testing.T) {
-
- type Phone struct {
- Number string
- }
-
- type TestError struct {
- Phone []Phone
- Phone2 []Phone
- Phone3 []Phone
- }
-
- values := url.Values{
- "Phone[0.Number": []string{"1(111)111-1111"},
- }
-
- var test TestError
-
- decoder := NewDecoder()
-
- PanicMatches(t, func() { _ = decoder.Decode(&test, values) }, "Invalid formatting for key 'Phone[0.Number' missing ']' bracket")
-
- i := 1
- err := decoder.Decode(i, values)
- NotEqual(t, err, nil)
-
- _, ok := err.(*InvalidDecoderError)
- Equal(t, ok, true)
- Equal(t, err.Error(), "form: Decode(non-pointer int)")
-
- err = decoder.Decode(nil, values)
- NotEqual(t, err, nil)
-
- _, ok = err.(*InvalidDecoderError)
- Equal(t, ok, true)
- Equal(t, err.Error(), "form: Decode(nil)")
-
- var ts *TestError
-
- err = decoder.Decode(ts, values)
- NotEqual(t, err, nil)
-
- _, ok = err.(*InvalidDecoderError)
- Equal(t, ok, true)
- Equal(t, err.Error(), "form: Decode(nil *form.TestError)")
-
- values = url.Values{
- "Phone0].Number": []string{"1(111)111-1111"},
- }
-
- PanicMatches(t, func() { _ = decoder.Decode(&test, values) }, "Invalid formatting for key 'Phone0].Number' missing '[' bracket")
-
- values = url.Values{
- "Phone[[0.Number": []string{"1(111)111-1111"},
- }
-
- PanicMatches(t, func() { _ = decoder.Decode(&test, values) }, "Invalid formatting for key 'Phone[[0.Number' missing ']' bracket")
-
- values = url.Values{
- "Phone0]].Number": []string{"1(111)111-1111"},
- }
-
- PanicMatches(t, func() { _ = decoder.Decode(&test, values) }, "Invalid formatting for key 'Phone0]].Number' missing '[' bracket")
-}
-
-func TestDecoderMapKeys(t *testing.T) {
-
- type TestMapKeys struct {
- MapIfaceKey map[interface{}]string
- MapFloat32Key map[float32]float32
- MapFloat64Key map[float64]float64
- MapNestedInt map[int]map[int]int
- MapInt8 map[int8]int8
- MapInt16 map[int16]int16
- MapInt32 map[int32]int32
- MapUint8 map[uint8]uint8
- MapUint16 map[uint16]uint16
- MapUint32 map[uint32]uint32
- }
-
- values := url.Values{
- "MapIfaceKey[key]": []string{"3"},
- "MapFloat32Key[0.0]": []string{"3.3"},
- "MapFloat64Key[0.0]": []string{"3.3"},
- "MapNestedInt[1][2]": []string{"3"},
- "MapInt8[0]": []string{"3"},
- "MapInt16[0]": []string{"3"},
- "MapInt32[0]": []string{"3"},
- "MapUint8[0]": []string{"3"},
- "MapUint16[0]": []string{"3"},
- "MapUint32[0]": []string{"3"},
- }
-
- var test TestMapKeys
-
- testCases := []struct {
- NamespacePrefix string
- NamespaceSuffix string
- }{
- {
- NamespacePrefix: ".",
- },
- {
- NamespacePrefix: "[",
- NamespaceSuffix: "]",
- },
- }
-
- for _, tc := range testCases {
- tc := tc
- t.Run(fmt.Sprintf("Namespace_%s%s", tc.NamespacePrefix, tc.NamespaceSuffix), func(t *testing.T) {
- decoder := NewDecoder()
- decoder.SetNamespacePrefix(tc.NamespacePrefix)
- decoder.SetNamespaceSuffix(tc.NamespaceSuffix)
-
- errs := decoder.Decode(&test, values)
- Equal(t, errs, nil)
-
- Equal(t, test.MapIfaceKey["key"], "3")
- Equal(t, test.MapFloat32Key[float32(0.0)], float32(3.3))
- Equal(t, test.MapFloat64Key[float64(0.0)], float64(3.3))
-
- Equal(t, test.MapInt8[int8(0)], int8(3))
- Equal(t, test.MapInt16[int16(0)], int16(3))
- Equal(t, test.MapInt32[int32(0)], int32(3))
-
- Equal(t, test.MapUint8[uint8(0)], uint8(3))
- Equal(t, test.MapUint16[uint16(0)], uint16(3))
- Equal(t, test.MapUint32[uint32(0)], uint32(3))
-
- Equal(t, len(test.MapNestedInt), 1)
- Equal(t, len(test.MapNestedInt[1]), 1)
- Equal(t, test.MapNestedInt[1][2], 3)
- })
- }
-}
-
-func TestDecoderStructRecursion(t *testing.T) {
-
- type Nested struct {
- Value string
- Nested *Nested
- }
-
- type TestRecursive struct {
- Nested Nested
- NestedPtr *Nested
- NestedTwo Nested
- }
-
- defaultValues := url.Values{
- "Value": []string{"value"},
- }
-
- testCases := []struct {
- Values url.Values
- NamespacePrefix string
- NamespaceSuffix string
- }{{
- NamespacePrefix: ".",
- Values: url.Values{
- "Nested.Value": []string{"value"},
- "NestedPtr.Value": []string{"value"},
- "NestedTwo.Nested.Value": []string{"value"},
- },
- }, {
- NamespacePrefix: "[",
- NamespaceSuffix: "]",
- Values: url.Values{
- "Nested[Value]": []string{"value"},
- "NestedPtr[Value]": []string{"value"},
- "NestedTwo[Nested][Value]": []string{"value"},
- },
- }}
-
- for _, tc := range testCases {
- tc := tc
- t.Run(fmt.Sprintf("Namespace_%s%s", tc.NamespacePrefix, tc.NamespaceSuffix), func(t *testing.T) {
- values := url.Values{}
-
- for key, vals := range defaultValues {
- values[key] = vals
- }
- for key, vals := range tc.Values {
- values[key] = vals
- }
-
- decoder := NewDecoder()
- decoder.SetNamespacePrefix(tc.NamespacePrefix)
- decoder.SetNamespaceSuffix(tc.NamespaceSuffix)
-
- var test TestRecursive
-
- errs := decoder.Decode(&test, values)
- Equal(t, errs, nil)
-
- Equal(t, test.Nested.Value, "value")
- Equal(t, test.NestedPtr.Value, "value")
- Equal(t, test.Nested.Nested, nil)
- Equal(t, test.NestedTwo.Nested.Value, "value")
- })
- }
-
-}
-
-func TestDecoderFormDecode(t *testing.T) {
-
- type Struct2 struct {
- Foo string
- Bar string
- }
-
- type Struct2Wrapper struct {
- InnerSlice []Struct2
- }
-
- sliceValues := map[string][]string{
- "InnerSlice[0].Foo": {"foo-is-set"},
- }
-
- singleValues := map[string][]string{
- "Foo": {"foo-is-set"},
- }
-
- fd := NewDecoder()
-
- dst := Struct2Wrapper{}
- err := fd.Decode(&dst, sliceValues)
- Equal(t, err, nil)
- NotEqual(t, dst.InnerSlice, nil)
- Equal(t, dst.InnerSlice[0].Foo, "foo-is-set")
-
- dst2 := Struct2{}
- err = fd.Decode(&dst2, singleValues)
- Equal(t, err, nil)
- Equal(t, dst2.Foo, "foo-is-set")
-}
-
-func TestDecoderArrayKeysSort(t *testing.T) {
-
- type Struct struct {
- Array []int
- }
-
- values := map[string][]string{
-
- "Array[2]": {"2"},
- "Array[10]": {"10"},
- }
-
- var test Struct
-
- d := NewDecoder()
-
- err := d.Decode(&test, values)
- Equal(t, err, nil)
-
- Equal(t, len(test.Array), 11)
- Equal(t, test.Array[2], int(2))
- Equal(t, test.Array[10], int(10))
-}
-
-func TestDecoderIncreasingKeys(t *testing.T) {
-
- type Struct struct {
- Array []int
- }
-
- values := map[string][]string{
- "Array[2]": {"2"},
- }
-
- var test Struct
-
- d := NewDecoder()
-
- err := d.Decode(&test, values)
- Equal(t, err, nil)
-
- Equal(t, len(test.Array), 3)
- Equal(t, test.Array[2], int(2))
-
- values["Array[10]"] = []string{"10"}
-
- var test2 Struct
-
- err = d.Decode(&test2, values)
- Equal(t, err, nil)
-
- Equal(t, len(test2.Array), 11)
- Equal(t, test2.Array[2], int(2))
- Equal(t, test2.Array[10], int(10))
-}
-
-func TestDecoderInterface(t *testing.T) {
-
- var iface interface{}
-
- d := NewDecoder()
-
- values := map[string][]string{
- "": {"2"},
- }
-
- var i int
-
- iface = &i
-
- err := d.Decode(iface, values)
- Equal(t, err, nil)
- Equal(t, i, 2)
-
- iface = i
-
- err = d.Decode(iface, values)
- NotEqual(t, err, nil)
-
- _, ok := err.(*InvalidDecoderError)
- Equal(t, ok, true)
- Equal(t, err.Error(), "form: Decode(non-pointer int)")
-
- values = map[string][]string{
- "Value": {"testVal"},
- }
-
- type test struct {
- Value string
- }
-
- var tst test
-
- iface = &tst
-
- err = d.Decode(iface, values)
- Equal(t, err, nil)
- Equal(t, tst.Value, "testVal")
-
- iface = tst
-
- err = d.Decode(iface, values)
- NotEqual(t, err, nil)
-
- _, ok = err.(*InvalidDecoderError)
- Equal(t, ok, true)
- Equal(t, err.Error(), "form: Decode(non-pointer form.test)")
-}
-
-func TestDecoderPointerToPointer(t *testing.T) {
-
- values := map[string][]string{
- "Value": {"testVal"},
- }
-
- type Test struct {
- Value string
- }
-
- var tst *Test
-
- d := NewDecoder()
- err := d.Decode(&tst, values)
- Equal(t, err, nil)
- Equal(t, tst.Value, "testVal")
-}
-
-func TestDecoderExplicit(t *testing.T) {
-
- type Test struct {
- Name string `form:"Name"`
- Age int
- }
-
- values := map[string][]string{
- "Name": {"Joeybloggs"},
- "Age": {"3"},
- }
-
- var test Test
-
- d := NewDecoder()
- d.SetMode(ModeExplicit)
-
- err := d.Decode(&test, values)
- Equal(t, err, nil)
- Equal(t, test.Name, "Joeybloggs")
- Equal(t, test.Age, 0)
-}
-
-func TestDecoderStructWithJSONTag(t *testing.T) {
- type Test struct {
- Name string `json:"name,omitempty"`
- Age int `json:",omitempty"`
- }
-
- values := map[string][]string{
- "name": {"Joeybloggs"},
- "Age": {"3"},
- }
-
- var test Test
-
- d := NewDecoder()
- d.SetTagName("json")
-
- err := d.Decode(&test, values)
- Equal(t, err, nil)
- Equal(t, test.Name, "Joeybloggs")
- Equal(t, test.Age, int(3))
-}
-
-func TestDecoderRegisterTagNameFunc(t *testing.T) {
-
- type Test struct {
- Value string `json:"val,omitempty"`
- Ignore string `json:"-"`
- }
-
- values := url.Values{
- "val": []string{"joeybloggs"},
- "Ignore": []string{"ignore"},
- }
-
- var test Test
-
- decoder := NewDecoder()
- decoder.RegisterTagNameFunc(func(fld reflect.StructField) string {
- name := fld.Tag.Get("json")
-
- if commaIndex := strings.Index(name, ","); commaIndex != -1 {
- name = name[:commaIndex]
- }
-
- return name
- })
-
- err := decoder.Decode(&test, values)
- Equal(t, err, nil)
- Equal(t, test.Value, "joeybloggs")
- Equal(t, test.Ignore, "")
-}
-
-func TestDecoderEmbedModes(t *testing.T) {
-
- type A struct {
- Field string
- }
-
- type B struct {
- A
- Field string
- }
-
- var b B
-
- decoder := NewDecoder()
-
- values := url.Values{
- "Field": []string{"Value"},
- }
-
- err := decoder.Decode(&b, values)
- Equal(t, err, nil)
- Equal(t, b.Field, "Value")
- Equal(t, b.A.Field, "Value")
-
- values = url.Values{
- "Field": []string{"B Val"},
- "A.Field": []string{"A Val"},
- }
-
- err = decoder.Decode(&b, values)
- Equal(t, err, nil)
- Equal(t, b.Field, "B Val")
- Equal(t, b.A.Field, "A Val")
-}
-
-func TestInterfaceDecoding(t *testing.T) {
-
- type Test struct {
- Iface interface{}
- }
-
- var b Test
-
- values := url.Values{
- "Iface": []string{"1"},
- }
-
- decoder := NewDecoder()
- err := decoder.Decode(&b, values)
- Equal(t, err, nil)
- Equal(t, b.Iface, "1")
-}
-
-func TestDecodeArrayBug(t *testing.T) {
- var data struct {
- A [2]string
- B [2]string
- C [2]string
- D [3]string
- E [3]string
- F [3]string
- G [3]string
- }
- decoder := NewDecoder()
- err := decoder.Decode(&data, url.Values{
- // Mixed types
- "A": {"10"},
- "A[1]": {"20"},
- // overflow
- "B": {"10", "20", "30"},
- "B[1]": {"31", "10", "20"},
- "B[2]": {"40"},
- // invalid array index
- "C[q]": {""},
- // index and mix tests
- "D": {"10"},
- "E": {"10", "20"},
- "F": {"10", "", "20"},
- "G": {"10"},
- "G[2]": {"20"},
- })
- NotEqual(t, err, nil)
- Equal(t, err.Error(), "Field Namespace:C ERROR:invalid array index 'q'")
- Equal(t, data.A[0], "10")
- Equal(t, data.A[1], "20")
- Equal(t, data.B[0], "10")
- Equal(t, data.B[1], "31")
- Equal(t, data.C[0], "")
- Equal(t, data.C[1], "")
- Equal(t, data.D[0], "10")
- Equal(t, data.D[1], "")
- Equal(t, data.D[2], "")
- Equal(t, data.E[0], "10")
- Equal(t, data.E[1], "20")
- Equal(t, data.E[2], "")
- Equal(t, data.F[0], "10")
- Equal(t, data.F[1], "")
- Equal(t, data.F[2], "20")
- Equal(t, data.G[0], "10")
- Equal(t, data.G[1], "")
- Equal(t, data.G[2], "20")
-}
-
-func TestDecoder_RegisterCustomTypeFuncOnSlice(t *testing.T) {
- type customString string
-
- type TestStruct struct {
- Slice []customString `form:"slice"`
- }
-
- d := NewDecoder()
- d.RegisterCustomTypeFunc(func(vals []string) (i interface{}, e error) {
- custom := make([]customString, 0, len(vals))
- for i := 0; i < len(vals); i++ {
- custom = append(custom, customString("custom"+vals[i]))
- }
- return custom, nil
- }, []customString{})
-
- var v TestStruct
- err := d.Decode(&v, url.Values{"slice": []string{"v1", "v2"}})
- Equal(t, err, nil)
- Equal(t, v.Slice, []customString{"customv1", "customv2"})
-}
-
-func TestDecoder_RegisterCustomTypeFunc(t *testing.T) {
- type customString string
-
- type TestStruct struct {
- Slice []customString `form:"slice"`
- }
-
- d := NewDecoder()
- d.RegisterCustomTypeFunc(func(vals []string) (i interface{}, e error) {
- return customString("custom" + vals[0]), nil
- }, customString(""))
-
- var v TestStruct
- err := d.Decode(&v, url.Values{"slice": []string{"v1", "v2"}})
- Equal(t, err, nil)
-
- Equal(t, v.Slice, []customString{"customv1", "customv2"})
-}
-
-func TestDecoder_EmptyArrayString(t *testing.T) {
- type T1 struct {
- F1 string `form:"F1"`
- }
- in := url.Values{
- "F1": []string{},
- }
-
- v := new(T1)
-
- d := NewDecoder()
- err := d.Decode(v, in)
- Equal(t, err, nil)
-}
-
-func TestDecoder_EmptyArrayBool(t *testing.T) {
- type T1 struct {
- F1 bool `form:"F1"`
- }
- in := url.Values{
- "F1": []string{},
- }
-
- v := new(T1)
- d := NewDecoder()
- err := d.Decode(v, in)
- Equal(t, err, nil)
-}
-
-func TestDecoder_InvalidSliceIndex(t *testing.T) {
- type PostsRequest struct {
- PostIds []string
- }
- in := url.Values{
- "PostIds[]": []string{"1", "2"},
- }
-
- v := new(PostsRequest)
- d := NewDecoder()
- err := d.Decode(v, in)
- NotEqual(t, err, nil)
- Equal(t, err.Error(), "Field Namespace:PostIds ERROR:invalid slice index ''")
-
- // No error with proper name
- type PostsRequest2 struct {
- PostIds []string `form:"PostIds[]"`
- }
-
- v2 := new(PostsRequest2)
- err = d.Decode(v2, in)
- Equal(t, err, nil)
- Equal(t, len(v2.PostIds), 2)
- Equal(t, v2.PostIds[0], "1")
- Equal(t, v2.PostIds[1], "2")
-}
diff --git a/toolkit/form/doc.go b/toolkit/form/doc.go
deleted file mode 100644
index f553dac2..00000000
--- a/toolkit/form/doc.go
+++ /dev/null
@@ -1,275 +0,0 @@
-/*
-Package form Decodes url.Values into Go value(s) and Encodes Go value(s) into url.Values.
-
-
-It has the following features:
-
- - Primitives types cause zero allocations.
- - Supports map of almost all types.
- - Supports both Numbered and Normal arrays eg. "Array[0]" and just "Array"
- with multiple values passed.
- - Slice honours the specified index. eg. if "Slice[2]" is the only Slice
- value passed down, it will be put at index 2; if slice isn't big enough
- it will be expanded.
- - Array honours the specified index. eg. if "Array[2]" is the only Array
- value passed down, it will be put at index 2; if array isn't big enough
- a warning will be printed and value ignored.
- - Only creates objects as necessary eg. if no `array` or `map` values are
- passed down, the `array` and `map` are left as their default values in
- the struct.
- - Allows for Custom Type registration.
- - Handles time.Time using RFC3339 time format by default,
- but can easily be changed by registering a Custom Type, see below.
- - Handles Encoding & Decoding of almost all Go types eg. can Decode into
- struct, array, map, int... and Encode a struct, array, map, int...
-
-Common Questions
-
-Questions
-
- Does it support encoding.TextUnmarshaler?
- No because TextUnmarshaler only accepts []byte but posted values can have
- multiple values, so is not suitable.
-
- Mixing array/slice with array[idx]/slice[idx], in which order are they parsed?
- array/slice then array[idx]/slice[idx]
-
-Supported Types
-
-out of the box supported types
-
- - string
- - bool
- - int, int8, int16, int32, int64
- - uint, uint8, uint16, uint32, uint64
- - float32, float64
- - struct and anonymous struct
- - interface{}
- - time.Time` - by default using RFC3339
- - a `pointer` to one of the above types
- - slice, array
- - map
- - `custom types` can override any of the above types
- - many other types may be supported inherently (eg. bson.ObjectId is
- type ObjectId string, which will get populated by the string type
-
- **NOTE**: map, struct and slice nesting are ad infinitum.
-
-Usage
-
-symbols
-
- - Use symbol `.` for separating fields/structs. (eg. `structfield.field`)
- - Use `[index or key]` for access to index of a slice/array or key for map.
- (eg. `arrayfield[0]`, `mapfield[keyvalue]`)
-
-html
-
-
-
-Example
-
-example decoding the above HTML
-
- package main
-
- import (
- "fmt"
- "log"
- "net/url"
-
- "github.com/go-playground/form/v4"
- )
-
- // Address contains address information
- type Address struct {
- Name string
- Phone string
- }
-
- // User contains user information
- type User struct {
- Name string
- Age uint8
- Gender string
- Address []Address
- Active bool `form:"active"`
- MapExample map[string]string
- NestedMap map[string]map[string]string
- NestedArray [][]string
- }
-
- // use a single instance of Decoder, it caches struct info
- var decoder *form.Decoder
-
- func main() {
- decoder = form.NewDecoder()
-
- // this simulates the results of http.Request's ParseForm() function
- values := parseForm()
-
- var user User
-
- // must pass a pointer
- err := decoder.Decode(&user, values)
- if err != nil {
- log.Panic(err)
- }
-
- fmt.Printf("%#v\n", user)
- }
-
- // this simulates the results of http.Request's ParseForm() function
- func parseForm() url.Values {
- return url.Values{
- "Name": []string{"joeybloggs"},
- "Age": []string{"3"},
- "Gender": []string{"Male"},
- "Address[0].Name": []string{"26 Here Blvd."},
- "Address[0].Phone": []string{"9(999)999-9999"},
- "Address[1].Name": []string{"26 There Blvd."},
- "Address[1].Phone": []string{"1(111)111-1111"},
- "active": []string{"true"},
- "MapExample[key]": []string{"value"},
- "NestedMap[key][key]": []string{"value"},
- "NestedArray[0][0]": []string{"value"},
- }
- }
-
-example encoding
-
- package main
-
- import (
- "fmt"
- "log"
-
- "github.com/go-playground/form/v4"
- )
-
- // Address contains address information
- type Address struct {
- Name string
- Phone string
- }
-
- // User contains user information
- type User struct {
- Name string
- Age uint8
- Gender string
- Address []Address
- Active bool `form:"active"`
- MapExample map[string]string
- NestedMap map[string]map[string]string
- NestedArray [][]string
- }
-
- // use a single instance of Encoder, it caches struct info
- var encoder *form.Encoder
-
- func main() {
- encoder = form.NewEncoder()
-
- user := User{
- Name: "joeybloggs",
- Age: 3,
- Gender: "Male",
- Address: []Address{
- {Name: "26 Here Blvd.", Phone: "9(999)999-9999"},
- {Name: "26 There Blvd.", Phone: "1(111)111-1111"},
- },
- Active: true,
- MapExample: map[string]string{"key": "value"},
- NestedMap: map[string]map[string]string{"key": {"key": "value"}},
- NestedArray: [][]string{{"value"}},
- }
-
- // must pass a pointer
- values, err := encoder.Encode(&user)
- if err != nil {
- log.Panic(err)
- }
-
- fmt.Printf("%#v\n", values)
- }
-
-
-Registering Custom Types
-
-Decoder
-
- decoder.RegisterCustomTypeFunc(func(vals []string) (interface{}, error) {
- return time.Parse("2006-01-02", vals[0])
- }, time.Time{})
-
- ADDITIONAL: if a struct type is registered, the function will only be called
- if a url.Value exists for the struct and not just the struct fields
- eg. url.Values{"User":"Name%3Djoeybloggs"} will call the custom type function
- with 'User' as the type, however url.Values{"User.Name":"joeybloggs"} will not.
-
-Encoder
-
- encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {
- return []string{x.(time.Time).Format("2006-01-02")}, nil
- }, time.Time{})
-
-
-Ignoring Fields
-
-you can tell form to ignore fields using `-` in the tag
-
- type MyStruct struct {
- Field string `form:"-"`
- }
-
-Omitempty
-
-you can tell form to omit empty fields using `,omitempty` or `FieldName,omitempty` in the tag
-
- type MyStruct struct {
- Field string `form:",omitempty"`
- Field2 string `form:"CustomFieldName,omitempty"`
- }
-
-
-Notes
-
-To maximize compatibility with other systems the Encoder attempts
-to avoid using array indexes in url.Values if at all possible.
-
- eg.
-
- // A struct field of
- Field []string{"1", "2", "3"}
-
- // will be output a url.Value as
- "Field": []string{"1", "2", "3"}
-
- and not
- "Field[0]": []string{"1"}
- "Field[1]": []string{"2"}
- "Field[2]": []string{"3"}
-
- // however there are times where it is unavoidable, like with pointers
- i := int(1)
- Field []*string{nil, nil, &i}
-
- // to avoid index 1 and 2 must use index
- "Field[2]": []string{"1"}
-
-*/
-package form
diff --git a/toolkit/form/encoder.go b/toolkit/form/encoder.go
deleted file mode 100644
index 6de26cd2..00000000
--- a/toolkit/form/encoder.go
+++ /dev/null
@@ -1,261 +0,0 @@
-package form
-
-import (
- "fmt"
- "net/url"
- "github.com/goccy/go-reflect"
- "strconv"
- "time"
-)
-
-type encoder struct {
- e *Encoder
- errs EncodeErrors
- values url.Values
- namespace []byte
-}
-
-func (e *encoder) setError(namespace []byte, err error) {
- if e.errs == nil {
- e.errs = make(EncodeErrors)
- }
-
- e.errs[string(namespace)] = err
-}
-
-func (e *encoder) setVal(namespace []byte, idx int, vals ...string) {
-
- arr, ok := e.values[string(namespace)]
- if ok {
- arr = append(arr, vals...)
- } else {
- arr = vals
- }
-
- e.values[string(namespace)] = arr
-}
-
-func (e *encoder) traverseStruct(v reflect.Value, namespace []byte, idx int) {
-
- typ := v.Type()
- l := len(namespace)
- first := l == 0
-
- // anonymous structs will still work for caching as the whole definition is stored
- // including tags
- s, ok := e.e.structCache.Get(typ)
- if !ok {
- s = e.e.structCache.parseStruct(e.e.mode, v, typ, e.e.tagName)
- }
-
- for _, f := range s.fields {
- namespace = namespace[:l]
-
- if f.isAnonymous && e.e.embedAnonymous {
- e.setFieldByType(v.Field(f.idx), namespace, idx, f.isOmitEmpty)
- continue
- }
-
- if first {
- namespace = append(namespace, f.name...)
- } else {
- namespace = append(namespace, e.e.namespacePrefix...)
- namespace = append(namespace, f.name...)
- namespace = append(namespace, e.e.namespaceSuffix...)
- }
-
- e.setFieldByType(v.Field(f.idx), namespace, idx, f.isOmitEmpty)
- }
-}
-
-func (e *encoder) setFieldByType(current reflect.Value, namespace []byte, idx int, isOmitEmpty bool) {
-
- if idx > -1 && current.Kind() == reflect.Ptr {
- namespace = append(namespace, '[')
- namespace = strconv.AppendInt(namespace, int64(idx), 10)
- namespace = append(namespace, ']')
- idx = -2
- }
-
- if isOmitEmpty && !hasValue(current) {
- return
- }
- v, kind := ExtractType(current)
-
- if e.e.customTypeFuncs != nil {
-
- if cf, ok := e.e.customTypeFuncs[v.Type()]; ok {
-
- arr, err := cf(v.Interface())
- if err != nil {
- e.setError(namespace, err)
- return
- }
-
- if idx > -1 {
- namespace = append(namespace, '[')
- namespace = strconv.AppendInt(namespace, int64(idx), 10)
- namespace = append(namespace, ']')
- }
-
- e.setVal(namespace, idx, arr...)
- return
- }
- }
-
- switch kind {
- case reflect.Ptr, reflect.Interface, reflect.Invalid:
- return
-
- case reflect.String:
-
- e.setVal(namespace, idx, v.String())
-
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
-
- e.setVal(namespace, idx, strconv.FormatUint(v.Uint(), 10))
-
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
-
- e.setVal(namespace, idx, strconv.FormatInt(v.Int(), 10))
-
- case reflect.Float32:
-
- e.setVal(namespace, idx, strconv.FormatFloat(v.Float(), 'f', -1, 32))
-
- case reflect.Float64:
-
- e.setVal(namespace, idx, strconv.FormatFloat(v.Float(), 'f', -1, 64))
-
- case reflect.Bool:
-
- e.setVal(namespace, idx, strconv.FormatBool(v.Bool()))
-
- case reflect.Slice, reflect.Array:
-
- if idx == -1 {
-
- for i := 0; i < v.Len(); i++ {
- e.setFieldByType(v.Index(i), namespace, i, false)
- }
-
- return
- }
-
- if idx > -1 {
- namespace = append(namespace, '[')
- namespace = strconv.AppendInt(namespace, int64(idx), 10)
- namespace = append(namespace, ']')
- }
-
- namespace = append(namespace, '[')
- l := len(namespace)
-
- for i := 0; i < v.Len(); i++ {
- namespace = namespace[:l]
- namespace = strconv.AppendInt(namespace, int64(i), 10)
- namespace = append(namespace, ']')
- e.setFieldByType(v.Index(i), namespace, -2, false)
- }
-
- case reflect.Map:
-
- if idx > -1 {
- namespace = append(namespace, '[')
- namespace = strconv.AppendInt(namespace, int64(idx), 10)
- namespace = append(namespace, ']')
- }
-
- var valid bool
- var s string
- l := len(namespace)
-
- for _, key := range v.MapKeys() {
-
- namespace = namespace[:l]
-
- if s, valid = e.getMapKey(key, namespace); !valid {
- continue
- }
-
- namespace = append(namespace, '[')
- namespace = append(namespace, s...)
- namespace = append(namespace, ']')
-
- e.setFieldByType(v.MapIndex(key), namespace, -2, false)
- }
-
- case reflect.Struct:
-
- // if we get here then no custom time function declared so use RFC3339 by default
- if v.Type() == timeType {
-
- if idx > -1 {
- namespace = append(namespace, '[')
- namespace = strconv.AppendInt(namespace, int64(idx), 10)
- namespace = append(namespace, ']')
- }
-
- e.setVal(namespace, idx, v.Interface().(time.Time).Format(time.RFC3339))
- return
- }
-
- if idx == -1 {
- e.traverseStruct(v, namespace, idx)
- return
- }
-
- if idx > -1 {
- namespace = append(namespace, '[')
- namespace = strconv.AppendInt(namespace, int64(idx), 10)
- namespace = append(namespace, ']')
- }
-
- e.traverseStruct(v, namespace, -2)
- }
-}
-
-func (e *encoder) getMapKey(key reflect.Value, namespace []byte) (string, bool) {
-
- v, kind := ExtractType(key)
-
- if e.e.customTypeFuncs != nil {
-
- if cf, ok := e.e.customTypeFuncs[v.Type()]; ok {
- arr, err := cf(v.Interface())
- if err != nil {
- e.setError(namespace, err)
- return "", false
- }
-
- return arr[0], true
- }
- }
-
- switch kind {
- case reflect.Interface, reflect.Ptr:
- return "", false
-
- case reflect.String:
- return v.String(), true
-
- case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
- return strconv.FormatUint(v.Uint(), 10), true
-
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
- return strconv.FormatInt(v.Int(), 10), true
-
- case reflect.Float32:
- return strconv.FormatFloat(v.Float(), 'f', -1, 32), true
-
- case reflect.Float64:
- return strconv.FormatFloat(v.Float(), 'f', -1, 64), true
-
- case reflect.Bool:
- return strconv.FormatBool(v.Bool()), true
-
- default:
- e.setError(namespace, fmt.Errorf("Unsupported Map Key '%v' Namespace '%s'", v.String(), namespace))
- return "", false
- }
-}
diff --git a/toolkit/form/encoder_test.go b/toolkit/form/encoder_test.go
deleted file mode 100644
index 32315610..00000000
--- a/toolkit/form/encoder_test.go
+++ /dev/null
@@ -1,1609 +0,0 @@
-package form
-
-import (
- "errors"
- "github.com/goccy/go-reflect"
- "strings"
- "testing"
- "time"
-
- . "github.com/go-playground/assert/v2"
-)
-
-// NOTES:
-// - Run "go test" to run tests
-// - Run "gocov test | gocov report" to report on test converage by file
-// - Run "gocov test | gocov annotate -" to report on all code and functions, those ,marked with "MISS" were never called
-//
-// or
-//
-// -- may be a good idea to change to output path to somewherelike /tmp
-// go test -coverprofile cover.out && go tool cover -html=cover.out -o cover.html
-//
-//
-// go test -cpuprofile cpu.out
-// ./validator.test -test.bench=. -test.cpuprofile=cpu.prof
-// go tool pprof validator.test cpu.prof
-//
-//
-// go test -memprofile mem.out
-
-func TestEncoderInt(t *testing.T) {
-
- type TestInt struct {
- Int int
- Int8 int8
- Int16 int16
- Int32 int32
- Int64 int64
- IntPtr *int
- Int8Ptr *int8
- Int16Ptr *int16
- Int32Ptr *int32
- Int64Ptr *int64
- IntArray []int
- IntPtrArray []*int
- IntArrayArray [][]int
- IntPtrArrayArray [][]*int
- IntMap map[int]int
- IntPtrMap map[*int]*int
- NoValue int
- NoPtrValue *int
- }
-
- i := int(3)
- i8 := int8(3)
- i16 := int16(3)
- i32 := int32(3)
- i64 := int64(3)
-
- zero := int(0)
- one := int(1)
- two := int(2)
- three := int(3)
-
- test := TestInt{
- Int: i,
- Int8: i8,
- Int16: i16,
- Int32: i32,
- Int64: i64,
- IntPtr: &i,
- Int8Ptr: &i8,
- Int16Ptr: &i16,
- Int32Ptr: &i32,
- Int64Ptr: &i64,
- IntArray: []int{one, two, three},
- IntPtrArray: []*int{&one, &two, &three},
- IntArrayArray: [][]int{{one, zero, three}},
- IntPtrArrayArray: [][]*int{{&one, &zero, &three}},
- IntMap: map[int]int{one: three, zero: two},
- IntPtrMap: map[*int]*int{&one: &three, &zero: &two},
- }
-
- encoder := NewEncoder()
- values, errs := encoder.Encode(test)
- Equal(t, errs, nil)
- Equal(t, len(values), 25)
-
- val, ok := values["Int8"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Int8"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Int16"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Int32"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Int64"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Int8"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Int8Ptr"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Int16Ptr"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Int32Ptr"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Int64Ptr"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["IntArray"]
- Equal(t, ok, true)
- Equal(t, len(val), 3)
- Equal(t, val[0], "1")
- Equal(t, val[1], "2")
- Equal(t, val[2], "3")
-
- val, ok = values["IntPtrArray[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1")
-
- val, ok = values["IntPtrArray[1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2")
-
- val, ok = values["IntPtrArray[2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["IntArrayArray[0][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1")
-
- val, ok = values["IntArrayArray[0][1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "0")
-
- val, ok = values["IntArrayArray[0][2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["IntPtrArrayArray[0][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1")
-
- val, ok = values["IntPtrArrayArray[0][1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "0")
-
- val, ok = values["IntPtrArrayArray[0][2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["IntMap[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2")
-
- val, ok = values["IntMap[1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["IntPtrMap[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2")
-
- val, ok = values["IntPtrMap[1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["NoValue"]
- Equal(t, ok, true)
- Equal(t, val[0], "0")
-
- _, ok = values["NoPtrValue"]
- Equal(t, ok, false)
-}
-
-func TestEncoderUint(t *testing.T) {
-
- type TestUint struct {
- Uint uint
- Uint8 uint8
- Uint16 uint16
- Uint32 uint32
- Uint64 uint64
- UintPtr *uint
- Uint8Ptr *uint8
- Uint16Ptr *uint16
- Uint32Ptr *uint32
- Uint64Ptr *uint64
- UintArray []uint
- UintPtrArray []*uint
- UintArrayArray [][]uint
- UintPtrArrayArray [][]*uint
- UintMap map[uint]uint
- UintPtrMap map[*uint]*uint
- NoValue uint
- NoPtrValue *uint
- }
-
- i := uint(3)
- i8 := uint8(3)
- i16 := uint16(3)
- i32 := uint32(3)
- i64 := uint64(3)
-
- zero := uint(0)
- one := uint(1)
- two := uint(2)
- three := uint(3)
-
- test := TestUint{
- Uint: i,
- Uint8: i8,
- Uint16: i16,
- Uint32: i32,
- Uint64: i64,
- UintPtr: &i,
- Uint8Ptr: &i8,
- Uint16Ptr: &i16,
- Uint32Ptr: &i32,
- Uint64Ptr: &i64,
- UintArray: []uint{one, two, three},
- UintPtrArray: []*uint{&one, &two, &three},
- UintArrayArray: [][]uint{{one, zero, three}},
- UintPtrArrayArray: [][]*uint{{&one, &zero, &three}},
- UintMap: map[uint]uint{one: three, zero: two},
- UintPtrMap: map[*uint]*uint{&one: &three, &zero: &two},
- }
-
- encoder := NewEncoder()
- values, errs := encoder.Encode(test)
-
- Equal(t, errs, nil)
- Equal(t, len(values), 25)
-
- val, ok := values["Uint8"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Uint8"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Uint16"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Uint32"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Uint64"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Uint8"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Uint8Ptr"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Uint16Ptr"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Uint32Ptr"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["Uint64Ptr"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["UintArray"]
- Equal(t, ok, true)
- Equal(t, len(val), 3)
- Equal(t, val[0], "1")
- Equal(t, val[1], "2")
- Equal(t, val[2], "3")
-
- val, ok = values["UintPtrArray[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1")
-
- val, ok = values["UintPtrArray[1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2")
-
- val, ok = values["UintPtrArray[2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["UintArrayArray[0][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1")
-
- val, ok = values["UintArrayArray[0][1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "0")
-
- val, ok = values["UintArrayArray[0][2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["UintPtrArrayArray[0][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1")
-
- val, ok = values["UintPtrArrayArray[0][1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "0")
-
- val, ok = values["UintPtrArrayArray[0][2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["UintMap[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2")
-
- val, ok = values["UintMap[1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["UintPtrMap[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2")
-
- val, ok = values["UintPtrMap[1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["NoValue"]
- Equal(t, ok, true)
- Equal(t, val[0], "0")
-
- _, ok = values["NoPtrValue"]
- Equal(t, ok, false)
-}
-
-func TestEncoderString(t *testing.T) {
-
- type TestString struct {
- String string
- StringPtr *string
- StringArray []string
- StringPtrArray []*string
- StringArrayArray [][]string
- StringPtrArrayArray [][]*string
- StringMap map[string]string
- StringPtrMap map[*string]*string
- NoValue string
- }
-
- one := "1"
- two := "2"
- three := "3"
-
- test := TestString{
- String: three,
- StringPtr: &two,
- StringArray: []string{one, "", three},
- StringPtrArray: []*string{&one, nil, &three},
- StringArrayArray: [][]string{{one, "", three}, nil, {one}},
- StringPtrArrayArray: [][]*string{{&one, nil, &three}, nil, {&one}},
- StringMap: map[string]string{one: three, three: two},
- StringPtrMap: map[*string]*string{&one: &three, &three: &two},
- }
-
- encoder := NewEncoder()
- values, errs := encoder.Encode(test)
-
- Equal(t, errs, nil)
- Equal(t, len(values), 17)
-
- val, ok := values["String"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["StringPtr"]
- Equal(t, ok, true)
- Equal(t, val[0], "2")
-
- val, ok = values["StringArray"]
- Equal(t, ok, true)
- Equal(t, len(val), 3)
- Equal(t, val[0], "1")
- Equal(t, val[1], "")
- Equal(t, val[2], "3")
-
- val, ok = values["StringPtr"]
- Equal(t, ok, true)
- Equal(t, val[0], "2")
-
- val, ok = values["StringPtrArray[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1")
-
- _, ok = values["StringPtrArray[1]"]
- Equal(t, ok, false)
-
- val, ok = values["StringPtrArray[2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["StringPtrArray[2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["StringArrayArray[0][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1")
-
- val, ok = values["StringArrayArray[0][1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "")
-
- val, ok = values["StringArrayArray[0][2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- _, ok = values["StringArrayArray[1][1]"]
- Equal(t, ok, false)
-
- val, ok = values["StringArrayArray[2][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1")
-
- val, ok = values["StringPtrArrayArray[0][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1")
-
- _, ok = values["StringPtrArrayArray[0][1]"]
- Equal(t, ok, false)
-
- val, ok = values["StringPtrArrayArray[0][2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- _, ok = values["StringPtrArrayArray[1][1]"]
- Equal(t, ok, false)
-
- val, ok = values["StringPtrArrayArray[2][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1")
-
- val, ok = values["StringMap[1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["StringMap[3]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2")
-
- val, ok = values["StringPtrMap[1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["StringPtrMap[3]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2")
-
- val, ok = values["NoValue"]
- Equal(t, ok, true)
- Equal(t, val[0], "")
-}
-
-func TestEncoderFloat(t *testing.T) {
-
- type TestFloat struct {
- Float32 float32
- Float32Ptr *float32
- Float64 float64
- Float64Ptr *float64
- Float32Array []float32
- Float64Array []float64
- Float32PtrArray []*float32
- Float64PtrArray []*float64
- Float32ArrayArray [][]float32
- Float64ArrayArray [][]float64
- Float32PtrArrayArray [][]*float32
- Float64PtrArrayArray [][]*float64
- Float32Map map[float32]float32
- Float64Map map[float64]float64
- Float32PtrMap map[*float32]*float32
- Float64PtrMap map[*float64]*float64
- NoValue float32
- }
-
- one32 := float32(1.1)
- two32 := float32(2.2)
- three32 := float32(3.3)
- one64 := float64(1.1)
- two64 := float64(2.2)
- three64 := float64(3.3)
-
- test := TestFloat{
- Float32: three32,
- Float32Ptr: &three32,
- Float64: three64,
- Float64Ptr: &three64,
- Float32Array: []float32{one32, two32, three32},
- Float64Array: []float64{one64, two64, three64},
- Float32PtrArray: []*float32{&one32, &two32, &three32},
- Float64PtrArray: []*float64{&one64, &two64, &three64},
- Float32ArrayArray: [][]float32{{one32, 0, three32}, nil, {one32}},
- Float64ArrayArray: [][]float64{{one64, 0, three64}, nil, {one64}},
- Float32PtrArrayArray: [][]*float32{{&one32, nil, &three32}, nil, {&one32}},
- Float64PtrArrayArray: [][]*float64{{&one64, nil, &three64}, nil, {&one64}},
- Float32Map: map[float32]float32{one32: three32, three32: two32},
- Float64Map: map[float64]float64{one64: three64, three64: two64},
- Float32PtrMap: map[*float32]*float32{&one32: &three32, &three32: &two32},
- Float64PtrMap: map[*float64]*float64{&one64: &three64, &three64: &two64},
- }
-
- encoder := NewEncoder()
- values, errs := encoder.Encode(test)
-
- Equal(t, errs, nil)
- Equal(t, len(values), 35)
-
- val, ok := values["Float32"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- val, ok = values["Float32Ptr"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- val, ok = values["Float64"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- val, ok = values["Float64Ptr"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- val, ok = values["Float32Array"]
- Equal(t, ok, true)
- Equal(t, len(val), 3)
- Equal(t, val[0], "1.1")
- Equal(t, val[1], "2.2")
- Equal(t, val[2], "3.3")
-
- val, ok = values["Float64Array"]
- Equal(t, ok, true)
- Equal(t, len(val), 3)
- Equal(t, val[0], "1.1")
- Equal(t, val[1], "2.2")
- Equal(t, val[2], "3.3")
-
- val, ok = values["Float32PtrArray[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1.1")
-
- val, ok = values["Float32PtrArray[1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2.2")
-
- val, ok = values["Float32PtrArray[2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- val, ok = values["Float64PtrArray[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1.1")
-
- val, ok = values["Float64PtrArray[1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2.2")
-
- val, ok = values["Float64PtrArray[2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- val, ok = values["Float32ArrayArray[0][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1.1")
-
- val, ok = values["Float32ArrayArray[0][1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "0")
-
- val, ok = values["Float32ArrayArray[0][2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- _, ok = values["Float32ArrayArray[1][0]"]
- Equal(t, ok, false)
-
- val, ok = values["Float32ArrayArray[2][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1.1")
-
- val, ok = values["Float64ArrayArray[0][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1.1")
-
- val, ok = values["Float64ArrayArray[0][1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "0")
-
- val, ok = values["Float64ArrayArray[0][2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- _, ok = values["Float64ArrayArray[1][0]"]
- Equal(t, ok, false)
-
- val, ok = values["Float64ArrayArray[2][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1.1")
-
- val, ok = values["Float32PtrArrayArray[0][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1.1")
-
- _, ok = values["Float32PtrArrayArray[0][1]"]
- Equal(t, ok, false)
-
- val, ok = values["Float32PtrArrayArray[0][2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- _, ok = values["Float32PtrArrayArray[1][0]"]
- Equal(t, ok, false)
-
- val, ok = values["Float32PtrArrayArray[2][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1.1")
-
- val, ok = values["Float64PtrArrayArray[0][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1.1")
-
- _, ok = values["Float64PtrArrayArray[0][1]"]
- Equal(t, ok, false)
-
- val, ok = values["Float64PtrArrayArray[0][2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- _, ok = values["Float64PtrArrayArray[1][0]"]
- Equal(t, ok, false)
-
- val, ok = values["Float64PtrArrayArray[2][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1.1")
-
- val, ok = values["Float32Map[1.1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- val, ok = values["Float32Map[3.3]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2.2")
-
- val, ok = values["Float64Map[1.1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- val, ok = values["Float64Map[3.3]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2.2")
-
- val, ok = values["Float32PtrMap[1.1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- val, ok = values["Float32PtrMap[3.3]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2.2")
-
- val, ok = values["Float64PtrMap[1.1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3.3")
-
- val, ok = values["Float64PtrMap[3.3]"]
- Equal(t, ok, true)
- Equal(t, val[0], "2.2")
-
- val, ok = values["NoValue"]
- Equal(t, ok, true)
- Equal(t, val[0], "0")
-}
-
-func TestEncoderBool(t *testing.T) {
-
- type TestBool struct {
- Bool bool
- BoolPtr *bool
- BoolArray []bool
- BoolPtrArray []*bool
- BoolArrayArray [][]bool
- BoolPtrArrayArray [][]*bool
- BoolMap map[bool]bool
- BoolPtrMap map[*bool]*bool
- NoValue bool
- }
-
- tr := true
- fa := false
-
- test := TestBool{
- Bool: tr,
- BoolPtr: &tr,
- BoolArray: []bool{fa, tr, tr},
- BoolPtrArray: []*bool{&fa, nil, &tr},
- BoolArrayArray: [][]bool{{tr, fa, tr}, nil, {tr}},
- BoolPtrArrayArray: [][]*bool{{&tr, nil, &tr}, nil, {&tr}},
- BoolMap: map[bool]bool{tr: fa, fa: true},
- BoolPtrMap: map[*bool]*bool{&tr: &fa, &fa: &tr},
- }
-
- encoder := NewEncoder()
- values, errs := encoder.Encode(test)
-
- Equal(t, errs, nil)
- Equal(t, len(values), 17)
-
- val, ok := values["Bool"]
- Equal(t, ok, true)
- Equal(t, val[0], "true")
-
- val, ok = values["BoolPtr"]
- Equal(t, ok, true)
- Equal(t, val[0], "true")
-
- val, ok = values["BoolArray"]
- Equal(t, ok, true)
- Equal(t, len(val), 3)
- Equal(t, val[0], "false")
- Equal(t, val[1], "true")
- Equal(t, val[2], "true")
-
- val, ok = values["BoolPtrArray[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "false")
-
- _, ok = values["BoolPtrArray[1]"]
- Equal(t, ok, false)
-
- val, ok = values["BoolPtrArray[2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "true")
-
- val, ok = values["BoolArrayArray[0][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "true")
-
- val, ok = values["BoolArrayArray[0][1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "false")
-
- val, ok = values["BoolArrayArray[0][2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "true")
-
- _, ok = values["BoolArrayArray[1][0]"]
- Equal(t, ok, false)
-
- val, ok = values["BoolArrayArray[2][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "true")
-
- val, ok = values["BoolPtrArrayArray[0][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "true")
-
- _, ok = values["BoolPtrArrayArray[0][1]"]
- Equal(t, ok, false)
-
- val, ok = values["BoolPtrArrayArray[0][2]"]
- Equal(t, ok, true)
- Equal(t, val[0], "true")
-
- _, ok = values["BoolPtrArrayArray[1][0]"]
- Equal(t, ok, false)
-
- val, ok = values["BoolPtrArrayArray[2][0]"]
- Equal(t, ok, true)
- Equal(t, val[0], "true")
-
- val, ok = values["BoolMap[true]"]
- Equal(t, ok, true)
- Equal(t, val[0], "false")
-
- val, ok = values["BoolMap[false]"]
- Equal(t, ok, true)
- Equal(t, val[0], "true")
-
- val, ok = values["BoolPtrMap[true]"]
- Equal(t, ok, true)
- Equal(t, val[0], "false")
-
- val, ok = values["BoolPtrMap[false]"]
- Equal(t, ok, true)
- Equal(t, val[0], "true")
-}
-
-func TestEncoderStruct(t *testing.T) {
-
- type Phone struct {
- Number string
- }
-
- type TestStruct struct {
- Name string `form:"name"`
- Ignore string `form:"-"`
- NonNilPtr *Phone
- Phone []Phone
- PhonePtr []*Phone
-
- Anonymous struct {
- Value string
- Ignore string `form:"-"`
- unexposed string
- }
- Time time.Time
- TimePtr *time.Time
- unexposed string
- Invalid interface{}
- ExistingMap map[string]string `form:"mp"`
- MapNoValue map[int]int
- Array []string
- ZeroLengthArray []string
- IfaceNonNil interface{}
- IfaceInvalid interface{}
- TimeMapKey map[time.Time]string
- ArrayMap []map[int]int
- ArrayTime []time.Time
- }
-
- tm, err := time.Parse("2006-01-02", "2016-01-02")
- Equal(t, err, nil)
-
- test := &TestStruct{
- Name: "joeybloggs",
- Ignore: "ignore",
- NonNilPtr: new(Phone),
- Phone: []Phone{
- {Number: "1(111)111-1111"},
- {Number: "9(999)999-9999"},
- },
- PhonePtr: []*Phone{
- {Number: "1(111)111-1111"},
- {Number: "9(999)999-9999"},
- },
- Anonymous: struct {
- Value string
- Ignore string `form:"-"`
- unexposed string
- }{
- Value: "Anon",
- },
- Time: tm,
- TimePtr: &tm,
- unexposed: "unexposed field",
- ExistingMap: map[string]string{"existingkey": "existingvalue", "key": "value"},
- Array: []string{"1", "2"},
- ZeroLengthArray: []string{},
- IfaceNonNil: new(Phone),
- IfaceInvalid: nil,
- TimeMapKey: map[time.Time]string{tm: "time"},
- ArrayMap: []map[int]int{{1: 3}},
- ArrayTime: []time.Time{tm},
- }
-
- encoder := NewEncoder()
- encoder.SetTagName("form")
- encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {
- return []string{x.(time.Time).Format("2006-01-02")}, nil
- }, time.Time{})
-
- values, errs := encoder.Encode(test)
-
- Equal(t, errs, nil)
- Equal(t, len(values), 16)
-
- val, ok := values["name"]
- Equal(t, ok, true)
- Equal(t, val[0], "joeybloggs")
-
- _, ok = values["Ignore"]
- Equal(t, ok, false)
-
- val, ok = values["NonNilPtr.Number"]
- Equal(t, ok, true)
- Equal(t, val[0], "")
-
- val, ok = values["Phone[0].Number"]
- Equal(t, ok, true)
- Equal(t, val[0], "1(111)111-1111")
-
- val, ok = values["Phone[1].Number"]
- Equal(t, ok, true)
- Equal(t, val[0], "9(999)999-9999")
-
- val, ok = values["PhonePtr[0].Number"]
- Equal(t, ok, true)
- Equal(t, val[0], "1(111)111-1111")
-
- val, ok = values["PhonePtr[1].Number"]
- Equal(t, ok, true)
- Equal(t, val[0], "9(999)999-9999")
-
- val, ok = values["Anonymous.Value"]
- Equal(t, ok, true)
- Equal(t, val[0], "Anon")
-
- _, ok = values["Anonymous.Ignore"]
- Equal(t, ok, false)
-
- _, ok = values["Anonymous.unexposed"]
- Equal(t, ok, false)
-
- val, ok = values["Time"]
- Equal(t, ok, true)
- Equal(t, val[0], tm.Format("2006-01-02"))
-
- val, ok = values["TimePtr"]
- Equal(t, ok, true)
- Equal(t, val[0], tm.Format("2006-01-02"))
-
- _, ok = values["unexposed"]
- Equal(t, ok, false)
-
- val, ok = values["mp[existingkey]"]
- Equal(t, ok, true)
- Equal(t, val[0], "existingvalue")
-
- val, ok = values["mp[key]"]
- Equal(t, ok, true)
- Equal(t, val[0], "value")
-
- val, ok = values["Array"]
- Equal(t, ok, true)
- Equal(t, len(val), 2)
- Equal(t, val[0], "1")
- Equal(t, val[1], "2")
-
- _, ok = values["ZeroLengthArray"]
- Equal(t, ok, false)
-
- val, ok = values["IfaceNonNil.Number"]
- Equal(t, ok, true)
- Equal(t, val[0], "")
-
- _, ok = values["IfaceInvalid"]
- Equal(t, ok, false)
-
- val, ok = values["IfaceNonNil.Number"]
- Equal(t, ok, true)
- Equal(t, val[0], "")
-
- val, ok = values["TimeMapKey["+tm.Format("2006-01-02")+"]"]
- Equal(t, ok, true)
- Equal(t, val[0], "time")
-
- val, ok = values["ArrayMap[0][1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["ArrayTime[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], tm.Format("2006-01-02"))
-
- s := struct {
- Value string
- Ignore string `form:"-"`
- unexposed string
- ArrayTime []time.Time
- }{
- Value: "tval",
- Ignore: "ignore",
- unexposed: "unexp",
- ArrayTime: []time.Time{tm},
- }
-
- encoder = NewEncoder()
- values, errs = encoder.Encode(&s)
- Equal(t, errs, nil)
- Equal(t, len(values), 2)
-
- val, ok = values["Value"]
- Equal(t, ok, true)
- Equal(t, val[0], "tval")
-
- _, ok = values["Ignore"]
- Equal(t, ok, false)
-
- _, ok = values["unexposed"]
- Equal(t, ok, false)
-
- val, ok = values["ArrayTime[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], tm.Format(time.RFC3339))
-}
-
-func TestEncoderStructCustomNamespace(t *testing.T) {
-
- type Phone struct {
- Number string
- }
-
- type TestStruct struct {
- Name string `form:"name"`
- Ignore string `form:"-"`
- NonNilPtr *Phone
- Phone []Phone
- PhonePtr []*Phone
-
- Anonymous struct {
- Value string
- Ignore string `form:"-"`
- unexposed string
- }
- Time time.Time
- TimePtr *time.Time
- unexposed string
- Invalid interface{}
- ExistingMap map[string]string `form:"mp"`
- MapNoValue map[int]int
- Array []string
- ZeroLengthArray []string
- IfaceNonNil interface{}
- IfaceInvalid interface{}
- TimeMapKey map[time.Time]string
- ArrayMap []map[int]int
- ArrayTime []time.Time
- }
-
- tm, err := time.Parse("2006-01-02", "2016-01-02")
- Equal(t, err, nil)
-
- test := &TestStruct{
- Name: "joeybloggs",
- Ignore: "ignore",
- NonNilPtr: new(Phone),
- Phone: []Phone{
- {Number: "1(111)111-1111"},
- {Number: "9(999)999-9999"},
- },
- PhonePtr: []*Phone{
- {Number: "1(111)111-1111"},
- {Number: "9(999)999-9999"},
- },
- Anonymous: struct {
- Value string
- Ignore string `form:"-"`
- unexposed string
- }{
- Value: "Anon",
- },
- Time: tm,
- TimePtr: &tm,
- unexposed: "unexposed field",
- ExistingMap: map[string]string{"existingkey": "existingvalue", "key": "value"},
- Array: []string{"1", "2"},
- ZeroLengthArray: []string{},
- IfaceNonNil: new(Phone),
- IfaceInvalid: nil,
- TimeMapKey: map[time.Time]string{tm: "time"},
- ArrayMap: []map[int]int{{1: 3}},
- ArrayTime: []time.Time{tm},
- }
-
- encoder := NewEncoder()
- encoder.SetTagName("form")
- encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {
- return []string{x.(time.Time).Format("2006-01-02")}, nil
- }, time.Time{})
- encoder.SetNamespacePrefix("[")
- encoder.SetNamespaceSuffix("]")
-
- values, errs := encoder.Encode(test)
-
- Equal(t, errs, nil)
- Equal(t, len(values), 16)
-
- val, ok := values["name"]
- Equal(t, ok, true)
- Equal(t, val[0], "joeybloggs")
-
- _, ok = values["Ignore"]
- Equal(t, ok, false)
-
- val, ok = values["NonNilPtr[Number]"]
- Equal(t, ok, true)
- Equal(t, val[0], "")
-
- val, ok = values["Phone[0][Number]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1(111)111-1111")
-
- val, ok = values["Phone[1][Number]"]
- Equal(t, ok, true)
- Equal(t, val[0], "9(999)999-9999")
-
- val, ok = values["PhonePtr[0][Number]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1(111)111-1111")
-
- val, ok = values["PhonePtr[1][Number]"]
- Equal(t, ok, true)
- Equal(t, val[0], "9(999)999-9999")
-
- val, ok = values["Anonymous[Value]"]
- Equal(t, ok, true)
- Equal(t, val[0], "Anon")
-
- _, ok = values["Anonymous[Ignore]"]
- Equal(t, ok, false)
-
- _, ok = values["Anonymous.unexposed"]
- Equal(t, ok, false)
-
- val, ok = values["Time"]
- Equal(t, ok, true)
- Equal(t, val[0], tm.Format("2006-01-02"))
-
- val, ok = values["TimePtr"]
- Equal(t, ok, true)
- Equal(t, val[0], tm.Format("2006-01-02"))
-
- _, ok = values["unexposed"]
- Equal(t, ok, false)
-
- val, ok = values["mp[existingkey]"]
- Equal(t, ok, true)
- Equal(t, val[0], "existingvalue")
-
- val, ok = values["mp[key]"]
- Equal(t, ok, true)
- Equal(t, val[0], "value")
-
- val, ok = values["Array"]
- Equal(t, ok, true)
- Equal(t, len(val), 2)
- Equal(t, val[0], "1")
- Equal(t, val[1], "2")
-
- _, ok = values["ZeroLengthArray"]
- Equal(t, ok, false)
-
- val, ok = values["IfaceNonNil[Number]"]
- Equal(t, ok, true)
- Equal(t, val[0], "")
-
- _, ok = values["IfaceInvalid"]
- Equal(t, ok, false)
-
- val, ok = values["IfaceNonNil[Number]"]
- Equal(t, ok, true)
- Equal(t, val[0], "")
-
- val, ok = values["TimeMapKey["+tm.Format("2006-01-02")+"]"]
- Equal(t, ok, true)
- Equal(t, val[0], "time")
-
- val, ok = values["ArrayMap[0][1]"]
- Equal(t, ok, true)
- Equal(t, val[0], "3")
-
- val, ok = values["ArrayTime[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], tm.Format("2006-01-02"))
-
- s := struct {
- Value string
- Ignore string `form:"-"`
- unexposed string
- ArrayTime []time.Time
- }{
- Value: "tval",
- Ignore: "ignore",
- unexposed: "unexp",
- ArrayTime: []time.Time{tm},
- }
-
- encoder = NewEncoder()
- values, errs = encoder.Encode(&s)
- Equal(t, errs, nil)
- Equal(t, len(values), 2)
-
- val, ok = values["Value"]
- Equal(t, ok, true)
- Equal(t, val[0], "tval")
-
- _, ok = values["Ignore"]
- Equal(t, ok, false)
-
- _, ok = values["unexposed"]
- Equal(t, ok, false)
-
- val, ok = values["ArrayTime[0]"]
- Equal(t, ok, true)
- Equal(t, val[0], tm.Format(time.RFC3339))
-}
-
-func TestEncoderMap(t *testing.T) {
- inner := map[string]interface{}{
- "inner": "1",
- }
-
- outer := map[string]interface{}{
- "outer": inner,
- }
-
- enc := NewEncoder()
- values, _ := enc.Encode(outer)
-
- val, ok := values["[outer][inner]"]
- Equal(t, ok, true)
- Equal(t, val[0], "1")
-}
-
-func TestDecodeAllNonStructTypes(t *testing.T) {
-
- encoder := NewEncoder()
-
- // test integers
-
- i := int(3)
- i8 := int8(2)
- i16 := int16(1)
- i32 := int32(0)
- i64 := int64(3)
-
- values, err := encoder.Encode(i)
- Equal(t, err, nil)
- Equal(t, values[""][0], "3")
-
- values, err = encoder.Encode(i8)
- Equal(t, err, nil)
- Equal(t, values[""][0], "2")
-
- values, err = encoder.Encode(i16)
- Equal(t, err, nil)
- Equal(t, values[""][0], "1")
-
- values, err = encoder.Encode(i32)
- Equal(t, err, nil)
- Equal(t, values[""][0], "0")
-
- values, err = encoder.Encode(i64)
- Equal(t, err, nil)
- Equal(t, values[""][0], "3")
-
- // test unsigned integers
-
- ui := uint(3)
- ui8 := uint8(2)
- ui16 := uint16(1)
- ui32 := uint32(0)
- ui64 := uint64(3)
-
- values, err = encoder.Encode(ui)
- Equal(t, err, nil)
- Equal(t, values[""][0], "3")
-
- values, err = encoder.Encode(ui8)
- Equal(t, err, nil)
- Equal(t, values[""][0], "2")
-
- values, err = encoder.Encode(ui16)
- Equal(t, err, nil)
- Equal(t, values[""][0], "1")
-
- values, err = encoder.Encode(ui32)
- Equal(t, err, nil)
- Equal(t, values[""][0], "0")
-
- values, err = encoder.Encode(ui64)
- Equal(t, err, nil)
- Equal(t, values[""][0], "3")
-
- // test bool
-
- ok := true
- values, err = encoder.Encode(ok)
- Equal(t, err, nil)
- Equal(t, values[""][0], "true")
-
- ok = false
- values, err = encoder.Encode(ok)
- Equal(t, err, nil)
- Equal(t, values[""][0], "false")
-
- // test time
-
- tm, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
- Equal(t, err, nil)
-
- values, err = encoder.Encode(tm)
- Equal(t, err, nil)
- Equal(t, values[""][0], "2006-01-02T15:04:05Z")
-
- // test basic array
-
- arr := []string{"arr1", "arr2"}
-
- values, err = encoder.Encode(arr)
- Equal(t, err, nil)
- Equal(t, len(values), 1)
- Equal(t, values[""][0], "arr1")
- Equal(t, values[""][1], "arr2")
-
- // test ptr array
-
- s1 := "arr1"
- s2 := "arr2"
- arrPtr := []*string{&s1, &s2}
-
- values, err = encoder.Encode(arrPtr)
- Equal(t, err, nil)
- Equal(t, len(values), 2)
- Equal(t, values["[0]"][0], "arr1")
- Equal(t, values["[1]"][0], "arr2")
-
- // test map
-
- m := map[string]string{"key1": "val1", "key2": "val2"}
-
- values, err = encoder.Encode(m)
- Equal(t, err, nil)
- Equal(t, len(values), 2)
- Equal(t, values["[key1]"][0], "val1")
- Equal(t, values["[key2]"][0], "val2")
-}
-
-func TestEncoderNativeTime(t *testing.T) {
-
- type TestError struct {
- Time time.Time
- TimeNoValue time.Time
- }
-
- tm, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
- Equal(t, err, nil)
-
- test := TestError{
- Time: tm,
- }
-
- encoder := NewEncoder()
- values, errs := encoder.Encode(&test)
- Equal(t, errs, nil)
-
- val, ok := values["Time"]
- Equal(t, ok, true)
- Equal(t, val[0], tm.Format(time.RFC3339))
-
- val, ok = values["TimeNoValue"]
- Equal(t, ok, true)
- Equal(t, val[0], "0001-01-01T00:00:00Z")
-}
-
-func TestEncoderErrors(t *testing.T) {
-
- tm, err := time.Parse(time.RFC3339, "2006-01-02T15:04:05Z")
- Equal(t, err, nil)
-
- type TestError struct {
- Time time.Time
- BadMapKey map[time.Time]string
- Iface map[interface{}]string
- Struct map[struct{}]string
- }
-
- test := TestError{
- BadMapKey: map[time.Time]string{tm: "time"},
- Iface: map[interface{}]string{nil: "time"},
- Struct: map[struct{}]string{{}: "str"},
- }
-
- encoder := NewEncoder()
- encoder.RegisterCustomTypeFunc(func(x interface{}) ([]string, error) {
- return nil, errors.New("Bad Type Conversion")
- }, time.Time{})
-
- values, errs := encoder.Encode(&test)
- NotEqual(t, errs, nil)
-
- Equal(t, len(values), 0)
-
- e := errs.Error()
- NotEqual(t, e, "")
-
- ee := errs.(EncodeErrors)
- Equal(t, len(ee), 3)
-
- k := ee["Time"]
- Equal(t, k.Error(), "Bad Type Conversion")
-
- k = ee["BadMapKey"]
- Equal(t, k.Error(), "Bad Type Conversion")
-
- k = ee["Struct"]
- Equal(t, k.Error(), "Unsupported Map Key '' Namespace 'Struct'")
-}
-
-func TestEncoderPanicsAndBadValues(t *testing.T) {
-
- encoder := NewEncoder()
-
- values, err := encoder.Encode(nil)
- NotEqual(t, err, nil)
- Equal(t, values, nil)
-
- _, ok := err.(*InvalidEncodeError)
- Equal(t, ok, true)
- Equal(t, err.Error(), "form: Encode(nil)")
-
- type TestStruct struct {
- Value string
- }
-
- var tst *TestStruct
-
- values, err = encoder.Encode(tst)
- NotEqual(t, err, nil)
- Equal(t, values, nil)
-
- _, ok = err.(*InvalidEncodeError)
- Equal(t, ok, true)
- Equal(t, err.Error(), "form: Encode(nil *form.TestStruct)")
-}
-
-func TestEncoderExplicit(t *testing.T) {
-
- type Test struct {
- Name string `form:"Name"`
- Age int
- }
-
- test := &Test{
- Name: "Joeybloggs",
- Age: 3,
- }
-
- encoder := NewEncoder()
- encoder.SetMode(ModeExplicit)
-
- values, err := encoder.Encode(test)
- Equal(t, err, nil)
- Equal(t, len(values), 1)
- Equal(t, values["Name"][0], "Joeybloggs")
-}
-
-func TestEncoderRegisterTagNameFunc(t *testing.T) {
-
- type Test struct {
- Name string `json:"name"`
- Age int `json:"-"`
- }
-
- test := &Test{
- Name: "Joeybloggs",
- Age: 3,
- }
-
- encoder := NewEncoder()
- encoder.RegisterTagNameFunc(func(fld reflect.StructField) string {
- name := fld.Tag.Get("json")
-
- if commaIndex := strings.Index(name, ","); commaIndex != -1 {
- name = name[:commaIndex]
- }
-
- return name
- })
-
- values, err := encoder.Encode(test)
- Equal(t, err, nil)
- Equal(t, len(values), 1)
- Equal(t, values["name"][0], "Joeybloggs")
-}
-
-func TestEncoderEmbedModes(t *testing.T) {
-
- type A struct {
- Field string
- }
-
- type B struct {
- A
- Field string
- }
-
- b := B{
- A: A{
- Field: "A Val",
- },
- Field: "B Val",
- }
-
- encoder := NewEncoder()
-
- values, err := encoder.Encode(b)
- Equal(t, err, nil)
- Equal(t, len(values), 1)
- Equal(t, values["Field"][0], "B Val")
- Equal(t, values["Field"][1], "A Val")
-
- encoder.SetAnonymousMode(AnonymousSeparate)
- values, err = encoder.Encode(b)
- Equal(t, err, nil)
- Equal(t, len(values), 2)
- Equal(t, values["Field"][0], "B Val")
- Equal(t, values["A.Field"][0], "A Val")
-}
-
-func TestOmitEmpty(t *testing.T) {
-
- type NotComparable struct {
- Slice []string
- }
-
- type Test struct {
- String string `form:",omitempty"`
- Array []string `form:",omitempty"`
- Map map[string]string `form:",omitempty"`
- String2 string `form:"str,omitempty"`
- Array2 []string `form:"arr,omitempty"`
- Map2 map[string]string `form:"map,omitempty"`
- NotComparable `form:",omitempty"`
- }
-
- var tst Test
-
- encoder := NewEncoder()
-
- values, err := encoder.Encode(tst)
- Equal(t, err, nil)
- Equal(t, len(values), 0)
-
- type Test2 struct {
- String string
- Array []string
- Map map[string]string
- String2 string `form:"str,omitempty"`
- Array2 []string `form:"arr,omitempty"`
- Map2 map[string]string `form:"map,omitempty"`
- }
-
- var tst2 Test2
-
- values, err = encoder.Encode(tst2)
- Equal(t, err, nil)
- Equal(t, len(values), 1)
- Equal(t, values["String"][0], "")
-
- type Test3 struct {
- String string
- Array []string
- Map map[string]string
- String2 string `form:"str"`
- Array2 []string
- Map2 map[string]string
- }
-
- var tst3 Test3
-
- values, err = encoder.Encode(tst3)
- Equal(t, err, nil)
- Equal(t, len(values), 2)
- Equal(t, values["String"][0], "")
- Equal(t, values["str"][0], "")
-
- type T struct {
- X *uint8 `form:"x,omitempty"`
- Array []*string `form:"arr,omitempty"`
- Array2 []*string `form:"arr2,dive,omitempty"`
- }
- x := uint8(0)
- s := ""
- tst4 := T{
- X: &x,
- Array: []*string{&s},
- }
-
- values, err = encoder.Encode(tst4)
- Equal(t, err, nil)
- Equal(t, len(values), 2)
- Equal(t, values["x"][0], "0")
- Equal(t, values["arr[0]"][0], "")
-}
diff --git a/toolkit/form/form.go b/toolkit/form/form.go
deleted file mode 100644
index 399813d2..00000000
--- a/toolkit/form/form.go
+++ /dev/null
@@ -1,49 +0,0 @@
-package form
-
-import (
- "github.com/goccy/go-reflect"
- "time"
-)
-
-const (
- blank = ""
- ignore = "-"
- fieldNS = "Field Namespace:"
- errorText = " ERROR:"
-)
-
-var (
- timeType = reflect.TypeOf(time.Time{})
-)
-
-// Mode specifies which mode the form decoder is to run
-type Mode uint8
-
-const (
-
- // ModeImplicit tries to parse values for all
- // fields that do not have an ignore '-' tag
- ModeImplicit Mode = iota
-
- // ModeExplicit only parses values for field with a field tag
- // and that tag is not the ignore '-' tag
- ModeExplicit
-)
-
-// AnonymousMode specifies how data should be rolled up
-// or separated from anonymous structs
-type AnonymousMode uint8
-
-const (
- // AnonymousEmbed embeds anonymous data when encoding
- // eg. type A struct { Field string }
- // type B struct { A, Field string }
- // encode results: url.Values{"Field":[]string{"B FieldVal", "A FieldVal"}}
- AnonymousEmbed AnonymousMode = iota
-
- // AnonymousSeparate does not embed anonymous data when encoding
- // eg. type A struct { Field string }
- // type B struct { A, Field string }
- // encode results: url.Values{"Field":[]string{"B FieldVal"}, "A.Field":[]string{"A FieldVal"}}
- AnonymousSeparate
-)
diff --git a/toolkit/form/form_decoder.go b/toolkit/form/form_decoder.go
deleted file mode 100644
index 1273db4e..00000000
--- a/toolkit/form/form_decoder.go
+++ /dev/null
@@ -1,207 +0,0 @@
-package form
-
-import (
- "bytes"
- "github.com/goccy/go-reflect"
- "github.com/samber/lo"
- "net/url"
- "strings"
- "sync"
-)
-
-// DecodeCustomTypeFunc allows for registering/overriding types to be parsed.
-type DecodeCustomTypeFunc func([]string) (interface{}, error)
-
-// DecodeErrors is a map of errors encountered during form decoding
-type DecodeErrors map[string]error
-
-func (d DecodeErrors) Error() string {
- buff := bytes.NewBufferString(blank)
-
- for k, err := range d {
- buff.WriteString(fieldNS)
- buff.WriteString(k)
- buff.WriteString(errorText)
- buff.WriteString(err.Error())
- buff.WriteString("\n")
- }
-
- return strings.TrimSpace(buff.String())
-}
-
-// An InvalidDecoderError describes an invalid argument passed to Decode.
-// (The argument passed to Decode must be a non-nil pointer.)
-type InvalidDecoderError struct {
- Type reflect.Type
-}
-
-func (e *InvalidDecoderError) Error() string {
-
- if e.Type == nil {
- return "form: Decode(nil)"
- }
-
- if e.Type.Kind() != reflect.Ptr {
- return "form: Decode(non-pointer " + e.Type.String() + ")"
- }
-
- return "form: Decode(nil " + e.Type.String() + ")"
-}
-
-type key struct {
- ivalue int
- value string
- searchValue string
-}
-
-const defaultSliceLen = -1
-
-type recursiveData struct {
- alias string
- sliceLen int
- keys []key
-}
-
-type dataMap []*recursiveData
-
-// Decoder is the main decode instance
-type Decoder struct {
- tagName string
- mode Mode
- structCache *structCacheMap
- customTypeFuncs map[reflect.Type]DecodeCustomTypeFunc
- maxArraySize int
- dataPool *sync.Pool
- namespacePrefix string
- namespaceSuffix string
-}
-
-// NewDecoder creates a new decoder instance with sane defaults
-func NewDecoder() *Decoder {
-
- d := &Decoder{
- tagName: "form",
- mode: ModeImplicit,
- structCache: newStructCacheMap(),
- maxArraySize: 10000,
- namespacePrefix: ".",
- }
-
- d.dataPool = &sync.Pool{New: func() interface{} {
- return &decoder{
- d: d,
- namespace: make([]byte, 0, 64),
- }
- }}
-
- return d
-}
-
-// SetTagName sets the given tag name to be used by the decoder.
-// Default is "form"
-func (d *Decoder) SetTagName(tagName string) {
- d.tagName = tagName
-}
-
-// SetMode sets the mode the decoder should run
-// Default is ModeImplicit
-func (d *Decoder) SetMode(mode Mode) {
- d.mode = mode
-}
-
-// SetNamespacePrefix sets a struct namespace prefix.
-func (d *Decoder) SetNamespacePrefix(namespacePrefix string) {
- d.namespacePrefix = namespacePrefix
-}
-
-// SetNamespaceSuffix sets a struct namespace suffix.
-func (d *Decoder) SetNamespaceSuffix(namespaceSuffix string) {
- d.namespaceSuffix = namespaceSuffix
-}
-
-// SetMaxArraySize sets maximum array size that can be created.
-// This limit is for the array indexing this library supports to
-// avoid potential DOS or man-in-the-middle attacks using an unusually
-// high number.
-// DEFAULT: 10000
-func (d *Decoder) SetMaxArraySize(size uint) {
- d.maxArraySize = int(size)
-}
-
-// RegisterTagNameFunc registers a custom tag name parser function
-// NOTE: This method is not thread-safe it is intended that these all be registered prior to any parsing
-//
-// ADDITIONAL: once a custom function has been registered the default, or custom set, tag name is ignored
-// and relies 100% on the function for the name data. The return value WILL BE CACHED and so return value
-// must be consistent.
-func (d *Decoder) RegisterTagNameFunc(fn TagNameFunc) {
- d.structCache.tagFn = fn
-}
-
-// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types.
-// NOTE: This method is not thread-safe it is intended that these all be registered prior to any parsing
-//
-// ADDITIONAL: if a struct type is registered, the function will only be called if a url.Value exists for
-// the struct and not just the struct fields eg. url.Values{"User":"Name%3Djoeybloggs"} will call the
-// custom type function with `User` as the type, however url.Values{"User.Name":"joeybloggs"} will not.
-func (d *Decoder) RegisterCustomTypeFunc(fn DecodeCustomTypeFunc, types ...interface{}) {
-
- if d.customTypeFuncs == nil {
- d.customTypeFuncs = map[reflect.Type]DecodeCustomTypeFunc{}
- }
-
- for _, t := range types {
- d.customTypeFuncs[reflect.TypeOf(t)] = fn
- }
-}
-
-// Decode parses the given values and sets the corresponding struct and/or type values
-//
-// Decode returns an InvalidDecoderError if interface passed is invalid.
-func (d *Decoder) Decode(v interface{}, values url.Values) (err error) {
-
- val := reflect.ValueOf(v)
-
- if val.Kind() != reflect.Ptr || val.IsNil() {
- return &InvalidDecoderError{reflect.TypeOf(v)}
- }
-
- dec := d.dataPool.Get().(*decoder)
- valuesCopy := make(url.Values, len(values))
- for item, v := range values {
- k := "["
- _, i, ok := lo.FindIndexOf[byte]([]byte(item), func(item byte) bool {
- if item == '[' || item == d.namespacePrefix[0] {
- return true
- }
- return false
- })
- if ok {
- k = k + item[:i] + "]" + item[i:]
- } else {
- k = k + item + "]"
- }
- valuesCopy[k] = v
- }
- dec.values = valuesCopy
- dec.dm = dec.dm[0:0]
- dec.decoded = dec.decoded[0:0]
-
- val = val.Elem()
- typ := val.Type()
-
- if val.Kind() == reflect.Struct && typ != timeType {
- dec.traverseStruct(val, typ, dec.namespace[0:0])
- } else {
- dec.setFieldByType(val, dec.namespace[0:0], 0)
- }
-
- if len(dec.errs) > 0 {
- err = dec.errs
- dec.errs = nil
- }
-
- d.dataPool.Put(dec)
-
- return
-}
diff --git a/toolkit/form/form_encoder.go b/toolkit/form/form_encoder.go
deleted file mode 100644
index 4cd83a29..00000000
--- a/toolkit/form/form_encoder.go
+++ /dev/null
@@ -1,157 +0,0 @@
-package form
-
-import (
- "bytes"
- "net/url"
- "github.com/goccy/go-reflect"
- "strings"
- "sync"
-)
-
-// EncodeCustomTypeFunc allows for registering/overriding types to be parsed.
-type EncodeCustomTypeFunc func(x interface{}) ([]string, error)
-
-// EncodeErrors is a map of errors encountered during form encoding
-type EncodeErrors map[string]error
-
-func (e EncodeErrors) Error() string {
- buff := bytes.NewBufferString(blank)
-
- for k, err := range e {
- buff.WriteString(fieldNS)
- buff.WriteString(k)
- buff.WriteString(errorText)
- buff.WriteString(err.Error())
- buff.WriteString("\n")
- }
-
- return strings.TrimSpace(buff.String())
-}
-
-// An InvalidEncodeError describes an invalid argument passed to Encode.
-type InvalidEncodeError struct {
- Type reflect.Type
-}
-
-func (e *InvalidEncodeError) Error() string {
-
- if e.Type == nil {
- return "form: Encode(nil)"
- }
-
- return "form: Encode(nil " + e.Type.String() + ")"
-}
-
-// Encoder is the main encode instance
-type Encoder struct {
- tagName string
- structCache *structCacheMap
- customTypeFuncs map[reflect.Type]EncodeCustomTypeFunc
- dataPool *sync.Pool
- mode Mode
- embedAnonymous bool
- namespacePrefix string
- namespaceSuffix string
-}
-
-// NewEncoder creates a new encoder instance with sane defaults
-func NewEncoder() *Encoder {
-
- e := &Encoder{
- tagName: "form",
- mode: ModeImplicit,
- structCache: newStructCacheMap(),
- embedAnonymous: true,
- namespacePrefix: ".",
- }
-
- e.dataPool = &sync.Pool{New: func() interface{} {
- return &encoder{
- e: e,
- namespace: make([]byte, 0, 64),
- }
- }}
-
- return e
-}
-
-// SetTagName sets the given tag name to be used by the encoder.
-// Default is "form"
-func (e *Encoder) SetTagName(tagName string) {
- e.tagName = tagName
-}
-
-// SetMode sets the mode the encoder should run
-// Default is ModeImplicit
-func (e *Encoder) SetMode(mode Mode) {
- e.mode = mode
-}
-
-// SetNamespacePrefix sets a struct namespace prefix.
-func (e *Encoder) SetNamespacePrefix(namespacePrefix string) {
- e.namespacePrefix = namespacePrefix
-}
-
-// SetNamespaceSuffix sets a struct namespace suffix.
-func (e *Encoder) SetNamespaceSuffix(namespaceSuffix string) {
- e.namespaceSuffix = namespaceSuffix
-}
-
-// SetAnonymousMode sets the mode the encoder should run
-// Default is AnonymousEmbed
-func (e *Encoder) SetAnonymousMode(mode AnonymousMode) {
- e.embedAnonymous = mode == AnonymousEmbed
-}
-
-// RegisterTagNameFunc registers a custom tag name parser function
-// NOTE: This method is not thread-safe it is intended that these all be registered prior to any parsing
-//
-// ADDITIONAL: once a custom function has been registered the default, or custom set, tag name is ignored
-// and relies 100% on the function for the name data. The return value WILL BE CACHED and so return value
-// must be consistent.
-func (e *Encoder) RegisterTagNameFunc(fn TagNameFunc) {
- e.structCache.tagFn = fn
-}
-
-// RegisterCustomTypeFunc registers a CustomTypeFunc against a number of types
-// NOTE: this method is not thread-safe it is intended that these all be registered prior to any parsing
-func (e *Encoder) RegisterCustomTypeFunc(fn EncodeCustomTypeFunc, types ...interface{}) {
-
- if e.customTypeFuncs == nil {
- e.customTypeFuncs = map[reflect.Type]EncodeCustomTypeFunc{}
- }
-
- for _, t := range types {
- e.customTypeFuncs[reflect.TypeOf(t)] = fn
- }
-}
-
-// Encode encodes the given values and sets the corresponding struct values
-func (e *Encoder) Encode(v interface{}) (values url.Values, err error) {
-
- val, kind := ExtractType(reflect.ValueOf(v))
-
- if kind == reflect.Ptr || kind == reflect.Interface || kind == reflect.Invalid {
- return nil, &InvalidEncodeError{reflect.TypeOf(v)}
- }
-
- enc := e.dataPool.Get().(*encoder)
- enc.values = make(url.Values)
-
- if kind == reflect.Struct && val.Type() != timeType {
- enc.traverseStruct(val, enc.namespace[0:0], -1)
- } else {
- enc.setFieldByType(val, enc.namespace[0:0], -1, false)
- }
-
- if len(enc.errs) > 0 {
- err = enc.errs
- enc.errs = nil
- }
-
- values = enc.values
-
- e.dataPool.Put(enc)
-
- return
-}
diff --git a/toolkit/form/logo.jpg b/toolkit/form/logo.jpg
deleted file mode 100644
index 2ef34f87..00000000
Binary files a/toolkit/form/logo.jpg and /dev/null differ
diff --git a/toolkit/form/util.go b/toolkit/form/util.go
deleted file mode 100644
index 7f9a4d31..00000000
--- a/toolkit/form/util.go
+++ /dev/null
@@ -1,62 +0,0 @@
-package form
-
-import (
- "github.com/goccy/go-reflect"
- "strconv"
-)
-
-// ExtractType gets the actual underlying type of field value.
-// it is exposed for use within you Custom Functions
-func ExtractType(current reflect.Value) (reflect.Value, reflect.Kind) {
-
- switch current.Kind() {
- case reflect.Ptr:
-
- if current.IsNil() {
- return current, reflect.Ptr
- }
-
- return ExtractType(current.Elem())
-
- case reflect.Interface:
-
- if current.IsNil() {
- return current, reflect.Interface
- }
-
- return ExtractType(current.Elem())
-
- default:
- return current, current.Kind()
- }
-}
-
-func parseBool(str string) (bool, error) {
-
- switch str {
- case "1", "t", "T", "true", "TRUE", "True", "on", "yes", "ok":
- return true, nil
- case "", "0", "f", "F", "false", "FALSE", "False", "off", "no":
- return false, nil
- }
-
- // strconv.NumError mimicing exactly the strconv.ParseBool(..) error and type
- // to ensure compatibility with std library and beyond.
- return false, &strconv.NumError{Func: "ParseBool", Num: str, Err: strconv.ErrSyntax}
-}
-
-// hasValue determines if a reflect.Value is it's default value
-func hasValue(field reflect.Value) bool {
- switch field.Kind() {
- case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
- return !field.IsNil()
- default:
- if !field.IsValid() {
- return false
- }
- if !field.Type().Comparable() {
- return true
- }
- return field.Interface() != reflect.Zero(field.Type()).Interface()
- }
-}
diff --git a/toolkit/gocache/.gitignore b/toolkit/gocache/.gitignore
deleted file mode 100644
index cd2d0c6b..00000000
--- a/toolkit/gocache/.gitignore
+++ /dev/null
@@ -1,3 +0,0 @@
-.idea/
-.vscode/
-pegasus.log
\ No newline at end of file
diff --git a/toolkit/gocache/LICENSE b/toolkit/gocache/LICENSE
deleted file mode 100644
index 902e8426..00000000
--- a/toolkit/gocache/LICENSE
+++ /dev/null
@@ -1,19 +0,0 @@
-Copyright (c) 2022 Vincent Composieux
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/toolkit/gocache/Makefile b/toolkit/gocache/Makefile
deleted file mode 100644
index a330f326..00000000
--- a/toolkit/gocache/Makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-.PHONY: update-stores-version mocks test benchmark-store
-
-# Usage: VERSION=v4.1.3 make update-stores-version
-update-stores-version:
- ls store/ | xargs -I % bash -c "sed -i '' -E 's,github.com/eko/gocache/lib/v4 v[0-9]\.[0-9]\.[0-9],github.com/eko/gocache/lib/v4 ${VERSION},g' store/%/go.mod"
-
-mocks:
- mockgen -source=lib/cache/interface.go -destination=lib/cache/cache_mock.go -package=cache
- mockgen -source=lib/codec/interface.go -destination=lib/codec/codec_mock.go -package=codec
- mockgen -source=lib/metrics/interface.go -destination=lib/metrics/metrics_mock.go -package=metrics
- mockgen -source=lib/store/interface.go -destination=lib/store/store_mock.go -package=store
- mockgen -source=store/bigcache/bigcache.go -destination=store/bigcache/bigcache_mock.go -package=bigcache
- mockgen -source=store/memcache/memcache.go -destination=store/memcache/memcache_mock.go -package=memcache
- mockgen -source=store/redis/redis.go -destination=store/redis/redis_mock.go -package=redis
- mockgen -source=store/rediscluster/rediscluster.go -destination=store/rediscluster/rediscluster_mock.go -package=rediscluster
- mockgen -source=store/ristretto/ristretto.go -destination=store/ristretto/ristretto_mock.go -package=ristretto
- mockgen -source=store/freecache/freecache.go -destination=store/freecache/freecache_mock.go -package=freecache
- mockgen -source=store/go_cache/go_cache.go -destination=store/go_cache/go_cache_mock.go -package=go_cache
- mockgen -source=store/hazelcast/hazelcast.go -destination=store/hazelcast/hazelcast_mock.go -package=hazelcast
-
-test:
- cd lib; GOGC=10 go test -v -p=4 ./...
- cd store/bigcache; GOGC=10 go test -v -p=4 ./...
- cd store/freecache; GOGC=10 go test -v -p=4 ./...
- cd store/go_cache; GOGC=10 go test -v -p=4 ./...
- cd store/memcache; GOGC=10 go test -v -p=4 ./...
- cd store/pegasus; GOGC=10 go test -v -p=4 ./...
- cd store/redis; GOGC=10 go test -v -p=4 ./...
- cd store/rediscluster; GOGC=10 go test -v -p=4 ./...
- cd store/ristretto; GOGC=10 go test -v -p=4 ./...
diff --git a/toolkit/gocache/README.md b/toolkit/gocache/README.md
deleted file mode 100644
index 23103704..00000000
--- a/toolkit/gocache/README.md
+++ /dev/null
@@ -1,554 +0,0 @@
-[![Test](https://github.com/eko/gocache/actions/workflows/all.yml/badge.svg?branch=master)](https://github.com/eko/gocache/actions/workflows/all.yml)
-[![GoDoc](https://godoc.org/github.com/eko/gocache?status.png)](https://godoc.org/github.com/eko/gocache)
-[![GoReportCard](https://goreportcard.com/badge/github.com/eko/gocache)](https://goreportcard.com/report/github.com/eko/gocache)
-[![codecov](https://codecov.io/gh/eko/gocache/branch/master/graph/badge.svg)](https://codecov.io/gh/eko/gocache)
-
-Gocache
-=======
-
-Guess what is Gocache? a Go cache library.
-This is an extendable cache library that brings you a lot of features for caching data.
-
-## Overview
-
-Here is what it brings in detail:
-
-* ✅ Multiple cache stores: actually in memory, redis, or your own custom store
-* ✅ A chain cache: use multiple cache with a priority order (memory then fallback to a redis shared cache for instance)
-* ✅ A loadable cache: allow you to call a callback function to put your data back in cache
-* ✅ A metric cache to let you store metrics about your caches usage (hits, miss, set success, set error, ...)
-* ✅ A marshaler to automatically marshal/unmarshal your cache values as a struct
-* ✅ Define default values in stores and override them when setting data
-* ✅ Cache invalidation by expiration time and/or using tags
-* ✅ Use of Generics
-
-## Built-in stores
-
-* [Memory (bigcache)](https://github.com/allegro/bigcache) (allegro/bigcache)
-* [Memory (ristretto)](https://github.com/dgraph-io/ristretto) (dgraph-io/ristretto)
-* [Memory (go-cache)](https://github.com/patrickmn/go-cache) (patrickmn/go-cache)
-* [Memcache](https://github.com/bradfitz/gomemcache) (bradfitz/memcache)
-* [Redis](https://github.com/go-redis/redis) (go-redis/redis)
-* [Redis (rueidis)](https://github.com/redis/rueidis) (redis/rueidis)
-* [Freecache](https://github.com/coocood/freecache) (coocood/freecache)
-* [Pegasus](https://pegasus.apache.org/) ([apache/incubator-pegasus](https://github.com/apache/incubator-pegasus)) [benchmark](https://pegasus.apache.org/overview/benchmark/)
-* [Hazelcast](https://github.com/hazelcast/hazelcast-go-client) (hazelcast-go-client/hazelcast)
-* More to come soon
-
-## Built-in metrics providers
-
-* [Prometheus](https://github.com/prometheus/client_golang)
-
-## Installation
-
-To begin working with the latest version of gocache, you can import the library in your project:
-
-```go
-go get github.com/eko/gocache/lib/v4
-```
-
-and then, import the store(s) you want to use between all available ones:
-
-```go
-go get github.com/eko/gocache/store/bigcache/v4
-go get github.com/eko/gocache/store/freecache/v4
-go get github.com/eko/gocache/store/go_cache/v4
-go get github.com/eko/gocache/store/hazelcast/v4
-go get github.com/eko/gocache/store/memcache/v4
-go get github.com/eko/gocache/store/pegasus/v4
-go get github.com/eko/gocache/store/redis/v4
-go get github.com/eko/gocache/store/rediscluster/v4
-go get github.com/eko/gocache/store/rueidis/v4
-go get github.com/eko/gocache/store/ristretto/v4
-```
-
-Then, simply use the following import statements:
-
-```go
-import (
- "github.com/eko/gocache/lib/v4/cache"
- "github.com/eko/gocache/store/redis/v4"
-)
-```
-
-If you run into any errors, please be sure to run `go mod tidy` to clean your go.mod file.
-
-## Available cache features in detail
-
-### A simple cache
-
-Here is a simple cache instantiation with Redis but you can also look at other available stores:
-
-#### Memcache
-
-```go
-memcacheStore := memcache_store.NewMemcache(
- memcache.New("10.0.0.1:11211", "10.0.0.2:11211", "10.0.0.3:11212"),
- store.WithExpiration(10*time.Second),
-)
-
-cacheManager := cache.New[[]byte](memcacheStore)
-err := cacheManager.Set(ctx, "my-key", []byte("my-value"),
- store.WithExpiration(15*time.Second), // Override default value of 10 seconds defined in the store
-)
-if err != nil {
- panic(err)
-}
-
-value := cacheManager.Get(ctx, "my-key")
-
-cacheManager.Delete(ctx, "my-key")
-
-cacheManager.Clear(ctx) // Clears the entire cache, in case you want to flush all cache
-```
-
-#### Memory (using Bigcache)
-
-```go
-bigcacheClient, _ := bigcache.NewBigCache(bigcache.DefaultConfig(5 * time.Minute))
-bigcacheStore := bigcache_store.NewBigcache(bigcacheClient)
-
-cacheManager := cache.New[[]byte](bigcacheStore)
-err := cacheManager.Set(ctx, "my-key", []byte("my-value"))
-if err != nil {
- panic(err)
-}
-
-value := cacheManager.Get(ctx, "my-key")
-```
-
-#### Memory (using Ristretto)
-
-```go
-import (
- "github.com/dgraph-io/ristretto"
- "github.com/eko/gocache/lib/v4/cache"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- ristretto_store "github.com/eko/gocache/store/ristretto/v4"
-)
-ristrettoCache, err := ristretto.NewCache(&ristretto.Config{
- NumCounters: 1000,
- MaxCost: 100,
- BufferItems: 64,
-})
-if err != nil {
- panic(err)
-}
-ristrettoStore := ristretto_store.NewRistretto(ristrettoCache)
-
-cacheManager := cache.New[string](ristrettoStore)
-err := cacheManager.Set(ctx, "my-key", "my-value", store.WithCost(2))
-if err != nil {
- panic(err)
-}
-
-value := cacheManager.Get(ctx, "my-key")
-
-cacheManager.Delete(ctx, "my-key")
-```
-
-#### Memory (using Go-cache)
-
-```go
-gocacheClient := gocache.New(5*time.Minute, 10*time.Minute)
-gocacheStore := gocache_store.NewGoCache(gocacheClient)
-
-cacheManager := cache.New[[]byte](gocacheStore)
-err := cacheManager.Set(ctx, "my-key", []byte("my-value"))
-if err != nil {
- panic(err)
-}
-
-value, err := cacheManager.Get(ctx, "my-key")
-if err != nil {
- panic(err)
-}
-fmt.Printf("%s", value)
-```
-
-#### Redis
-
-```go
-redisStore := redis_store.NewRedis(redis.NewClient(&redis.Options{
- Addr: "127.0.0.1:6379",
-}))
-
-cacheManager := cache.New[string](redisStore)
-err := cacheManager.Set(ctx, "my-key", "my-value", store.WithExpiration(15*time.Second))
-if err != nil {
- panic(err)
-}
-
-value, err := cacheManager.Get(ctx, "my-key")
-switch err {
- case nil:
- fmt.Printf("Get the key '%s' from the redis cache. Result: %s", "my-key", value)
- case redis.Nil:
- fmt.Printf("Failed to find the key '%s' from the redis cache.", "my-key")
- default:
- fmt.Printf("Failed to get the value from the redis cache with key '%s': %v", "my-key", err)
-}
-```
-
-#### [Redis Client-Side Caching](https://redis.io/docs/manual/client-side-caching/) (using rueidis)
-
-```go
-client, err := rueidis.NewClient(rueidis.ClientOption{InitAddress: []string{"127.0.0.1:6379"}})
-if err != nil {
- panic(err)
-}
-
-cacheManager := cache.New[string](rueidis_store.NewRueidis(
- client,
- store.WithExpiration(15*time.Second),
- store.WithClientSideCaching(15*time.Second)),
-)
-
-if err = cacheManager.Set(ctx, "my-key", "my-value"); err != nil {
- panic(err)
-}
-
-value, err := cacheManager.Get(ctx, "my-key")
-if err != nil {
- log.Fatalf("Failed to get the value from the redis cache with key '%s': %v", "my-key", err)
-}
-log.Printf("Get the key '%s' from the redis cache. Result: %s", "my-key", value)
-```
-
-#### Freecache
-
-```go
-freecacheStore := freecache_store.NewFreecache(freecache.NewCache(1000), store.WithExpiration(10 * time.Second))
-
-cacheManager := cache.New[[]byte](freecacheStore)
-err := cacheManager.Set(ctx, "by-key", []byte("my-value"), opts)
-if err != nil {
- panic(err)
-}
-
-value := cacheManager.Get(ctx, "my-key")
-```
-
-#### Pegasus
-
-```go
-pegasusStore, err := pegasus_store.NewPegasus(&store.OptionsPegasus{
- MetaServers: []string{"127.0.0.1:34601", "127.0.0.1:34602", "127.0.0.1:34603"},
-})
-
-if err != nil {
- fmt.Println(err)
- return
-}
-
-cacheManager := cache.New[string](pegasusStore)
-err = cacheManager.Set(ctx, "my-key", "my-value", store.WithExpiration(10 * time.Second))
-if err != nil {
- panic(err)
-}
-
-value, _ := cacheManager.Get(ctx, "my-key")
-```
-
-#### Hazelcast
-
-```go
-hzClient, err := hazelcast.StartNewClient(ctx)
-if err != nil {
- log.Fatalf("Failed to start client: %v", err)
-}
-
-hzMapName:= "gocache"
-
-hazelcastStore := hazelcast_store.NewHazelcast(hzClient, hzMapName)
-
-cacheManager := cache.New[string](hazelcastStore)
-err := cacheManager.Set(ctx, "my-key", "my-value", store.WithExpiration(15*time.Second))
-if err != nil {
- panic(err)
-}
-
-value, err := cacheManager.Get(ctx, "my-key")
-if err != nil {
- panic(err)
-}
-fmt.Printf("Get the key '%s' from the hazelcast cache. Result: %s", "my-key", value)
-```
-
-### A chained cache
-
-Here, we will chain caches in the following order: first in memory with Ristretto store, then in Redis (as a fallback):
-
-```go
-// Initialize Ristretto cache and Redis client
-ristrettoCache, err := ristretto.NewCache(&ristretto.Config{NumCounters: 1000, MaxCost: 100, BufferItems: 64})
-if err != nil {
- panic(err)
-}
-
-redisClient := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
-
-// Initialize stores
-ristrettoStore := ristretto_store.NewRistretto(ristrettoCache)
-redisStore := redis_store.NewRedis(redisClient, store.WithExpiration(5*time.Second))
-
-// Initialize chained cache
-cacheManager := cache.NewChain[any](
- cache.New[any](ristrettoStore),
- cache.New[any](redisStore),
-)
-
-// ... Then, do what you want with your cache
-```
-
-`Chain` cache also put data back in previous caches when it's found so in this case, if ristretto doesn't have the data in its cache but redis have, data will also get setted back into ristretto (memory) cache.
-
-### A loadable cache
-
-This cache will provide a load function that acts as a callable function and will set your data back in your cache in case they are not available:
-
-```go
-type Book struct {
- ID string
- Name string
-}
-
-// Initialize Redis client and store
-redisClient := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
-redisStore := redis_store.NewRedis(redisClient)
-
-// Initialize a load function that loads your data from a custom source
-loadFunction := func(ctx context.Context, key any) (*Book, error) {
- // ... retrieve value from available source
- return &Book{ID: 1, Name: "My test amazing book"}, nil
-}
-
-// Initialize loadable cache
-cacheManager := cache.NewLoadable[*Book](
- loadFunction,
- cache.New[*Book](redisStore),
-)
-
-// ... Then, you can get your data and your function will automatically put them in cache(s)
-```
-
-Of course, you can also pass a `Chain` cache into the `Loadable` one so if your data is not available in all caches, it will bring it back in all caches.
-
-### A metric cache to retrieve cache statistics
-
-This cache will record metrics depending on the metric provider you pass to it. Here we give a Prometheus provider:
-
-```go
-// Initialize Redis client and store
-redisClient := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
-redisStore := redis_store.NewRedis(redisClient)
-
-// Initializes Prometheus metrics service
-promMetrics := metrics.NewPrometheus("my-test-app")
-
-// Initialize metric cache
-cacheManager := cache.NewMetric[any](
- promMetrics,
- cache.New[any](redisStore),
-)
-
-// ... Then, you can get your data and metrics will be observed by Prometheus
-```
-
-### A marshaler wrapper
-
-Some caches like Redis stores and returns the value as a string so you have to marshal/unmarshal your structs if you want to cache an object. That's why we bring a marshaler service that wraps your cache and make the work for you:
-
-```go
-// Initialize Redis client and store
-redisClient := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
-redisStore := redis_store.NewRedis(redisClient)
-
-// Initialize chained cache
-cacheManager := cache.NewMetric[any](
- promMetrics,
- cache.New[any](redisStore),
-)
-
-// Initializes marshaler
-marshal := marshaler.New(cacheManager)
-
-key := BookQuery{Slug: "my-test-amazing-book"}
-value := Book{ID: 1, Name: "My test amazing book", Slug: "my-test-amazing-book"}
-
-err = marshal.Set(ctx, key, value)
-if err != nil {
- panic(err)
-}
-
-returnedValue, err := marshal.Get(ctx, key, new(Book))
-if err != nil {
- panic(err)
-}
-
-// Then, do what you want with the value
-
-marshal.Delete(ctx, "my-key")
-```
-
-The only thing you have to do is to specify the struct in which you want your value to be un-marshalled as a second argument when calling the `.Get()` method.
-
-
-### Cache invalidation using tags
-
-You can attach some tags to items you create so you can easily invalidate some of them later.
-
-Tags are stored using the same storage you choose for your cache.
-
-Here is an example on how to use it:
-
-```go
-// Initialize Redis client and store
-redisClient := redis.NewClient(&redis.Options{Addr: "127.0.0.1:6379"})
-redisStore := redis_store.NewRedis(redisClient)
-
-// Initialize chained cache
-cacheManager := cache.NewMetric[*Book](
- promMetrics,
- cache.New[*Book](redisStore),
-)
-
-// Initializes marshaler
-marshal := marshaler.New(cacheManager)
-
-key := BookQuery{Slug: "my-test-amazing-book"}
-value := &Book{ID: 1, Name: "My test amazing book", Slug: "my-test-amazing-book"}
-
-// Set an item in the cache and attach it a "book" tag
-err = marshal.Set(ctx, key, value, store.WithTags([]string{"book"}))
-if err != nil {
- panic(err)
-}
-
-// Remove all items that have the "book" tag
-err := marshal.Invalidate(ctx, store.WithInvalidateTags([]string{"book"}))
-if err != nil {
- panic(err)
-}
-
-returnedValue, err := marshal.Get(ctx, key, new(Book))
-if err != nil {
- // Should be triggered because item has been deleted so it cannot be found.
- panic(err)
-}
-```
-
-Mix this with expiration times on your caches to have a fine-tuned control on how your data are cached.
-
-```go
-package main
-
-import (
- "fmt"
- "log"
- "time"
-
- "github.com/eko/gocache/lib/v4/cache"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "github.com/redis/go-redis/v9"
-)
-
-func main() {
- redisStore := redis_store.NewRedis(redis.NewClient(&redis.Options{
- Addr: "127.0.0.1:6379",
- }), nil)
-
- cacheManager := cache.New[string](redisStore)
- err := cacheManager.Set(ctx, "my-key", "my-value", store.WithExpiration(15*time.Second))
- if err != nil {
- panic(err)
- }
-
- key := "my-key"
- value, err := cacheManager.Get(ctx, key)
- if err != nil {
- log.Fatalf("unable to get cache key '%s' from the cache: %v", key, err)
- }
-
- fmt.Printf("%#+v\n", value)
-}
-
-```
-
-### Write your own custom cache
-
-Cache respect the following interface so you can write your own (proprietary?) cache logic if needed by implementing the following interface:
-
-```go
-type CacheInterface[T any] interface {
- Get(ctx context.Context, key any) (T, error)
- Set(ctx context.Context, key any, object T, options ...store.Option) error
- Delete(ctx context.Context, key any) error
- Invalidate(ctx context.Context, options ...store.InvalidateOption) error
- Clear(ctx context.Context) error
- GetType() string
-}
-```
-
-Or, in case you use a setter cache, also implement the `GetCodec()` method:
-
-```go
-type SetterCacheInterface[T any] interface {
- CacheInterface[T]
- GetWithTTL(ctx context.Context, key any) (T, time.Duration, error)
-
- GetCodec() codec.CodecInterface
-}
-```
-
-As all caches available in this library implement `CacheInterface`, you will be able to mix your own caches with your own.
-
-### Write your own custom store
-
-You also have the ability to write your own custom store by implementing the following interface:
-
-```go
-type StoreInterface interface {
- Get(ctx context.Context, key any) (any, error)
- GetWithTTL(ctx context.Context, key any) (any, time.Duration, error)
- Set(ctx context.Context, key any, value any, options ...Option) error
- Delete(ctx context.Context, key any) error
- Invalidate(ctx context.Context, options ...InvalidateOption) error
- Clear(ctx context.Context) error
- GetType() string
-}
-```
-
-Of course, I suggest you to have a look at current caches or stores to implement your own.
-
-### Custom cache key generator
-
-You can implement the following interface in order to generate a custom cache key:
-
-```go
-type CacheKeyGenerator interface {
- GetCacheKey() string
-}
-```
-
-### Benchmarks
-
-![Benchmarks](https://raw.githubusercontent.com/eko/gocache/master/lib/misc/benchmarks.jpeg)
-
-## Run tests
-
-To generate mocks using mockgen library, run:
-
-```bash
-$ make mocks
-```
-
-Test suite can be run with:
-
-```bash
-$ make test # run unit test
-```
-
-## Community
-
-Please feel free to contribute on this library and do not hesitate to open an issue if you want to discuss about a feature.
diff --git a/toolkit/gocache/lib/cache/cache.go b/toolkit/gocache/lib/cache/cache.go
deleted file mode 100644
index 5791d530..00000000
--- a/toolkit/gocache/lib/cache/cache.go
+++ /dev/null
@@ -1,117 +0,0 @@
-package cache
-
-import (
- "context"
- "crypto"
- "fmt"
- "reflect"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/codec"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-const (
- // CacheType represents the cache type as a string value
- CacheType = "cache"
-)
-
-// Cache represents the configuration needed by a cache
-type Cache[T any] struct {
- codec codec.CodecInterface
-}
-
-// New instantiates a new cache entry
-func New[T any](store store.StoreInterface) *Cache[T] {
- return &Cache[T]{
- codec: codec.New(store),
- }
-}
-
-// Get returns the object stored in cache if it exists
-func (c *Cache[T]) Get(ctx context.Context, key any) (T, error) {
- cacheKey := c.getCacheKey(key)
-
- value, err := c.codec.Get(ctx, cacheKey)
- if err != nil {
- return *new(T), err
- }
-
- if v, ok := value.(T); ok {
- return v, nil
- }
-
- return *new(T), nil
-}
-
-// GetWithTTL returns the object stored in cache and its corresponding TTL
-func (c *Cache[T]) GetWithTTL(ctx context.Context, key any) (T, time.Duration, error) {
- cacheKey := c.getCacheKey(key)
-
- value, duration, err := c.codec.GetWithTTL(ctx, cacheKey)
- if err != nil {
- return *new(T), duration, err
- }
-
- if v, ok := value.(T); ok {
- return v, duration, nil
- }
-
- return *new(T), duration, nil
-}
-
-// Set populates the cache item using the given key
-func (c *Cache[T]) Set(ctx context.Context, key any, object T, options ...store.Option) error {
- cacheKey := c.getCacheKey(key)
- return c.codec.Set(ctx, cacheKey, object, options...)
-}
-
-// Delete removes the cache item using the given key
-func (c *Cache[T]) Delete(ctx context.Context, key any) error {
- cacheKey := c.getCacheKey(key)
- return c.codec.Delete(ctx, cacheKey)
-}
-
-// Invalidate invalidates cache item from given options
-func (c *Cache[T]) Invalidate(ctx context.Context, options ...store.InvalidateOption) error {
- return c.codec.Invalidate(ctx, options...)
-}
-
-// Clear resets all cache data
-func (c *Cache[T]) Clear(ctx context.Context) error {
- return c.codec.Clear(ctx)
-}
-
-// GetCodec returns the current codec
-func (c *Cache[T]) GetCodec() codec.CodecInterface {
- return c.codec
-}
-
-// GetType returns the cache type
-func (c *Cache[T]) GetType() string {
- return CacheType
-}
-
-// getCacheKey returns the cache key for the given key object by returning
-// the key if type is string or by computing a checksum of key structure
-// if its type is other than string
-func (c *Cache[T]) getCacheKey(key any) string {
- switch v := key.(type) {
- case string:
- return v
- case CacheKeyGenerator:
- return v.GetCacheKey()
- default:
- return checksum(key)
- }
-}
-
-// checksum hashes a given object into a string
-func checksum(object any) string {
- digester := crypto.MD5.New()
- fmt.Fprint(digester, reflect.TypeOf(object))
- fmt.Fprint(digester, object)
- hash := digester.Sum(nil)
-
- return fmt.Sprintf("%x", hash)
-}
diff --git a/toolkit/gocache/lib/cache/cache_mock.go b/toolkit/gocache/lib/cache/cache_mock.go
deleted file mode 100644
index 1e7fe989..00000000
--- a/toolkit/gocache/lib/cache/cache_mock.go
+++ /dev/null
@@ -1,318 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: lib/cache/interface.go
-
-// Package cache is a generated GoMock package.
-package cache
-
-import (
- context "context"
- reflect "reflect"
- time "time"
-
- codec "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/codec"
- store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- gomock "go.uber.org/mock/gomock"
-)
-
-// MockCacheInterface is a mock of CacheInterface interface.
-type MockCacheInterface[T any] struct {
- ctrl *gomock.Controller
- recorder *MockCacheInterfaceMockRecorder[T]
-}
-
-// MockCacheInterfaceMockRecorder is the mock recorder for MockCacheInterface.
-type MockCacheInterfaceMockRecorder[T any] struct {
- mock *MockCacheInterface[T]
-}
-
-// NewMockCacheInterface creates a new mock instance.
-func NewMockCacheInterface[T any](ctrl *gomock.Controller) *MockCacheInterface[T] {
- mock := &MockCacheInterface[T]{ctrl: ctrl}
- mock.recorder = &MockCacheInterfaceMockRecorder[T]{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockCacheInterface[T]) EXPECT() *MockCacheInterfaceMockRecorder[T] {
- return m.recorder
-}
-
-// Clear mocks base method.
-func (m *MockCacheInterface[T]) Clear(ctx context.Context) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Clear", ctx)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Clear indicates an expected call of Clear.
-func (mr *MockCacheInterfaceMockRecorder[T]) Clear(ctx interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockCacheInterface[T])(nil).Clear), ctx)
-}
-
-// Delete mocks base method.
-func (m *MockCacheInterface[T]) Delete(ctx context.Context, key any) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Delete", ctx, key)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Delete indicates an expected call of Delete.
-func (mr *MockCacheInterfaceMockRecorder[T]) Delete(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockCacheInterface[T])(nil).Delete), ctx, key)
-}
-
-// Get mocks base method.
-func (m *MockCacheInterface[T]) Get(ctx context.Context, key any) (T, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", ctx, key)
- ret0, _ := ret[0].(T)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockCacheInterfaceMockRecorder[T]) Get(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCacheInterface[T])(nil).Get), ctx, key)
-}
-
-// GetType mocks base method.
-func (m *MockCacheInterface[T]) GetType() string {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetType")
- ret0, _ := ret[0].(string)
- return ret0
-}
-
-// GetType indicates an expected call of GetType.
-func (mr *MockCacheInterfaceMockRecorder[T]) GetType() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetType", reflect.TypeOf((*MockCacheInterface[T])(nil).GetType))
-}
-
-// Invalidate mocks base method.
-func (m *MockCacheInterface[T]) Invalidate(ctx context.Context, options ...store.InvalidateOption) error {
- m.ctrl.T.Helper()
- varargs := []interface{}{ctx}
- for _, a := range options {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "Invalidate", varargs...)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Invalidate indicates an expected call of Invalidate.
-func (mr *MockCacheInterfaceMockRecorder[T]) Invalidate(ctx interface{}, options ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{ctx}, options...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invalidate", reflect.TypeOf((*MockCacheInterface[T])(nil).Invalidate), varargs...)
-}
-
-// Set mocks base method.
-func (m *MockCacheInterface[T]) Set(ctx context.Context, key any, object T, options ...store.Option) error {
- m.ctrl.T.Helper()
- varargs := []interface{}{ctx, key, object}
- for _, a := range options {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "Set", varargs...)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Set indicates an expected call of Set.
-func (mr *MockCacheInterfaceMockRecorder[T]) Set(ctx, key, object interface{}, options ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{ctx, key, object}, options...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockCacheInterface[T])(nil).Set), varargs...)
-}
-
-// MockCacheKeyGenerator is a mock of CacheKeyGenerator interface.
-type MockCacheKeyGenerator struct {
- ctrl *gomock.Controller
- recorder *MockCacheKeyGeneratorMockRecorder
-}
-
-// MockCacheKeyGeneratorMockRecorder is the mock recorder for MockCacheKeyGenerator.
-type MockCacheKeyGeneratorMockRecorder struct {
- mock *MockCacheKeyGenerator
-}
-
-// NewMockCacheKeyGenerator creates a new mock instance.
-func NewMockCacheKeyGenerator(ctrl *gomock.Controller) *MockCacheKeyGenerator {
- mock := &MockCacheKeyGenerator{ctrl: ctrl}
- mock.recorder = &MockCacheKeyGeneratorMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockCacheKeyGenerator) EXPECT() *MockCacheKeyGeneratorMockRecorder {
- return m.recorder
-}
-
-// GetCacheKey mocks base method.
-func (m *MockCacheKeyGenerator) GetCacheKey() string {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetCacheKey")
- ret0, _ := ret[0].(string)
- return ret0
-}
-
-// GetCacheKey indicates an expected call of GetCacheKey.
-func (mr *MockCacheKeyGeneratorMockRecorder) GetCacheKey() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCacheKey", reflect.TypeOf((*MockCacheKeyGenerator)(nil).GetCacheKey))
-}
-
-// MockSetterCacheInterface is a mock of SetterCacheInterface interface.
-type MockSetterCacheInterface[T any] struct {
- ctrl *gomock.Controller
- recorder *MockSetterCacheInterfaceMockRecorder[T]
-}
-
-// MockSetterCacheInterfaceMockRecorder is the mock recorder for MockSetterCacheInterface.
-type MockSetterCacheInterfaceMockRecorder[T any] struct {
- mock *MockSetterCacheInterface[T]
-}
-
-// NewMockSetterCacheInterface creates a new mock instance.
-func NewMockSetterCacheInterface[T any](ctrl *gomock.Controller) *MockSetterCacheInterface[T] {
- mock := &MockSetterCacheInterface[T]{ctrl: ctrl}
- mock.recorder = &MockSetterCacheInterfaceMockRecorder[T]{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockSetterCacheInterface[T]) EXPECT() *MockSetterCacheInterfaceMockRecorder[T] {
- return m.recorder
-}
-
-// Clear mocks base method.
-func (m *MockSetterCacheInterface[T]) Clear(ctx context.Context) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Clear", ctx)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Clear indicates an expected call of Clear.
-func (mr *MockSetterCacheInterfaceMockRecorder[T]) Clear(ctx interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockSetterCacheInterface[T])(nil).Clear), ctx)
-}
-
-// Delete mocks base method.
-func (m *MockSetterCacheInterface[T]) Delete(ctx context.Context, key any) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Delete", ctx, key)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Delete indicates an expected call of Delete.
-func (mr *MockSetterCacheInterfaceMockRecorder[T]) Delete(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockSetterCacheInterface[T])(nil).Delete), ctx, key)
-}
-
-// Get mocks base method.
-func (m *MockSetterCacheInterface[T]) Get(ctx context.Context, key any) (T, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", ctx, key)
- ret0, _ := ret[0].(T)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockSetterCacheInterfaceMockRecorder[T]) Get(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockSetterCacheInterface[T])(nil).Get), ctx, key)
-}
-
-// GetCodec mocks base method.
-func (m *MockSetterCacheInterface[T]) GetCodec() codec.CodecInterface {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetCodec")
- ret0, _ := ret[0].(codec.CodecInterface)
- return ret0
-}
-
-// GetCodec indicates an expected call of GetCodec.
-func (mr *MockSetterCacheInterfaceMockRecorder[T]) GetCodec() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetCodec", reflect.TypeOf((*MockSetterCacheInterface[T])(nil).GetCodec))
-}
-
-// GetType mocks base method.
-func (m *MockSetterCacheInterface[T]) GetType() string {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetType")
- ret0, _ := ret[0].(string)
- return ret0
-}
-
-// GetType indicates an expected call of GetType.
-func (mr *MockSetterCacheInterfaceMockRecorder[T]) GetType() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetType", reflect.TypeOf((*MockSetterCacheInterface[T])(nil).GetType))
-}
-
-// GetWithTTL mocks base method.
-func (m *MockSetterCacheInterface[T]) GetWithTTL(ctx context.Context, key any) (T, time.Duration, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetWithTTL", ctx, key)
- ret0, _ := ret[0].(T)
- ret1, _ := ret[1].(time.Duration)
- ret2, _ := ret[2].(error)
- return ret0, ret1, ret2
-}
-
-// GetWithTTL indicates an expected call of GetWithTTL.
-func (mr *MockSetterCacheInterfaceMockRecorder[T]) GetWithTTL(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWithTTL", reflect.TypeOf((*MockSetterCacheInterface[T])(nil).GetWithTTL), ctx, key)
-}
-
-// Invalidate mocks base method.
-func (m *MockSetterCacheInterface[T]) Invalidate(ctx context.Context, options ...store.InvalidateOption) error {
- m.ctrl.T.Helper()
- varargs := []interface{}{ctx}
- for _, a := range options {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "Invalidate", varargs...)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Invalidate indicates an expected call of Invalidate.
-func (mr *MockSetterCacheInterfaceMockRecorder[T]) Invalidate(ctx interface{}, options ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{ctx}, options...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invalidate", reflect.TypeOf((*MockSetterCacheInterface[T])(nil).Invalidate), varargs...)
-}
-
-// Set mocks base method.
-func (m *MockSetterCacheInterface[T]) Set(ctx context.Context, key any, object T, options ...store.Option) error {
- m.ctrl.T.Helper()
- varargs := []interface{}{ctx, key, object}
- for _, a := range options {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "Set", varargs...)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Set indicates an expected call of Set.
-func (mr *MockSetterCacheInterfaceMockRecorder[T]) Set(ctx, key, object interface{}, options ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{ctx, key, object}, options...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockSetterCacheInterface[T])(nil).Set), varargs...)
-}
diff --git a/toolkit/gocache/lib/cache/cache_test.go b/toolkit/gocache/lib/cache/cache_test.go
deleted file mode 100644
index 7c3f36db..00000000
--- a/toolkit/gocache/lib/cache/cache_test.go
+++ /dev/null
@@ -1,381 +0,0 @@
-package cache
-
-import (
- "context"
- "errors"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/codec"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "go.uber.org/mock/gomock"
-)
-
-func TestNew(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- store := store.NewMockStoreInterface(ctrl)
-
- // When
- cache := New[any](store)
-
- // Then
- assert.IsType(t, new(Cache[any]), cache)
- assert.IsType(t, new(codec.Codec), cache.codec)
-
- assert.Equal(t, store, cache.codec.GetStore())
-}
-
-func TestCacheSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- value := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- mockedStore := store.NewMockStoreInterface(ctrl)
- mockedStore.EXPECT().Set(ctx, "my-key", value, store.OptionsMatcher{
- Expiration: 5 * time.Second,
- }).Return(nil)
-
- cache := New[any](mockedStore)
-
- // When
- err := cache.Set(ctx, "my-key", value, store.WithExpiration(5*time.Second))
- assert.Nil(t, err)
-}
-
-func TestCacheSetWhenErrorOccurs(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- value := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- storeErr := errors.New("an error has occurred while inserting data into store")
-
- mockedStore := store.NewMockStoreInterface(ctrl)
- mockedStore.EXPECT().Set(ctx, "my-key", value, store.OptionsMatcher{
- Expiration: 5 * time.Second,
- }).Return(storeErr)
-
- cache := New[any](mockedStore)
-
- // When
- err := cache.Set(ctx, "my-key", value, store.WithExpiration(5*time.Second))
- assert.Equal(t, storeErr, err)
-}
-
-func TestCacheGet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().Get(ctx, "my-key").Return(cacheValue, nil)
-
- cache := New[any](store)
-
- // When
- value, err := cache.Get(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-}
-
-func TestCacheGetWhenNotFound(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- returnedErr := errors.New("unable to find item in store")
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().Get(ctx, "my-key").Return(nil, returnedErr)
-
- cache := New[any](store)
-
- // When
- value, err := cache.Get(ctx, "my-key")
-
- // Then
- assert.Nil(t, value)
- assert.Equal(t, returnedErr, err)
-}
-
-func TestCacheGetWithTTL(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
- expiration := 1 * time.Second
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().GetWithTTL(ctx, "my-key").
- Return(cacheValue, expiration, nil)
-
- cache := New[any](store)
-
- // When
- value, ttl, err := cache.GetWithTTL(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
- assert.Equal(t, expiration, ttl)
-}
-
-func TestCacheGetWithTTLWhenNotFound(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- returnedErr := errors.New("unable to find item in store")
- expiration := 0 * time.Second
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().GetWithTTL(ctx, "my-key").
- Return(nil, expiration, returnedErr)
-
- cache := New[any](store)
-
- // When
- value, ttl, err := cache.GetWithTTL(ctx, "my-key")
-
- // Then
- assert.Nil(t, value)
- assert.Equal(t, returnedErr, err)
- assert.Equal(t, expiration, ttl)
-}
-
-func TestCacheGetCodec(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- store := store.NewMockStoreInterface(ctrl)
-
- cache := New[any](store)
-
- // When
- value := cache.GetCodec()
-
- // Then
- assert.IsType(t, new(codec.Codec), value)
- assert.Equal(t, store, value.GetStore())
-}
-
-func TestCacheGetType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- store := store.NewMockStoreInterface(ctrl)
-
- cache := New[any](store)
-
- // When - Then
- assert.Equal(t, CacheType, cache.GetType())
-}
-
-func TestCacheGetCacheKeyWhenKeyIsString(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- store := store.NewMockStoreInterface(ctrl)
-
- cache := New[any](store)
-
- // When
- computedKey := cache.getCacheKey("my-Key")
-
- // Then
- assert.Equal(t, "my-Key", computedKey)
-}
-
-func TestCacheGetCacheKeyWhenKeyIsStruct(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- store := store.NewMockStoreInterface(ctrl)
-
- cache := New[any](store)
-
- // When
- key := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- computedKey := cache.getCacheKey(key)
-
- // Then
- assert.Equal(t, "8144fe5310cf0e62ac83fd79c113aad2", computedKey)
-}
-
-type StructWithGenerator struct{}
-
-func (_ *StructWithGenerator) GetCacheKey() string {
- return "my-generated-key"
-}
-
-func TestCacheGetCacheKeyWhenKeyImplementsGenerator(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- store := store.NewMockStoreInterface(ctrl)
-
- cache := New[any](store)
-
- // When
- key := &StructWithGenerator{}
-
- generatedKey := cache.getCacheKey(key)
- // Then
- assert.Equal(t, "my-generated-key", generatedKey)
-}
-
-func TestCacheDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().Delete(ctx, "my-key").Return(nil)
-
- cache := New[any](store)
-
- // When
- err := cache.Delete(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestCacheInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- mockedStore := store.NewMockStoreInterface(ctrl)
- mockedStore.EXPECT().Invalidate(ctx, store.InvalidateOptionsMatcher{
- Tags: []string{"tag1"},
- }).Return(nil)
-
- cache := New[any](mockedStore)
-
- // When
- err := cache.Invalidate(ctx, store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestCacheInvalidateWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unexpected error during invalidation")
-
- mockedStore := store.NewMockStoreInterface(ctrl)
- mockedStore.EXPECT().Invalidate(ctx, store.InvalidateOptionsMatcher{
- Tags: []string{"tag1"},
- }).Return(expectedErr)
-
- cache := New[any](mockedStore)
-
- // When
- err := cache.Invalidate(ctx, store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestCacheClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().Clear(ctx).Return(nil)
-
- cache := New[any](store)
-
- // When
- err := cache.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestCacheClearWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unexpected error during invalidation")
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().Clear(ctx).Return(expectedErr)
-
- cache := New[any](store)
-
- // When
- err := cache.Clear(ctx)
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestCacheDeleteWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unable to delete key")
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().Delete(ctx, "my-key").Return(expectedErr)
-
- cache := New[any](store)
-
- // When
- err := cache.Delete(ctx, "my-key")
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
diff --git a/toolkit/gocache/lib/cache/chain.go b/toolkit/gocache/lib/cache/chain.go
deleted file mode 100644
index f528bb96..00000000
--- a/toolkit/gocache/lib/cache/chain.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package cache
-
-import (
- "context"
- "errors"
- "fmt"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-const (
- // ChainType represents the chain cache type as a string value
- ChainType = "chain"
-)
-
-type chainKeyValue[T any] struct {
- key any
- value T
- ttl time.Duration
- storeType *string
-}
-
-// ChainCache represents the configuration needed by a cache aggregator
-type ChainCache[T any] struct {
- caches []SetterCacheInterface[T]
- setChannel chan *chainKeyValue[T]
-}
-
-// NewChain instantiates a new cache aggregator
-func NewChain[T any](caches ...SetterCacheInterface[T]) *ChainCache[T] {
- chain := &ChainCache[T]{
- caches: caches,
- setChannel: make(chan *chainKeyValue[T], 10000),
- }
-
- go chain.setter()
-
- return chain
-}
-
-// setter sets a value in available caches, until a given cache layer
-func (c *ChainCache[T]) setter() {
- for item := range c.setChannel {
- for _, cache := range c.caches {
- if item.storeType != nil && *item.storeType == cache.GetCodec().GetStore().GetType() {
- break
- }
-
- cache.Set(context.Background(), item.key, item.value, store.WithExpiration(item.ttl))
- }
- }
-}
-
-// Get returns the object stored in cache if it exists
-func (c *ChainCache[T]) Get(ctx context.Context, key any) (T, error) {
- var object T
- var err error
- var ttl time.Duration
-
- for _, cache := range c.caches {
- storeType := cache.GetCodec().GetStore().GetType()
- object, ttl, err = cache.GetWithTTL(ctx, key)
- if err == nil {
- // Set the value back until this cache layer
- c.setChannel <- &chainKeyValue[T]{key, object, ttl, &storeType}
- return object, nil
- }
- }
-
- return object, err
-}
-
-// Set sets a value in available caches
-func (c *ChainCache[T]) Set(ctx context.Context, key any, object T, options ...store.Option) error {
- errs := []error{}
- for _, cache := range c.caches {
- err := cache.Set(ctx, key, object, options...)
- if err != nil {
- storeType := cache.GetCodec().GetStore().GetType()
- errs = append(errs, fmt.Errorf("Unable to set item into cache with store '%s': %v", storeType, err))
- }
- }
- if len(errs) > 0 {
- errStr := ""
- for k, v := range errs {
- errStr += fmt.Sprintf("error %d of %d: %v", k+1, len(errs), v.Error())
- }
- return errors.New(errStr)
- }
-
- return nil
-}
-
-// Delete removes a value from all available caches
-func (c *ChainCache[T]) Delete(ctx context.Context, key any) error {
- for _, cache := range c.caches {
- cache.Delete(ctx, key)
- }
-
- return nil
-}
-
-// Invalidate invalidates cache item from given options
-func (c *ChainCache[T]) Invalidate(ctx context.Context, options ...store.InvalidateOption) error {
- for _, cache := range c.caches {
- cache.Invalidate(ctx, options...)
- }
-
- return nil
-}
-
-// Clear resets all cache data
-func (c *ChainCache[T]) Clear(ctx context.Context) error {
- for _, cache := range c.caches {
- cache.Clear(ctx)
- }
-
- return nil
-}
-
-// GetCaches returns all Chained caches
-func (c *ChainCache[T]) GetCaches() []SetterCacheInterface[T] {
- return c.caches
-}
-
-// GetType returns the cache type
-func (c *ChainCache[T]) GetType() string {
- return ChainType
-}
diff --git a/toolkit/gocache/lib/cache/chain_test.go b/toolkit/gocache/lib/cache/chain_test.go
deleted file mode 100644
index 287b7a9b..00000000
--- a/toolkit/gocache/lib/cache/chain_test.go
+++ /dev/null
@@ -1,453 +0,0 @@
-package cache
-
-import (
- "context"
- "errors"
- "fmt"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/codec"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "go.uber.org/mock/gomock"
-)
-
-func TestNewChain(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache2 := NewMockSetterCacheInterface[any](ctrl)
-
- // When
- cache := NewChain[any](cache1, cache2)
-
- // Then
- assert.IsType(t, new(ChainCache[any]), cache)
-
- assert.Equal(t, []SetterCacheInterface[any]{cache1, cache2}, cache.caches)
-}
-
-func TestChainGetCaches(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache2 := NewMockSetterCacheInterface[any](ctrl)
-
- cache := NewChain[any](cache1, cache2)
-
- // When
- caches := cache.GetCaches()
-
- // Then
- assert.Equal(t, []SetterCacheInterface[any]{cache1, cache2}, caches)
-
- assert.Equal(t, cache1, caches[0])
- assert.Equal(t, cache2, caches[1])
-}
-
-func TestChainGetWhenAvailableInFirstCache(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- // Cache 1
- store1 := store.NewMockStoreInterface(ctrl)
- store1.EXPECT().GetType().AnyTimes().Return("store1")
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- codec1.EXPECT().GetStore().AnyTimes().Return(store1)
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().GetCodec().AnyTimes().Return(codec1)
- cache1.EXPECT().GetWithTTL(ctx, "my-key").Return(cacheValue,
- 0*time.Second, nil)
-
- // Cache 2
- cache2 := NewMockSetterCacheInterface[any](ctrl)
-
- cache := NewChain[any](cache1, cache2)
-
- // When
- value, err := cache.Get(ctx, "my-key")
-
- // Wait for data to be processed
- time.Sleep(100 * time.Millisecond)
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-}
-
-func TestChainGetWhenAvailableInSecondCache(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- // Cache 1
- store1 := store.NewMockStoreInterface(ctrl)
- store1.EXPECT().GetType().AnyTimes().Return("store1")
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- codec1.EXPECT().GetStore().AnyTimes().Return(store1)
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().GetCodec().AnyTimes().Return(codec1)
- cache1.EXPECT().GetWithTTL(ctx, "my-key").Return(nil, 0*time.Second,
- errors.New("unable to find in cache 1"))
- cache1.EXPECT().Set(ctx, "my-key", cacheValue, &store.OptionsMatcher{}).AnyTimes().Return(nil)
-
- // Cache 2
- store2 := store.NewMockStoreInterface(ctrl)
- store2.EXPECT().GetType().AnyTimes().Return("store2")
-
- codec2 := codec.NewMockCodecInterface(ctrl)
- codec2.EXPECT().GetStore().AnyTimes().Return(store2)
-
- cache2 := NewMockSetterCacheInterface[any](ctrl)
- cache2.EXPECT().GetCodec().AnyTimes().Return(codec2)
- cache2.EXPECT().GetWithTTL(ctx, "my-key").Return(cacheValue,
- 0*time.Second, nil)
-
- cache := NewChain[any](cache1, cache2)
-
- // When
- value, err := cache.Get(ctx, "my-key")
-
- // Wait for data to be processed
- time.Sleep(100 * time.Millisecond)
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-}
-
-func TestChainGetWhenNotAvailableInAnyCache(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- // Cache 1
- store1 := store.NewMockStoreInterface(ctrl)
- store1.EXPECT().GetType().Return("store1")
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- codec1.EXPECT().GetStore().Return(store1)
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().GetCodec().Return(codec1)
- cache1.EXPECT().GetWithTTL(ctx, "my-key").Return(nil, 0*time.Second,
- errors.New("unable to find in cache 1"))
-
- // Cache 2
- store2 := store.NewMockStoreInterface(ctrl)
- store2.EXPECT().GetType().Return("store2")
-
- codec2 := codec.NewMockCodecInterface(ctrl)
- codec2.EXPECT().GetStore().Return(store2)
-
- cache2 := NewMockSetterCacheInterface[any](ctrl)
- cache2.EXPECT().GetCodec().Return(codec2)
- cache2.EXPECT().GetWithTTL(ctx, "my-key").Return(nil, 0*time.Second,
- errors.New("unable to find in cache 2"))
-
- cache := NewChain[any](cache1, cache2)
-
- // When
- value, err := cache.Get(ctx, "my-key")
-
- // Wait for data to be processed
- time.Sleep(100 * time.Millisecond)
-
- // Then
- assert.Equal(t, errors.New("unable to find in cache 2"), err)
- assert.Equal(t, nil, value)
-}
-
-func TestChainSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- // Cache 1
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Set(ctx, "my-key", cacheValue).Return(nil)
-
- // Cache 2
- cache2 := NewMockSetterCacheInterface[any](ctrl)
- cache2.EXPECT().Set(ctx, "my-key", cacheValue).Return(nil)
-
- cache := NewChain[any](cache1, cache2)
-
- // When
- err := cache.Set(ctx, "my-key", cacheValue)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestChainSetWhenErrorOnSetting(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- expectedErr := errors.New("an unexpected error occurred while setting data")
-
- // Cache 1
- store1 := store.NewMockStoreInterface(ctrl)
- store1.EXPECT().GetType().Return("store1")
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- codec1.EXPECT().GetStore().Return(store1)
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().GetCodec().Return(codec1)
- cache1.EXPECT().Set(ctx, "my-key", cacheValue).Return(expectedErr)
-
- // Cache 2
- cache2 := NewMockSetterCacheInterface[any](ctrl)
- cache2.EXPECT().Set(ctx, "my-key", cacheValue)
-
- cache := NewChain[any](cache1, cache2)
-
- // When
- err := cache.Set(ctx, "my-key", cacheValue)
-
- // Then
- assert.Error(t, err)
- assert.Equal(t, fmt.Sprintf("error 1 of 1: Unable to set item into cache with store 'store1': %s", expectedErr.Error()), err.Error())
-}
-
-func TestChainDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- // Cache 1
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Delete(ctx, "my-key").Return(nil)
-
- // Cache 2
- cache2 := NewMockSetterCacheInterface[any](ctrl)
- cache2.EXPECT().Delete(ctx, "my-key").Return(nil)
-
- cache := NewChain[any](cache1, cache2)
-
- // When
- err := cache.Delete(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestChainDeleteWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- // Cache 1
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Delete(ctx, "my-key").Return(errors.New("an error has occurred while deleting key"))
-
- // Cache 2
- cache2 := NewMockSetterCacheInterface[any](ctrl)
- cache2.EXPECT().Delete(ctx, "my-key").Return(nil)
-
- cache := NewChain[any](cache1, cache2)
-
- // When
- err := cache.Delete(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestChainInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- // Cache 1
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Invalidate(ctx).Return(nil)
-
- // Cache 2
- cache2 := NewMockSetterCacheInterface[any](ctrl)
- cache2.EXPECT().Invalidate(ctx).Return(nil)
-
- cache := NewChain[any](cache1, cache2)
-
- // When
- err := cache.Invalidate(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestChainInvalidateWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- // Cache 1
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Invalidate(ctx).Return(errors.New("an unexpected error has occurred while invalidation data"))
-
- // Cache 2
- cache2 := NewMockSetterCacheInterface[any](ctrl)
- cache2.EXPECT().Invalidate(ctx).Return(nil)
-
- cache := NewChain[any](cache1, cache2)
-
- // When
- err := cache.Invalidate(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestChainClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- // Cache 1
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Clear(ctx).Return(nil)
-
- // Cache 2
- cache2 := NewMockSetterCacheInterface[any](ctrl)
- cache2.EXPECT().Clear(ctx).Return(nil)
-
- cache := NewChain[any](cache1, cache2)
-
- // When
- err := cache.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestChainClearWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- // Cache 1
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Clear(ctx).Return(errors.New("an unexpected error has occurred while invalidation data"))
-
- // Cache 2
- cache2 := NewMockSetterCacheInterface[any](ctrl)
- cache2.EXPECT().Clear(ctx).Return(nil)
-
- cache := NewChain[any](cache1, cache2)
-
- // When
- err := cache.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestChainGetType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
-
- cache := NewChain[any](cache1)
-
- // When - Then
- assert.Equal(t, ChainType, cache.GetType())
-}
-
-func TestCacheChecksum(t *testing.T) {
- testCases := []struct {
- value any
- expectedHash string
- }{
- {value: 273273623, expectedHash: "a187c153af38575778244cb3796536da"},
- {value: "hello-world", expectedHash: "f31215be6928a6f6e0c7c1cf2c68054e"},
- {value: []byte(`hello-world`), expectedHash: "f097ebac995e666eb074e019cd39d99b"},
- {value: struct{ Label string }{}, expectedHash: "2938da2beee350d6ea988e404109f428"},
- {value: struct{ Label string }{Label: "hello-world"}, expectedHash: "4119a1c8530a0420859f1c6ecf2dc0b7"},
- {value: struct{ Label string }{Label: "hello-everyone"}, expectedHash: "1d7e7ed4acd56d2635f7cb33aa702bdd"},
- }
-
- for _, tc := range testCases {
- value := checksum(tc.value)
-
- assert.Equal(t, tc.expectedHash, value)
- }
-}
-
-func TestChainSetWhenErrorInChain(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
- store1 := NewMockSetterCacheInterface[any](ctrl)
-
- store1.EXPECT().GetType().AnyTimes().Return("store1")
- codec1 := codec.NewMockCodecInterface(ctrl)
- codec1.EXPECT().GetStore().AnyTimes().Return(store1)
- store1.EXPECT().GetCodec().AnyTimes().Return(codec1)
-
- ctx := context.Background()
- key := "test-key"
- value := "test-value"
- interError := errors.New("an issue occurred with the cache")
- store1.EXPECT().Set(ctx, key, value, nil).DoAndReturn(func(_, _, _, _ interface{}) error {
- return interError
- })
-
- store2 := NewMockSetterCacheInterface[any](ctrl)
-
- cache := NewChain[any](store1, store2)
-
- // assert store2 set is called
- store2.EXPECT().Set(ctx, key, value, nil).Return(nil)
-
- // When - Then
- err := cache.Set(ctx, key, value, nil)
-
- expErr := errors.New("error 1 of 1: Unable to set item into cache with store 'store1': an issue occurred with the cache")
- // Then
- assert.Equal(t, expErr, err)
-}
diff --git a/toolkit/gocache/lib/cache/interface.go b/toolkit/gocache/lib/cache/interface.go
deleted file mode 100644
index ed3c7e2c..00000000
--- a/toolkit/gocache/lib/cache/interface.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package cache
-
-import (
- "context"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/codec"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-// CacheInterface represents the interface for all caches (aggregates, metric, memory, redis, ...)
-type CacheInterface[T any] interface {
- Get(ctx context.Context, key any) (T, error)
- Set(ctx context.Context, key any, object T, options ...store.Option) error
- Delete(ctx context.Context, key any) error
- Invalidate(ctx context.Context, options ...store.InvalidateOption) error
- Clear(ctx context.Context) error
- GetType() string
-}
-
-type CacheKeyGenerator interface {
- GetCacheKey() string
-}
-
-// SetterCacheInterface represents the interface for caches that allows
-// storage (for instance: memory, redis, ...)
-type SetterCacheInterface[T any] interface {
- // CacheInterface[T] TODO: Waiting for gomock to support nested interfaces with generics.
- Get(ctx context.Context, key any) (T, error)
- Set(ctx context.Context, key any, object T, options ...store.Option) error
- Delete(ctx context.Context, key any) error
- Invalidate(ctx context.Context, options ...store.InvalidateOption) error
- Clear(ctx context.Context) error
- GetType() string
-
- GetWithTTL(ctx context.Context, key any) (T, time.Duration, error)
-
- GetCodec() codec.CodecInterface
-}
diff --git a/toolkit/gocache/lib/cache/loadable.go b/toolkit/gocache/lib/cache/loadable.go
deleted file mode 100644
index dd95463c..00000000
--- a/toolkit/gocache/lib/cache/loadable.go
+++ /dev/null
@@ -1,141 +0,0 @@
-package cache
-
-import (
- "context"
- "errors"
- "fmt"
- "sync"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "golang.org/x/sync/singleflight"
-)
-
-const (
- // LoadableType represents the loadable cache type as a string value
- LoadableType = "loadable"
-)
-
-type loadableKeyValue[T any] struct {
- key any
- value T
-}
-
-type LoadFunction[T any] func(ctx context.Context, key any) (T, error)
-
-// LoadableCache represents a cache that uses a function to load data
-type LoadableCache[T any] struct {
- singleFlight singleflight.Group
- loadFunc LoadFunction[T]
- cache CacheInterface[T]
- setChannel chan *loadableKeyValue[T]
- setterWg *sync.WaitGroup
-}
-
-// NewLoadable instantiates a new cache that uses a function to load data
-func NewLoadable[T any](loadFunc LoadFunction[T], cache CacheInterface[T]) *LoadableCache[T] {
- loadable := &LoadableCache[T]{
- singleFlight: singleflight.Group{},
- loadFunc: loadFunc,
- cache: cache,
- setChannel: make(chan *loadableKeyValue[T], 10000),
- setterWg: &sync.WaitGroup{},
- }
-
- loadable.setterWg.Add(1)
- go loadable.setter()
-
- return loadable
-}
-
-func (c *LoadableCache[T]) setter() {
- defer c.setterWg.Done()
-
- for item := range c.setChannel {
- c.Set(context.Background(), item.key, item.value)
-
- cacheKey := c.getCacheKey(item.key)
- c.singleFlight.Forget(cacheKey)
- }
-}
-
-// Get returns the object stored in cache if it exists
-func (c *LoadableCache[T]) Get(ctx context.Context, key any) (T, error) {
- var err error
-
- object, err := c.cache.Get(ctx, key)
- if err == nil {
- return object, err
- }
-
- // Unable to find in cache, try to load it from load function
- cacheKey := c.getCacheKey(key)
- zero := *new(T)
-
- loadedResult, err, _ := c.singleFlight.Do(
- cacheKey,
- func() (any, error) {
- return c.loadFunc(ctx, key)
- },
- )
- if err != nil {
- return zero, err
- }
-
- var ok bool
- if object, ok = loadedResult.(T); !ok {
- return zero, errors.New(
- fmt.Sprintf("returned value can't be cast to %T", zero),
- )
- }
-
- // Then, put it back in cache
- c.setChannel <- &loadableKeyValue[T]{key, object}
-
- return object, err
-}
-
-// Set sets a value in available caches
-func (c *LoadableCache[T]) Set(ctx context.Context, key any, object T, options ...store.Option) error {
- return c.cache.Set(ctx, key, object, options...)
-}
-
-// Delete removes a value from cache
-func (c *LoadableCache[T]) Delete(ctx context.Context, key any) error {
- return c.cache.Delete(ctx, key)
-}
-
-// Invalidate invalidates cache item from given options
-func (c *LoadableCache[T]) Invalidate(ctx context.Context, options ...store.InvalidateOption) error {
- return c.cache.Invalidate(ctx, options...)
-}
-
-// Clear resets all cache data
-func (c *LoadableCache[T]) Clear(ctx context.Context) error {
- return c.cache.Clear(ctx)
-}
-
-// GetType returns the cache type
-func (c *LoadableCache[T]) GetType() string {
- return LoadableType
-}
-
-func (c *LoadableCache[T]) Close() error {
- close(c.setChannel)
- c.setterWg.Wait()
-
- return nil
-}
-
-// getCacheKey returns the cache key for the given key object by returning
-// the key if type is string or by computing a checksum of key structure
-// if its type is other than string
-func (c *LoadableCache[T]) getCacheKey(key any) string {
- switch v := key.(type) {
- case string:
- return v
- case CacheKeyGenerator:
- return v.GetCacheKey()
- default:
- return checksum(key)
- }
-}
diff --git a/toolkit/gocache/lib/cache/loadable_test.go b/toolkit/gocache/lib/cache/loadable_test.go
deleted file mode 100644
index 26bd0d6b..00000000
--- a/toolkit/gocache/lib/cache/loadable_test.go
+++ /dev/null
@@ -1,301 +0,0 @@
-package cache
-
-import (
- "context"
- "errors"
- "sync"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "go.uber.org/mock/gomock"
-)
-
-func TestNewLoadable(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
-
- loadFunc := func(_ context.Context, key any) (any, error) {
- return "test data loaded", nil
- }
-
- // When
- cache := NewLoadable[any](loadFunc, cache1)
-
- // Then
- assert.IsType(t, new(LoadableCache[any]), cache)
-
- assert.IsType(t, new(LoadFunction[any]), &cache.loadFunc)
- assert.Equal(t, cache1, cache.cache)
-}
-
-func TestLoadableGetWhenAlreadyInCache(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Get(ctx, "my-key").Return(cacheValue, nil)
-
- loadFunc := func(_ context.Context, key any) (any, error) {
- return nil, errors.New("should not be called")
- }
-
- cache := NewLoadable[any](loadFunc, cache1)
-
- // When
- value, err := cache.Get(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-}
-
-func TestLoadableGetWhenNotAvailableInLoadFunc(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- // Cache
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Get(ctx, "my-key").Return(nil, errors.New("unable to find in cache 1"))
-
- loadFunc := func(_ context.Context, key any) (any, error) {
- return nil, errors.New("an error has occurred while loading data from custom source")
- }
-
- cache := NewLoadable[any](loadFunc, cache1)
-
- // When
- value, err := cache.Get(ctx, "my-key")
-
- // Then
- assert.Nil(t, value)
- assert.Equal(t, errors.New("an error has occurred while loading data from custom source"), err)
-}
-
-func TestLoadableGetWhenAvailableInLoadFunc(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- // Cache 1
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Get(ctx, "my-key").Return(nil, errors.New("unable to find in cache 1"))
- cache1.EXPECT().Get(ctx, "my-key").Return(nil, errors.New("unable to find in cache 1"))
- cache1.EXPECT().Get(ctx, "my-key").Return(nil, errors.New("unable to find in cache 1"))
- cache1.EXPECT().Set(ctx, "my-key", cacheValue).AnyTimes().Return(nil)
-
- var loadCallCount int32
- pauseLoadFn := make(chan struct{})
-
- loadFunc := func(_ context.Context, key any) (any, error) {
- atomic.AddInt32(&loadCallCount, 1)
- <-pauseLoadFn
- time.Sleep(time.Millisecond * 10)
- return cacheValue, nil
- }
-
- cache := NewLoadable[any](loadFunc, cache1)
-
- const numRequests = 3
- var started sync.WaitGroup
- started.Add(numRequests)
- var finished sync.WaitGroup
- finished.Add(numRequests)
- for i := 0; i < numRequests; i++ {
- go func() {
- defer finished.Done()
- started.Done()
- // When
- value, err := cache.Get(ctx, "my-key")
-
- // Wait for data to be processed
- for len(cache.setChannel) > 0 {
- time.Sleep(1 * time.Millisecond)
- }
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
- }()
- }
-
- started.Wait()
- close(pauseLoadFn)
- finished.Wait()
-
- assert.Equal(t, int32(1), loadCallCount)
-}
-
-func TestLoadableDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Delete(ctx, "my-key").Return(nil)
-
- loadFunc := func(_ context.Context, key any) (any, error) {
- return "a value", nil
- }
-
- cache := NewLoadable[any](loadFunc, cache1)
-
- // When
- err := cache.Delete(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestLoadableDeleteWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unable to delete key")
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Delete(ctx, "my-key").Return(expectedErr)
-
- loadFunc := func(_ context.Context, key any) (any, error) {
- return "a value", nil
- }
-
- cache := NewLoadable[any](loadFunc, cache1)
-
- // When
- err := cache.Delete(ctx, "my-key")
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestLoadableInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Invalidate(ctx).Return(nil)
-
- loadFunc := func(_ context.Context, key any) (any, error) {
- return "a value", nil
- }
-
- cache := NewLoadable[any](loadFunc, cache1)
-
- // When
- err := cache.Invalidate(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestLoadableInvalidateWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unexpected error when invalidating data")
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Invalidate(ctx).Return(expectedErr)
-
- loadFunc := func(_ context.Context, key any) (any, error) {
- return "a value", nil
- }
-
- cache := NewLoadable[any](loadFunc, cache1)
-
- // When
- err := cache.Invalidate(ctx)
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestLoadableClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Clear(ctx).Return(nil)
-
- loadFunc := func(_ context.Context, key any) (any, error) {
- return "a value", nil
- }
-
- cache := NewLoadable[any](loadFunc, cache1)
-
- // When
- err := cache.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestLoadableClearWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unexpected error when invalidating data")
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Clear(ctx).Return(expectedErr)
-
- loadFunc := func(_ context.Context, key any) (any, error) {
- return "a value", nil
- }
-
- cache := NewLoadable[any](loadFunc, cache1)
-
- // When
- err := cache.Clear(ctx)
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestLoadableGetType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
-
- loadFunc := func(_ context.Context, key any) (any, error) {
- return "test data loaded", nil
- }
-
- cache := NewLoadable[any](loadFunc, cache1)
-
- // When - Then
- assert.Equal(t, LoadableType, cache.GetType())
-}
diff --git a/toolkit/gocache/lib/cache/metric.go b/toolkit/gocache/lib/cache/metric.go
deleted file mode 100644
index c832062e..00000000
--- a/toolkit/gocache/lib/cache/metric.go
+++ /dev/null
@@ -1,78 +0,0 @@
-package cache
-
-import (
- "context"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/metrics"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-const (
- // MetricType represents the metric cache type as a string value
- MetricType = "metric"
-)
-
-// MetricCache is the struct that specifies metrics available for different caches
-type MetricCache[T any] struct {
- metrics metrics.MetricsInterface
- cache CacheInterface[T]
-}
-
-// NewMetric creates a new cache with metrics and a given cache storage
-func NewMetric[T any](metrics metrics.MetricsInterface, cache CacheInterface[T]) *MetricCache[T] {
- metricCache := &MetricCache[T]{
- metrics: metrics,
- cache: cache,
- }
-
- metricCache.updateMetrics(cache)
-
- return metricCache
-}
-
-// Get obtains a value from cache and also records metrics
-func (c *MetricCache[T]) Get(ctx context.Context, key any) (T, error) {
- result, err := c.cache.Get(ctx, key)
-
- c.updateMetrics(c.cache)
-
- return result, err
-}
-
-// Set sets a value from the cache
-func (c *MetricCache[T]) Set(ctx context.Context, key any, object T, options ...store.Option) error {
- return c.cache.Set(ctx, key, object, options...)
-}
-
-// Delete removes a value from the cache
-func (c *MetricCache[T]) Delete(ctx context.Context, key any) error {
- return c.cache.Delete(ctx, key)
-}
-
-// Invalidate invalidates cache item from given options
-func (c *MetricCache[T]) Invalidate(ctx context.Context, options ...store.InvalidateOption) error {
- return c.cache.Invalidate(ctx, options...)
-}
-
-// Clear resets all cache data
-func (c *MetricCache[T]) Clear(ctx context.Context) error {
- return c.cache.Clear(ctx)
-}
-
-// Get obtains a value from cache and also records metrics
-func (c *MetricCache[T]) updateMetrics(cache CacheInterface[T]) {
- switch current := cache.(type) {
- case *ChainCache[T]:
- for _, cache := range current.GetCaches() {
- c.updateMetrics(cache)
- }
-
- case SetterCacheInterface[T]:
- c.metrics.RecordFromCodec(current.GetCodec())
- }
-}
-
-// GetType returns the cache type
-func (c *MetricCache[T]) GetType() string {
- return MetricType
-}
diff --git a/toolkit/gocache/lib/cache/metric_test.go b/toolkit/gocache/lib/cache/metric_test.go
deleted file mode 100644
index b77e8f07..00000000
--- a/toolkit/gocache/lib/cache/metric_test.go
+++ /dev/null
@@ -1,291 +0,0 @@
-package cache
-
-import (
- "context"
- "errors"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/codec"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/metrics"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "go.uber.org/mock/gomock"
-)
-
-func TestNewMetric(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().GetCodec().Return(codec1).Times(1)
- metrics := metrics.NewMockMetricsInterface(ctrl)
- metrics.EXPECT().RecordFromCodec(codec1).Times(1)
-
- // When
- cache := NewMetric[any](metrics, cache1)
-
- // Then
- assert.IsType(t, new(MetricCache[any]), cache)
-
- assert.Equal(t, cache1, cache.cache)
- assert.Equal(t, metrics, cache.metrics)
-}
-
-func TestMetricGet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Get(ctx, "my-key").Return(cacheValue, nil)
- cache1.EXPECT().GetCodec().Return(codec1).MinTimes(1)
-
- metrics := metrics.NewMockMetricsInterface(ctrl)
- metrics.EXPECT().RecordFromCodec(codec1).MinTimes(1)
-
- cache := NewMetric[any](metrics, cache1)
-
- // When
- value, err := cache.Get(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-}
-
-func TestMetricGetWhenChainCache(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- store1 := store.NewMockStoreInterface(ctrl)
- store1.EXPECT().GetType().AnyTimes().Return("store1")
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- codec1.EXPECT().GetStore().AnyTimes().Return(store1)
-
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().GetWithTTL(ctx, "my-key").Return(cacheValue,
- 0*time.Second, nil)
- cache1.EXPECT().GetCodec().AnyTimes().Return(codec1)
-
- chainCache := NewChain[any](cache1)
-
- metrics := metrics.NewMockMetricsInterface(ctrl)
- metrics.EXPECT().RecordFromCodec(codec1).AnyTimes()
-
- cache := NewMetric[any](metrics, chainCache)
-
- // When
- value, err := cache.Get(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-}
-
-func TestMetricSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- value := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Set(ctx, "my-key", value).Return(nil)
- cache1.EXPECT().GetCodec().Return(codec1).Times(1)
-
- metrics := metrics.NewMockMetricsInterface(ctrl)
- metrics.EXPECT().RecordFromCodec(codec1).Times(1)
-
- cache := NewMetric[any](metrics, cache1)
-
- // When
- err := cache.Set(ctx, "my-key", value)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestMetricDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Delete(ctx, "my-key").Return(nil)
- cache1.EXPECT().GetCodec().Return(codec1).Times(1)
-
- metrics := metrics.NewMockMetricsInterface(ctrl)
- metrics.EXPECT().RecordFromCodec(codec1).Times(1)
-
- cache := NewMetric[any](metrics, cache1)
-
- // When
- err := cache.Delete(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestMetricDeleteWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unable to delete key")
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Delete(ctx, "my-key").Return(expectedErr)
- cache1.EXPECT().GetCodec().Return(codec1).Times(1)
-
- metrics := metrics.NewMockMetricsInterface(ctrl)
- metrics.EXPECT().RecordFromCodec(codec1).Times(1)
-
- cache := NewMetric[any](metrics, cache1)
-
- // When
- err := cache.Delete(ctx, "my-key")
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestMetricInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Invalidate(ctx).Return(nil)
- cache1.EXPECT().GetCodec().Return(codec1).Times(1)
-
- metrics := metrics.NewMockMetricsInterface(ctrl)
- metrics.EXPECT().RecordFromCodec(codec1).Times(1)
-
- cache := NewMetric[any](metrics, cache1)
-
- // When
- err := cache.Invalidate(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestMetricInvalidateWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unexpected error while invalidating data")
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Invalidate(ctx).Return(expectedErr)
- cache1.EXPECT().GetCodec().Return(codec1).Times(1)
-
- metrics := metrics.NewMockMetricsInterface(ctrl)
- metrics.EXPECT().RecordFromCodec(codec1).Times(1)
-
- cache := NewMetric[any](metrics, cache1)
-
- // When
- err := cache.Invalidate(ctx)
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestMetricClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Clear(ctx).Return(nil)
- cache1.EXPECT().GetCodec().Return(codec1).Times(1)
-
- metrics := metrics.NewMockMetricsInterface(ctrl)
- metrics.EXPECT().RecordFromCodec(codec1).Times(1)
-
- cache := NewMetric[any](metrics, cache1)
-
- // When
- err := cache.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestMetricClearWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unexpected error while clearing cache")
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().Clear(ctx).Return(expectedErr)
- cache1.EXPECT().GetCodec().Return(codec1).Times(1)
-
- metrics := metrics.NewMockMetricsInterface(ctrl)
- metrics.EXPECT().RecordFromCodec(codec1).Times(1)
-
- cache := NewMetric[any](metrics, cache1)
-
- // When
- err := cache.Clear(ctx)
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestMetricGetType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- codec1 := codec.NewMockCodecInterface(ctrl)
- cache1 := NewMockSetterCacheInterface[any](ctrl)
- cache1.EXPECT().GetCodec().Return(codec1).Times(1)
- metrics := metrics.NewMockMetricsInterface(ctrl)
- metrics.EXPECT().RecordFromCodec(codec1).Times(1)
-
- cache := NewMetric[any](metrics, cache1)
-
- // When - Then
- assert.Equal(t, MetricType, cache.GetType())
-}
diff --git a/toolkit/gocache/lib/codec/codec.go b/toolkit/gocache/lib/codec/codec.go
deleted file mode 100644
index 0ad38910..00000000
--- a/toolkit/gocache/lib/codec/codec.go
+++ /dev/null
@@ -1,142 +0,0 @@
-package codec
-
-import (
- "context"
- "sync"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-// Stats allows to returns some statistics of codec usage
-type Stats struct {
- Hits int
- Miss int
- SetSuccess int
- SetError int
- DeleteSuccess int
- DeleteError int
- InvalidateSuccess int
- InvalidateError int
- ClearSuccess int
- ClearError int
-}
-
-// Codec represents an instance of a cache store
-type Codec struct {
- store store.StoreInterface
- stats *Stats
- statsMtx sync.Mutex
-}
-
-// New return a new codec instance
-func New(store store.StoreInterface) *Codec {
- return &Codec{
- store: store,
- stats: &Stats{},
- }
-}
-
-// Get allows to retrieve the value from a given key identifier
-func (c *Codec) Get(ctx context.Context, key any) (any, error) {
- val, err := c.store.Get(ctx, key)
-
- c.statsMtx.Lock()
- defer c.statsMtx.Unlock()
- if err == nil {
- c.stats.Hits++
- } else {
- c.stats.Miss++
- }
-
- return val, err
-}
-
-// GetWithTTL allows to retrieve the value from a given key identifier and its corresponding TTL
-func (c *Codec) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
- val, ttl, err := c.store.GetWithTTL(ctx, key)
-
- c.statsMtx.Lock()
- defer c.statsMtx.Unlock()
- if err == nil {
- c.stats.Hits++
- } else {
- c.stats.Miss++
- }
-
- return val, ttl, err
-}
-
-// Set allows to set a value for a given key identifier and also allows to specify
-// an expiration time
-func (c *Codec) Set(ctx context.Context, key any, value any, options ...store.Option) error {
- err := c.store.Set(ctx, key, value, options...)
-
- c.statsMtx.Lock()
- defer c.statsMtx.Unlock()
- if err == nil {
- c.stats.SetSuccess++
- } else {
- c.stats.SetError++
- }
-
- return err
-}
-
-// Delete allows to remove a value for a given key identifier
-func (c *Codec) Delete(ctx context.Context, key any) error {
- err := c.store.Delete(ctx, key)
-
- c.statsMtx.Lock()
- defer c.statsMtx.Unlock()
- if err == nil {
- c.stats.DeleteSuccess++
- } else {
- c.stats.DeleteError++
- }
-
- return err
-}
-
-// Invalidate invalidates some cach items from given options
-func (c *Codec) Invalidate(ctx context.Context, options ...store.InvalidateOption) error {
- err := c.store.Invalidate(ctx, options...)
-
- c.statsMtx.Lock()
- defer c.statsMtx.Unlock()
- if err == nil {
- c.stats.InvalidateSuccess++
- } else {
- c.stats.InvalidateError++
- }
-
- return err
-}
-
-// Clear resets all codec store data
-func (c *Codec) Clear(ctx context.Context) error {
- err := c.store.Clear(ctx)
-
- c.statsMtx.Lock()
- defer c.statsMtx.Unlock()
- if err == nil {
- c.stats.ClearSuccess++
- } else {
- c.stats.ClearError++
- }
-
- return err
-}
-
-// GetStore returns the store associated to this codec
-func (c *Codec) GetStore() store.StoreInterface {
- return c.store
-}
-
-// GetStats returns some statistics about the current codec
-func (c *Codec) GetStats() *Stats {
- c.statsMtx.Lock()
- defer c.statsMtx.Unlock()
- stats := *c.stats
- return &stats
-}
diff --git a/toolkit/gocache/lib/codec/codec_mock.go b/toolkit/gocache/lib/codec/codec_mock.go
deleted file mode 100644
index c9f3ce85..00000000
--- a/toolkit/gocache/lib/codec/codec_mock.go
+++ /dev/null
@@ -1,162 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: lib/codec/interface.go
-
-// Package codec is a generated GoMock package.
-package codec
-
-import (
- context "context"
- reflect "reflect"
- time "time"
-
- store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- gomock "go.uber.org/mock/gomock"
-)
-
-// MockCodecInterface is a mock of CodecInterface interface.
-type MockCodecInterface struct {
- ctrl *gomock.Controller
- recorder *MockCodecInterfaceMockRecorder
-}
-
-// MockCodecInterfaceMockRecorder is the mock recorder for MockCodecInterface.
-type MockCodecInterfaceMockRecorder struct {
- mock *MockCodecInterface
-}
-
-// NewMockCodecInterface creates a new mock instance.
-func NewMockCodecInterface(ctrl *gomock.Controller) *MockCodecInterface {
- mock := &MockCodecInterface{ctrl: ctrl}
- mock.recorder = &MockCodecInterfaceMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockCodecInterface) EXPECT() *MockCodecInterfaceMockRecorder {
- return m.recorder
-}
-
-// Clear mocks base method.
-func (m *MockCodecInterface) Clear(ctx context.Context) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Clear", ctx)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Clear indicates an expected call of Clear.
-func (mr *MockCodecInterfaceMockRecorder) Clear(ctx interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockCodecInterface)(nil).Clear), ctx)
-}
-
-// Delete mocks base method.
-func (m *MockCodecInterface) Delete(ctx context.Context, key any) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Delete", ctx, key)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Delete indicates an expected call of Delete.
-func (mr *MockCodecInterfaceMockRecorder) Delete(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockCodecInterface)(nil).Delete), ctx, key)
-}
-
-// Get mocks base method.
-func (m *MockCodecInterface) Get(ctx context.Context, key any) (any, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", ctx, key)
- ret0, _ := ret[0].(any)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockCodecInterfaceMockRecorder) Get(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockCodecInterface)(nil).Get), ctx, key)
-}
-
-// GetStats mocks base method.
-func (m *MockCodecInterface) GetStats() *Stats {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetStats")
- ret0, _ := ret[0].(*Stats)
- return ret0
-}
-
-// GetStats indicates an expected call of GetStats.
-func (mr *MockCodecInterfaceMockRecorder) GetStats() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStats", reflect.TypeOf((*MockCodecInterface)(nil).GetStats))
-}
-
-// GetStore mocks base method.
-func (m *MockCodecInterface) GetStore() store.StoreInterface {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetStore")
- ret0, _ := ret[0].(store.StoreInterface)
- return ret0
-}
-
-// GetStore indicates an expected call of GetStore.
-func (mr *MockCodecInterfaceMockRecorder) GetStore() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetStore", reflect.TypeOf((*MockCodecInterface)(nil).GetStore))
-}
-
-// GetWithTTL mocks base method.
-func (m *MockCodecInterface) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetWithTTL", ctx, key)
- ret0, _ := ret[0].(any)
- ret1, _ := ret[1].(time.Duration)
- ret2, _ := ret[2].(error)
- return ret0, ret1, ret2
-}
-
-// GetWithTTL indicates an expected call of GetWithTTL.
-func (mr *MockCodecInterfaceMockRecorder) GetWithTTL(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWithTTL", reflect.TypeOf((*MockCodecInterface)(nil).GetWithTTL), ctx, key)
-}
-
-// Invalidate mocks base method.
-func (m *MockCodecInterface) Invalidate(ctx context.Context, options ...store.InvalidateOption) error {
- m.ctrl.T.Helper()
- varargs := []interface{}{ctx}
- for _, a := range options {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "Invalidate", varargs...)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Invalidate indicates an expected call of Invalidate.
-func (mr *MockCodecInterfaceMockRecorder) Invalidate(ctx interface{}, options ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{ctx}, options...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invalidate", reflect.TypeOf((*MockCodecInterface)(nil).Invalidate), varargs...)
-}
-
-// Set mocks base method.
-func (m *MockCodecInterface) Set(ctx context.Context, key, value any, options ...store.Option) error {
- m.ctrl.T.Helper()
- varargs := []interface{}{ctx, key, value}
- for _, a := range options {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "Set", varargs...)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Set indicates an expected call of Set.
-func (mr *MockCodecInterfaceMockRecorder) Set(ctx, key, value interface{}, options ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{ctx, key, value}, options...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockCodecInterface)(nil).Set), varargs...)
-}
diff --git a/toolkit/gocache/lib/codec/codec_test.go b/toolkit/gocache/lib/codec/codec_test.go
deleted file mode 100644
index 27bbf3fd..00000000
--- a/toolkit/gocache/lib/codec/codec_test.go
+++ /dev/null
@@ -1,448 +0,0 @@
-package codec
-
-import (
- "context"
- "errors"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "go.uber.org/mock/gomock"
-)
-
-func TestNew(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- store := store.NewMockStoreInterface(ctrl)
-
- // When
- codec := New(store)
-
- // Then
- assert.IsType(t, new(Codec), codec)
-}
-
-func TestGetWhenHit(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().Get(ctx, "my-key").Return(cacheValue, nil)
-
- codec := New(store)
-
- // When
- value, err := codec.Get(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-
- assert.Equal(t, 1, codec.GetStats().Hits)
- assert.Equal(t, 0, codec.GetStats().Miss)
- assert.Equal(t, 0, codec.GetStats().SetSuccess)
- assert.Equal(t, 0, codec.GetStats().SetError)
- assert.Equal(t, 0, codec.GetStats().DeleteSuccess)
- assert.Equal(t, 0, codec.GetStats().DeleteError)
- assert.Equal(t, 0, codec.GetStats().InvalidateSuccess)
- assert.Equal(t, 0, codec.GetStats().InvalidateError)
- assert.Equal(t, 0, codec.GetStats().ClearSuccess)
- assert.Equal(t, 0, codec.GetStats().ClearError)
-}
-
-func TestGetWithTTLWhenHit(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().GetWithTTL(ctx, "my-key").Return(cacheValue, 1*time.Second, nil)
-
- codec := New(store)
-
- // When
- value, ttl, err := codec.GetWithTTL(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
- assert.Equal(t, 1*time.Second, ttl)
-
- assert.Equal(t, 1, codec.GetStats().Hits)
- assert.Equal(t, 0, codec.GetStats().Miss)
- assert.Equal(t, 0, codec.GetStats().SetSuccess)
- assert.Equal(t, 0, codec.GetStats().SetError)
- assert.Equal(t, 0, codec.GetStats().DeleteSuccess)
- assert.Equal(t, 0, codec.GetStats().DeleteError)
- assert.Equal(t, 0, codec.GetStats().InvalidateSuccess)
- assert.Equal(t, 0, codec.GetStats().InvalidateError)
- assert.Equal(t, 0, codec.GetStats().ClearSuccess)
- assert.Equal(t, 0, codec.GetStats().ClearError)
-}
-
-func TestGetWithTTLWhenMiss(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unable to find in store")
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().GetWithTTL(ctx, "my-key").Return(nil, 0*time.Second, expectedErr)
-
- codec := New(store)
-
- // When
- value, ttl, err := codec.GetWithTTL(ctx, "my-key")
-
- // Then
- assert.Equal(t, expectedErr, err)
- assert.Nil(t, value)
- assert.Equal(t, 0*time.Second, ttl)
-
- assert.Equal(t, 0, codec.GetStats().Hits)
- assert.Equal(t, 1, codec.GetStats().Miss)
- assert.Equal(t, 0, codec.GetStats().SetSuccess)
- assert.Equal(t, 0, codec.GetStats().SetError)
- assert.Equal(t, 0, codec.GetStats().DeleteSuccess)
- assert.Equal(t, 0, codec.GetStats().DeleteError)
- assert.Equal(t, 0, codec.GetStats().InvalidateSuccess)
- assert.Equal(t, 0, codec.GetStats().InvalidateError)
- assert.Equal(t, 0, codec.GetStats().ClearSuccess)
- assert.Equal(t, 0, codec.GetStats().ClearError)
-}
-
-func TestGetWhenMiss(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unable to find in store")
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().Get(ctx, "my-key").Return(nil, expectedErr)
-
- codec := New(store)
-
- // When
- value, err := codec.Get(ctx, "my-key")
-
- // Then
- assert.Equal(t, expectedErr, err)
- assert.Nil(t, value)
-
- assert.Equal(t, 0, codec.GetStats().Hits)
- assert.Equal(t, 1, codec.GetStats().Miss)
- assert.Equal(t, 0, codec.GetStats().SetSuccess)
- assert.Equal(t, 0, codec.GetStats().SetError)
- assert.Equal(t, 0, codec.GetStats().DeleteSuccess)
- assert.Equal(t, 0, codec.GetStats().DeleteError)
- assert.Equal(t, 0, codec.GetStats().InvalidateSuccess)
- assert.Equal(t, 0, codec.GetStats().InvalidateError)
- assert.Equal(t, 0, codec.GetStats().ClearSuccess)
- assert.Equal(t, 0, codec.GetStats().ClearError)
-}
-
-func TestSetWhenSuccess(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- mockedStore := store.NewMockStoreInterface(ctrl)
- mockedStore.EXPECT().Set(ctx, "my-key", cacheValue, store.OptionsMatcher{
- Expiration: 5 * time.Second,
- }).Return(nil)
-
- codec := New(mockedStore)
-
- // When
- err := codec.Set(ctx, "my-key", cacheValue, store.WithExpiration(5*time.Second))
-
- // Then
- assert.Nil(t, err)
-
- assert.Equal(t, 0, codec.GetStats().Hits)
- assert.Equal(t, 0, codec.GetStats().Miss)
- assert.Equal(t, 1, codec.GetStats().SetSuccess)
- assert.Equal(t, 0, codec.GetStats().SetError)
- assert.Equal(t, 0, codec.GetStats().DeleteSuccess)
- assert.Equal(t, 0, codec.GetStats().DeleteError)
- assert.Equal(t, 0, codec.GetStats().InvalidateSuccess)
- assert.Equal(t, 0, codec.GetStats().InvalidateError)
- assert.Equal(t, 0, codec.GetStats().ClearSuccess)
- assert.Equal(t, 0, codec.GetStats().ClearError)
-}
-
-func TestSetWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &struct {
- Hello string
- }{
- Hello: "world",
- }
-
- expectedErr := errors.New("unable to set value in store")
-
- mockedStore := store.NewMockStoreInterface(ctrl)
- mockedStore.EXPECT().Set(ctx, "my-key", cacheValue, store.OptionsMatcher{
- Expiration: 5 * time.Second,
- }).Return(expectedErr)
-
- codec := New(mockedStore)
-
- // When
- err := codec.Set(ctx, "my-key", cacheValue, store.WithExpiration(5*time.Second))
-
- // Then
- assert.Equal(t, expectedErr, err)
-
- assert.Equal(t, 0, codec.GetStats().Hits)
- assert.Equal(t, 0, codec.GetStats().Miss)
- assert.Equal(t, 0, codec.GetStats().SetSuccess)
- assert.Equal(t, 1, codec.GetStats().SetError)
- assert.Equal(t, 0, codec.GetStats().DeleteSuccess)
- assert.Equal(t, 0, codec.GetStats().DeleteError)
- assert.Equal(t, 0, codec.GetStats().InvalidateSuccess)
- assert.Equal(t, 0, codec.GetStats().InvalidateError)
- assert.Equal(t, 0, codec.GetStats().ClearSuccess)
- assert.Equal(t, 0, codec.GetStats().ClearError)
-}
-
-func TestDeleteWhenSuccess(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().Delete(ctx, "my-key").Return(nil)
-
- codec := New(store)
-
- // When
- err := codec.Delete(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
-
- assert.Equal(t, 0, codec.GetStats().Hits)
- assert.Equal(t, 0, codec.GetStats().Miss)
- assert.Equal(t, 0, codec.GetStats().SetSuccess)
- assert.Equal(t, 0, codec.GetStats().SetError)
- assert.Equal(t, 1, codec.GetStats().DeleteSuccess)
- assert.Equal(t, 0, codec.GetStats().DeleteError)
- assert.Equal(t, 0, codec.GetStats().InvalidateSuccess)
- assert.Equal(t, 0, codec.GetStats().InvalidateError)
- assert.Equal(t, 0, codec.GetStats().ClearSuccess)
- assert.Equal(t, 0, codec.GetStats().ClearError)
-}
-
-func TesDeleteWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unable to delete key")
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().Delete(ctx, "my-key").Return(expectedErr)
-
- codec := New(store)
-
- // When
- err := codec.Delete(ctx, "my-key")
-
- // Then
- assert.Equal(t, expectedErr, err)
-
- assert.Equal(t, 0, codec.GetStats().Hits)
- assert.Equal(t, 0, codec.GetStats().Miss)
- assert.Equal(t, 0, codec.GetStats().SetSuccess)
- assert.Equal(t, 0, codec.GetStats().SetError)
- assert.Equal(t, 0, codec.GetStats().DeleteSuccess)
- assert.Equal(t, 1, codec.GetStats().DeleteError)
- assert.Equal(t, 0, codec.GetStats().InvalidateSuccess)
- assert.Equal(t, 0, codec.GetStats().InvalidateError)
- assert.Equal(t, 0, codec.GetStats().ClearSuccess)
- assert.Equal(t, 0, codec.GetStats().ClearError)
-}
-
-func TestInvalidateWhenSuccess(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- mockedStore := store.NewMockStoreInterface(ctrl)
- mockedStore.EXPECT().Invalidate(ctx, store.InvalidateOptionsMatcher{
- Tags: []string{"tag1"},
- }).Return(nil)
-
- codec := New(mockedStore)
-
- // When
- err := codec.Invalidate(ctx, store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-
- assert.Equal(t, 0, codec.GetStats().Hits)
- assert.Equal(t, 0, codec.GetStats().Miss)
- assert.Equal(t, 0, codec.GetStats().SetSuccess)
- assert.Equal(t, 0, codec.GetStats().SetError)
- assert.Equal(t, 0, codec.GetStats().DeleteSuccess)
- assert.Equal(t, 0, codec.GetStats().DeleteError)
- assert.Equal(t, 1, codec.GetStats().InvalidateSuccess)
- assert.Equal(t, 0, codec.GetStats().InvalidateError)
- assert.Equal(t, 0, codec.GetStats().ClearSuccess)
- assert.Equal(t, 0, codec.GetStats().ClearError)
-}
-
-func TestInvalidateWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unexpected error when invalidating data")
-
- mockedStore := store.NewMockStoreInterface(ctrl)
- mockedStore.EXPECT().Invalidate(ctx, store.InvalidateOptionsMatcher{
- Tags: []string{"tag1"},
- }).Return(expectedErr)
-
- codec := New(mockedStore)
-
- // When
- err := codec.Invalidate(ctx, store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Equal(t, expectedErr, err)
-
- assert.Equal(t, 0, codec.GetStats().Hits)
- assert.Equal(t, 0, codec.GetStats().Miss)
- assert.Equal(t, 0, codec.GetStats().SetSuccess)
- assert.Equal(t, 0, codec.GetStats().SetError)
- assert.Equal(t, 0, codec.GetStats().DeleteSuccess)
- assert.Equal(t, 0, codec.GetStats().DeleteError)
- assert.Equal(t, 0, codec.GetStats().InvalidateSuccess)
- assert.Equal(t, 1, codec.GetStats().InvalidateError)
- assert.Equal(t, 0, codec.GetStats().ClearSuccess)
- assert.Equal(t, 0, codec.GetStats().ClearError)
-}
-
-func TestClearWhenSuccess(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().Clear(ctx).Return(nil)
-
- codec := New(store)
-
- // When
- err := codec.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-
- assert.Equal(t, 0, codec.GetStats().Hits)
- assert.Equal(t, 0, codec.GetStats().Miss)
- assert.Equal(t, 0, codec.GetStats().SetSuccess)
- assert.Equal(t, 0, codec.GetStats().SetError)
- assert.Equal(t, 0, codec.GetStats().DeleteSuccess)
- assert.Equal(t, 0, codec.GetStats().DeleteError)
- assert.Equal(t, 0, codec.GetStats().InvalidateSuccess)
- assert.Equal(t, 0, codec.GetStats().InvalidateError)
- assert.Equal(t, 1, codec.GetStats().ClearSuccess)
- assert.Equal(t, 0, codec.GetStats().ClearError)
-}
-
-func TestClearWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unexpected error when clearing cache")
-
- store := store.NewMockStoreInterface(ctrl)
- store.EXPECT().Clear(ctx).Return(expectedErr)
-
- codec := New(store)
-
- // When
- err := codec.Clear(ctx)
-
- // Then
- assert.Equal(t, expectedErr, err)
-
- assert.Equal(t, 0, codec.GetStats().Hits)
- assert.Equal(t, 0, codec.GetStats().Miss)
- assert.Equal(t, 0, codec.GetStats().SetSuccess)
- assert.Equal(t, 0, codec.GetStats().SetError)
- assert.Equal(t, 0, codec.GetStats().DeleteSuccess)
- assert.Equal(t, 0, codec.GetStats().DeleteError)
- assert.Equal(t, 0, codec.GetStats().InvalidateSuccess)
- assert.Equal(t, 0, codec.GetStats().InvalidateError)
- assert.Equal(t, 0, codec.GetStats().ClearSuccess)
- assert.Equal(t, 1, codec.GetStats().ClearError)
-}
-
-func TestGetStore(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- store := store.NewMockStoreInterface(ctrl)
-
- codec := New(store)
-
- // When - Then
- assert.Equal(t, store, codec.GetStore())
-}
-
-func TestGetStats(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- store := store.NewMockStoreInterface(ctrl)
-
- codec := New(store)
-
- // When - Then
- expectedStats := &Stats{}
- assert.Equal(t, expectedStats, codec.GetStats())
-}
diff --git a/toolkit/gocache/lib/codec/interface.go b/toolkit/gocache/lib/codec/interface.go
deleted file mode 100644
index 284c5d13..00000000
--- a/toolkit/gocache/lib/codec/interface.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package codec
-
-import (
- "context"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-// CodecInterface represents an instance of a cache codec
-type CodecInterface interface {
- Get(ctx context.Context, key any) (any, error)
- GetWithTTL(ctx context.Context, key any) (any, time.Duration, error)
- Set(ctx context.Context, key any, value any, options ...store.Option) error
- Delete(ctx context.Context, key any) error
- Invalidate(ctx context.Context, options ...store.InvalidateOption) error
- Clear(ctx context.Context) error
-
- GetStore() store.StoreInterface
- GetStats() *Stats
-}
diff --git a/toolkit/gocache/lib/marshaler/marshaler.go b/toolkit/gocache/lib/marshaler/marshaler.go
deleted file mode 100644
index f39b76fa..00000000
--- a/toolkit/gocache/lib/marshaler/marshaler.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package marshaler
-
-import (
- "context"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/cache"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "github.com/vmihailenco/msgpack/v5"
-)
-
-// Marshaler is the struct that marshal and unmarshal cache values
-type Marshaler struct {
- cache cache.CacheInterface[any]
-}
-
-// New creates a new marshaler that marshals/unmarshals cache values
-func New(cache cache.CacheInterface[any]) *Marshaler {
- return &Marshaler{
- cache: cache,
- }
-}
-
-// Get obtains a value from cache and unmarshal value with given object
-func (c *Marshaler) Get(ctx context.Context, key any, returnObj any) (any, error) {
- result, err := c.cache.Get(ctx, key)
- if err != nil {
- return nil, err
- }
-
- switch v := result.(type) {
- case []byte:
- err = msgpack.Unmarshal(v, returnObj)
- case string:
- err = msgpack.Unmarshal([]byte(v), returnObj)
- }
-
- if err != nil {
- return nil, err
- }
-
- return returnObj, nil
-}
-
-// Set sets a value in cache by marshaling value
-func (c *Marshaler) Set(ctx context.Context, key, object any, options ...store.Option) error {
- bytes, err := msgpack.Marshal(object)
- if err != nil {
- return err
- }
-
- return c.cache.Set(ctx, key, bytes, options...)
-}
-
-// Delete removes a value from the cache
-func (c *Marshaler) Delete(ctx context.Context, key any) error {
- return c.cache.Delete(ctx, key)
-}
-
-// Invalidate invalidate cache values using given options
-func (c *Marshaler) Invalidate(ctx context.Context, options ...store.InvalidateOption) error {
- return c.cache.Invalidate(ctx, options...)
-}
-
-// Clear reset all cache data
-func (c *Marshaler) Clear(ctx context.Context) error {
- return c.cache.Clear(ctx)
-}
diff --git a/toolkit/gocache/lib/marshaler/marshaler_test.go b/toolkit/gocache/lib/marshaler/marshaler_test.go
deleted file mode 100644
index 9f239a4d..00000000
--- a/toolkit/gocache/lib/marshaler/marshaler_test.go
+++ /dev/null
@@ -1,327 +0,0 @@
-package marshaler
-
-import (
- "context"
- "errors"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/cache"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "github.com/vmihailenco/msgpack/v5"
- "go.uber.org/mock/gomock"
-)
-
-type testCacheValue struct {
- Hello string
-}
-
-func TestNew(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- cache := cache.NewMockCacheInterface[any](ctrl)
-
- // When
- marshaler := New(cache)
-
- // Then
- assert.IsType(t, new(Marshaler), marshaler)
- assert.Equal(t, cache, marshaler.cache)
-}
-
-func TestGetWhenStoreReturnsSliceOfBytes(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &testCacheValue{
- Hello: "world",
- }
-
- cacheValueBytes, err := msgpack.Marshal(cacheValue)
- if err != nil {
- assert.Error(t, err)
- }
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Get(ctx, "my-key").Return(cacheValueBytes, nil)
-
- marshaler := New(cache)
-
- // When
- value, err := marshaler.Get(ctx, "my-key", new(testCacheValue))
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-}
-
-func TestGetWhenStoreReturnsString(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &testCacheValue{
- Hello: "world",
- }
-
- cacheValueBytes, err := msgpack.Marshal(cacheValue)
- if err != nil {
- assert.Error(t, err)
- }
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Get(ctx, "my-key").Return(string(cacheValueBytes), nil)
-
- marshaler := New(cache)
-
- // When
- value, err := marshaler.Get(ctx, "my-key", new(testCacheValue))
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-}
-
-func TestGetWhenUnmarshalingError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Get(ctx, "my-key").Return("unknown-string", nil)
-
- marshaler := New(cache)
-
- // When
- value, err := marshaler.Get(ctx, "my-key", new(testCacheValue))
-
- // Then
- assert.NotNil(t, err)
- assert.Nil(t, value)
-}
-
-func TestGetWhenNotFoundInStore(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unable to find item in store")
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Get(ctx, "my-key").Return(nil, expectedErr)
-
- marshaler := New(cache)
-
- // When
- value, err := marshaler.Get(ctx, "my-key", new(testCacheValue))
-
- // Then
- assert.Equal(t, expectedErr, err)
- assert.Nil(t, value)
-}
-
-func TestSetWhenStruct(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := &testCacheValue{
- Hello: "world",
- }
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Set(
- ctx,
- "my-key",
- []byte{0x81, 0xa5, 0x48, 0x65, 0x6c, 0x6c, 0x6f, 0xa5, 0x77, 0x6f, 0x72, 0x6c, 0x64},
- store.OptionsMatcher{
- Expiration: 5 * time.Second,
- },
- ).Return(nil)
-
- marshaler := New(cache)
-
- // When
- err := marshaler.Set(ctx, "my-key", cacheValue, store.WithExpiration(5*time.Second))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestSetWhenString(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := "test"
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Set(
- ctx,
- "my-key",
- []byte{0xa4, 0x74, 0x65, 0x73, 0x74},
- store.OptionsMatcher{
- Expiration: 5 * time.Second,
- },
- ).Return(nil)
-
- marshaler := New(cache)
-
- // When
- err := marshaler.Set(ctx, "my-key", cacheValue, store.WithExpiration(5*time.Second))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestSetWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheValue := "test"
-
- expectedErr := errors.New("an unexpected error occurred")
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Set(
- ctx,
- "my-key",
- []byte{0xa4, 0x74, 0x65, 0x73, 0x74},
- store.OptionsMatcher{Expiration: 5 * time.Second},
- ).Return(expectedErr)
-
- marshaler := New(cache)
-
- // When
- err := marshaler.Set(ctx, "my-key", cacheValue, store.WithExpiration(5*time.Second))
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Delete(ctx, "my-key").Return(nil)
-
- marshaler := New(cache)
-
- // When
- err := marshaler.Delete(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestDeleteWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unable to delete key")
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Delete(ctx, "my-key").Return(expectedErr)
-
- marshaler := New(cache)
-
- // When
- err := marshaler.Delete(ctx, "my-key")
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Invalidate(ctx, store.InvalidateOptionsMatcher{
- Tags: []string{"tag1"},
- }).Return(nil)
-
- marshaler := New(cache)
-
- // When
- err := marshaler.Invalidate(ctx, store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestInvalidatingWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unexpected error when invalidating data")
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Invalidate(ctx, store.InvalidateOptionsMatcher{Tags: []string{"tag1"}}).Return(expectedErr)
-
- marshaler := New(cache)
-
- // When
- err := marshaler.Invalidate(ctx, store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Clear(ctx).Return(nil)
-
- marshaler := New(cache)
-
- // When
- err := marshaler.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestClearWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("an unexpected error occurred")
-
- cache := cache.NewMockCacheInterface[any](ctrl)
- cache.EXPECT().Clear(ctx).Return(expectedErr)
-
- marshaler := New(cache)
-
- // When
- err := marshaler.Clear(ctx)
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
diff --git a/toolkit/gocache/lib/metrics/interface.go b/toolkit/gocache/lib/metrics/interface.go
deleted file mode 100644
index cf584803..00000000
--- a/toolkit/gocache/lib/metrics/interface.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package metrics
-
-import "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/codec"
-
-// MetricsInterface represents the metrics interface for all available providers
-type MetricsInterface interface {
- RecordFromCodec(codec codec.CodecInterface)
-}
diff --git a/toolkit/gocache/lib/metrics/metrics_mock.go b/toolkit/gocache/lib/metrics/metrics_mock.go
deleted file mode 100644
index f95ffbfd..00000000
--- a/toolkit/gocache/lib/metrics/metrics_mock.go
+++ /dev/null
@@ -1,47 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: lib/metrics/interface.go
-
-// Package metrics is a generated GoMock package.
-package metrics
-
-import (
- reflect "reflect"
-
- codec "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/codec"
- gomock "go.uber.org/mock/gomock"
-)
-
-// MockMetricsInterface is a mock of MetricsInterface interface.
-type MockMetricsInterface struct {
- ctrl *gomock.Controller
- recorder *MockMetricsInterfaceMockRecorder
-}
-
-// MockMetricsInterfaceMockRecorder is the mock recorder for MockMetricsInterface.
-type MockMetricsInterfaceMockRecorder struct {
- mock *MockMetricsInterface
-}
-
-// NewMockMetricsInterface creates a new mock instance.
-func NewMockMetricsInterface(ctrl *gomock.Controller) *MockMetricsInterface {
- mock := &MockMetricsInterface{ctrl: ctrl}
- mock.recorder = &MockMetricsInterfaceMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockMetricsInterface) EXPECT() *MockMetricsInterfaceMockRecorder {
- return m.recorder
-}
-
-// RecordFromCodec mocks base method.
-func (m *MockMetricsInterface) RecordFromCodec(codec codec.CodecInterface) {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "RecordFromCodec", codec)
-}
-
-// RecordFromCodec indicates an expected call of RecordFromCodec.
-func (mr *MockMetricsInterfaceMockRecorder) RecordFromCodec(codec interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RecordFromCodec", reflect.TypeOf((*MockMetricsInterface)(nil).RecordFromCodec), codec)
-}
diff --git a/toolkit/gocache/lib/metrics/prometheus.go b/toolkit/gocache/lib/metrics/prometheus.go
deleted file mode 100644
index 6d62c42b..00000000
--- a/toolkit/gocache/lib/metrics/prometheus.go
+++ /dev/null
@@ -1,102 +0,0 @@
-package metrics
-
-import (
- "github.com/prometheus/client_golang/prometheus"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/codec"
-)
-
-const (
- defaultNamespace = "cache"
-)
-
-// Prometheus represents the prometheus struct for collecting metrics
-type Prometheus struct {
- service string
- namespace string
- collector *prometheus.GaugeVec
- registerer prometheus.Registerer
- codecChannel chan codec.CodecInterface
-}
-
-// PrometheusOption is a type for defining Prometheus options
-type PrometheusOption func(*Prometheus)
-
-// WithCodecChannel sets the prometheus codec channel
-func WithCodecChannel(codecChannel chan codec.CodecInterface) PrometheusOption {
- return func(m *Prometheus) {
- m.codecChannel = codecChannel
- }
-}
-
-// WithNamespace sets the prometheus namespace
-func WithNamespace(namespace string) PrometheusOption {
- return func(m *Prometheus) {
- m.namespace = namespace
- }
-}
-
-// WithRegisterer sets the prometheus registerer
-func WithRegisterer(registerer prometheus.Registerer) PrometheusOption {
- return func(m *Prometheus) {
- m.registerer = registerer
- }
-}
-
-// NewPrometheus initializes a new prometheus metric instance
-func NewPrometheus(service string, options ...PrometheusOption) *Prometheus {
- instance := &Prometheus{
- namespace: defaultNamespace,
- registerer: prometheus.DefaultRegisterer,
- service: service,
- codecChannel: make(chan codec.CodecInterface, 10000),
- }
-
- for _, option := range options {
- option(instance)
- }
-
- instance.collector = prometheus.NewGaugeVec(
- prometheus.GaugeOpts{
- Name: "collector",
- Namespace: instance.namespace,
- Help: "This represent the number of items in cache",
- },
- []string{"service", "store", "metric"},
- )
-
- instance.registerer.MustRegister(instance.collector)
-
- go instance.recorder()
-
- return instance
-}
-
-// Record records a metric in prometheus by specifying the store name, metric name and value
-func (m *Prometheus) record(store, metric string, value float64) {
- m.collector.WithLabelValues(m.service, store, metric).Set(value)
-}
-
-// Recorder records metrics in prometheus by retrieving values from the codec channel
-func (m *Prometheus) recorder() {
- for codec := range m.codecChannel {
- stats := codec.GetStats()
- storeType := codec.GetStore().GetType()
-
- m.record(storeType, "hit_count", float64(stats.Hits))
- m.record(storeType, "miss_count", float64(stats.Miss))
-
- m.record(storeType, "set_success", float64(stats.SetSuccess))
- m.record(storeType, "set_error", float64(stats.SetError))
-
- m.record(storeType, "delete_success", float64(stats.DeleteSuccess))
- m.record(storeType, "delete_error", float64(stats.DeleteError))
-
- m.record(storeType, "invalidate_success", float64(stats.InvalidateSuccess))
- m.record(storeType, "invalidate_error", float64(stats.InvalidateError))
- }
-}
-
-// RecordFromCodec sends the given codec into the codec channel to be read from recorder
-func (m *Prometheus) RecordFromCodec(codec codec.CodecInterface) {
- m.codecChannel <- codec
-}
diff --git a/toolkit/gocache/lib/metrics/prometheus_test.go b/toolkit/gocache/lib/metrics/prometheus_test.go
deleted file mode 100644
index 7cf8678f..00000000
--- a/toolkit/gocache/lib/metrics/prometheus_test.go
+++ /dev/null
@@ -1,166 +0,0 @@
-package metrics
-
-import (
- "testing"
- "time"
-
- "github.com/prometheus/client_golang/prometheus"
- "github.com/prometheus/client_golang/prometheus/testutil"
- "github.com/stretchr/testify/assert"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/codec"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "go.uber.org/mock/gomock"
-)
-
-func TestNewPrometheus(t *testing.T) {
- // Given
- serviceName := "my-test-service-name"
-
- // When
- metrics := NewPrometheus(serviceName)
-
- // Then
- assert.IsType(t, new(Prometheus), metrics)
-
- assert.Equal(t, serviceName, metrics.service)
- assert.Equal(t, defaultNamespace, metrics.namespace)
- assert.Equal(t, prometheus.DefaultRegisterer, metrics.registerer)
-
- assert.IsType(t, new(prometheus.GaugeVec), metrics.collector)
-}
-
-func TestNewPrometheus_WithOptions(t *testing.T) {
- // Given
- serviceName := "my-test-service-name"
-
- customNamespace := "my_custom_namespace"
- customRegistry := prometheus.NewRegistry()
- customChannel := make(chan codec.CodecInterface, 100)
-
- // When
- metrics := NewPrometheus(
- serviceName,
- WithCodecChannel(customChannel),
- WithNamespace(customNamespace),
- WithRegisterer(customRegistry),
- )
-
- // Then
- assert.IsType(t, new(Prometheus), metrics)
-
- assert.Equal(t, serviceName, metrics.service)
- assert.Equal(t, customChannel, metrics.codecChannel)
- assert.Equal(t, customNamespace, metrics.namespace)
- assert.Equal(t, customRegistry, metrics.registerer)
-
- assert.IsType(t, new(prometheus.GaugeVec), metrics.collector)
-}
-
-func TestRecord(t *testing.T) {
- // Given
- customRegistry := prometheus.NewRegistry()
-
- metrics := NewPrometheus(
- "my-test-service-name",
- WithRegisterer(customRegistry),
- )
-
- // When
- metrics.record("redis", "hit_count", 6)
-
- // Then
- metric, err := metrics.collector.GetMetricWithLabelValues("my-test-service-name", "redis", "hit_count")
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- v := testutil.ToFloat64(metric)
-
- assert.Equal(t, float64(6), v)
-}
-
-func TestRecordFromCodec(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- redisStore := store.NewMockStoreInterface(ctrl)
- redisStore.EXPECT().GetType().Return("redis")
-
- stats := &codec.Stats{
- Hits: 4,
- Miss: 6,
- SetSuccess: 12,
- SetError: 3,
- DeleteSuccess: 8,
- DeleteError: 5,
- InvalidateSuccess: 2,
- InvalidateError: 1,
- }
-
- testCodec := codec.NewMockCodecInterface(ctrl)
- testCodec.EXPECT().GetStats().Return(stats)
- testCodec.EXPECT().GetStore().Return(redisStore)
-
- customRegistry := prometheus.NewRegistry()
-
- metrics := NewPrometheus(
- "my-test-service-name",
- WithRegisterer(customRegistry),
- )
-
- // When
- metrics.RecordFromCodec(testCodec)
-
- // Wait for data to be processed
- for len(metrics.codecChannel) > 0 {
- time.Sleep(1 * time.Millisecond)
- }
-
- // Then
- testCases := []struct {
- metricName string
- expected float64
- }{
- {
- metricName: "hit_count",
- expected: float64(stats.Hits),
- },
- {
- metricName: "miss_count",
- expected: float64(stats.Miss),
- },
- {
- metricName: "set_success",
- expected: float64(stats.SetSuccess),
- },
- {
- metricName: "set_error",
- expected: float64(stats.SetError),
- },
- {
- metricName: "delete_success",
- expected: float64(stats.DeleteSuccess),
- },
- {
- metricName: "delete_error",
- expected: float64(stats.DeleteError),
- },
- {
- metricName: "invalidate_success",
- expected: float64(stats.InvalidateSuccess),
- },
- {
- metricName: "invalidate_error",
- expected: float64(stats.InvalidateError),
- },
- }
-
- for _, tc := range testCases {
- metric, err := metrics.collector.GetMetricWithLabelValues("my-test-service-name", "redis", tc.metricName)
- if err != nil {
- t.Fatalf("unexpected error: %v", err)
- }
- v := testutil.ToFloat64(metric)
-
- assert.Equal(t, tc.expected, v)
- }
-}
diff --git a/toolkit/gocache/lib/misc/benchmarks.jpeg b/toolkit/gocache/lib/misc/benchmarks.jpeg
deleted file mode 100644
index 8d9c5823..00000000
Binary files a/toolkit/gocache/lib/misc/benchmarks.jpeg and /dev/null differ
diff --git a/toolkit/gocache/lib/store/errors.go b/toolkit/gocache/lib/store/errors.go
deleted file mode 100644
index 6c62407d..00000000
--- a/toolkit/gocache/lib/store/errors.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package store
-
-const NOT_FOUND_ERR string = "value not found in store"
-
-type NotFound struct {
- cause error
-}
-
-func NotFoundWithCause(e error) error {
- err := NotFound{
- cause: e,
- }
- return &err
-}
-
-func (e NotFound) Cause() error {
- return e.cause
-}
-
-func (e NotFound) Is(err error) bool {
- return err.Error() == NOT_FOUND_ERR
-}
-
-func (e NotFound) Error() string {
- return NOT_FOUND_ERR
-}
-func (e NotFound) Unwrap() error { return e.cause }
diff --git a/toolkit/gocache/lib/store/errors_test.go b/toolkit/gocache/lib/store/errors_test.go
deleted file mode 100644
index 9727acb5..00000000
--- a/toolkit/gocache/lib/store/errors_test.go
+++ /dev/null
@@ -1,22 +0,0 @@
-package store
-
-import (
- "errors"
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestNotFoundIs(t *testing.T) {
- expectedErr := errors.New(("this is an expected error cause"))
- err := NotFoundWithCause(nil)
- assert.True(t, errors.Is(err, NotFound{cause: expectedErr}))
-
- err2 := &NotFound{}
- assert.True(t, errors.Is(err2, &NotFound{}))
-
- _, ok := err.(*NotFound)
- assert.True(t, ok)
-
- assert.True(t, err.Error() == NotFound{}.Error())
-}
diff --git a/toolkit/gocache/lib/store/interface.go b/toolkit/gocache/lib/store/interface.go
deleted file mode 100644
index 66eb37cf..00000000
--- a/toolkit/gocache/lib/store/interface.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package store
-
-import (
- "context"
- "time"
-)
-
-// StoreInterface is the interface for all available stores
-type StoreInterface interface {
- Get(ctx context.Context, key any) (any, error)
- GetWithTTL(ctx context.Context, key any) (any, time.Duration, error)
- Set(ctx context.Context, key any, value any, options ...Option) error
- Delete(ctx context.Context, key any) error
- Invalidate(ctx context.Context, options ...InvalidateOption) error
- Clear(ctx context.Context) error
- GetType() string
-}
diff --git a/toolkit/gocache/lib/store/invalidate_options.go b/toolkit/gocache/lib/store/invalidate_options.go
deleted file mode 100644
index aa3ed66e..00000000
--- a/toolkit/gocache/lib/store/invalidate_options.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package store
-
-// InvalidateOption represents a cache invalidation function.
-type InvalidateOption func(o *InvalidateOptions)
-
-type InvalidateOptions struct {
- Tags []string
-}
-
-func (o *InvalidateOptions) isEmpty() bool {
- return len(o.Tags) == 0
-}
-
-func ApplyInvalidateOptionsWithDefault(defaultOptions *InvalidateOptions, opts ...InvalidateOption) *InvalidateOptions {
- returnedOptions := ApplyInvalidateOptions(opts...)
-
- if returnedOptions == new(InvalidateOptions) {
- returnedOptions = defaultOptions
- }
-
- return returnedOptions
-}
-
-func ApplyInvalidateOptions(opts ...InvalidateOption) *InvalidateOptions {
- o := &InvalidateOptions{}
-
- for _, opt := range opts {
- opt(o)
- }
-
- return o
-}
-
-// WithInvalidateTags allows setting the invalidate tags.
-func WithInvalidateTags(tags []string) InvalidateOption {
- return func(o *InvalidateOptions) {
- o.Tags = tags
- }
-}
diff --git a/toolkit/gocache/lib/store/invalidate_options_test.go b/toolkit/gocache/lib/store/invalidate_options_test.go
deleted file mode 100644
index 4f95ebb9..00000000
--- a/toolkit/gocache/lib/store/invalidate_options_test.go
+++ /dev/null
@@ -1,17 +0,0 @@
-package store
-
-import (
- "testing"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestInvalidateOptionsTagsValue(t *testing.T) {
- // Given
- options := InvalidateOptions{
- Tags: []string{"tag1", "tag2", "tag3"},
- }
-
- // When - Then
- assert.Equal(t, []string{"tag1", "tag2", "tag3"}, options.Tags)
-}
diff --git a/toolkit/gocache/lib/store/options.go b/toolkit/gocache/lib/store/options.go
deleted file mode 100644
index 5e58a7d0..00000000
--- a/toolkit/gocache/lib/store/options.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package store
-
-import (
- "time"
-)
-
-// Option represents a store option function.
-type Option func(o *Options)
-
-type Options struct {
- SynchronousSet bool
- Cost int64
- Expiration time.Duration
- Tags []string
- ClientSideCacheExpiration time.Duration
-}
-
-func (o *Options) IsEmpty() bool {
- return o.Cost == 0 && o.Expiration == 0 && len(o.Tags) == 0
-}
-
-func ApplyOptionsWithDefault(defaultOptions *Options, opts ...Option) *Options {
- returnedOptions := &Options{}
- *returnedOptions = *defaultOptions
-
- for _, opt := range opts {
- opt(returnedOptions)
- }
-
- return returnedOptions
-}
-
-func ApplyOptions(opts ...Option) *Options {
- o := &Options{}
-
- for _, opt := range opts {
- opt(o)
- }
-
- return o
-}
-
-// WithCost allows setting the memory capacity used by the item when setting a value.
-// Actually it seems to be used by Ristretto library only.
-func WithCost(cost int64) Option {
- return func(o *Options) {
- o.Cost = cost
- }
-}
-
-// WithSynchronousSet allows setting the behavior when setting a value, whether to wait until all buffered writes have been applied or not.
-// Currently to be used by Ristretto library only.
-func WithSynchronousSet() Option {
- return func(o *Options) {
- o.SynchronousSet = true
- }
-}
-
-// WithExpiration allows to specify an expiration time when setting a value.
-func WithExpiration(expiration time.Duration) Option {
- return func(o *Options) {
- o.Expiration = expiration
- }
-}
-
-// WithTags allows to specify associated tags to the current value.
-func WithTags(tags []string) Option {
- return func(o *Options) {
- o.Tags = tags
- }
-}
-
-// WithClientSideCaching allows setting the client side caching, enabled by default
-// Currently to be used by Rueidis(redis) library only.
-func WithClientSideCaching(clientSideCacheExpiration time.Duration) Option {
- return func(o *Options) {
- o.ClientSideCacheExpiration = clientSideCacheExpiration
- }
-}
diff --git a/toolkit/gocache/lib/store/options_test.go b/toolkit/gocache/lib/store/options_test.go
deleted file mode 100644
index bb132b73..00000000
--- a/toolkit/gocache/lib/store/options_test.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package store
-
-import (
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
-)
-
-func TestOptionsCostValue(t *testing.T) {
- // Given
- options := &Options{
- Cost: 7,
- }
-
- // When - Then
- assert.Equal(t, int64(7), options.Cost)
-}
-
-func TestOptionsExpirationValue(t *testing.T) {
- // Given
- options := &Options{
- Expiration: 25 * time.Second,
- }
-
- // When - Then
- assert.Equal(t, 25*time.Second, options.Expiration)
-}
-
-func TestOptionsTagsValue(t *testing.T) {
- // Given
- options := &Options{
- Tags: []string{"tag1", "tag2", "tag3"},
- }
-
- // When - Then
- assert.Equal(t, []string{"tag1", "tag2", "tag3"}, options.Tags)
-}
-
-func Test_applyOptionsWithDefault(t *testing.T) {
- // Given
- defaultOptions := &Options{
- Expiration: 25 * time.Second,
- }
-
- // When
- options := ApplyOptionsWithDefault(defaultOptions, WithCost(7))
-
- // Then
- assert.Equal(t, int64(7), options.Cost)
- assert.Equal(t, 25*time.Second, options.Expiration)
-}
diff --git a/toolkit/gocache/lib/store/options_test_matchers.go b/toolkit/gocache/lib/store/options_test_matchers.go
deleted file mode 100644
index b4029a85..00000000
--- a/toolkit/gocache/lib/store/options_test_matchers.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package store
-
-import (
- "fmt"
- "time"
-
- "golang.org/x/exp/slices"
-)
-
-type OptionsMatcher struct {
- Cost int64
- Expiration time.Duration
- Tags []string
-}
-
-func (m OptionsMatcher) Matches(x interface{}) bool {
- switch values := x.(type) {
- case []Option:
- opts := &Options{}
- for _, value := range values {
- value(opts)
- }
-
- return opts.Cost == m.Cost &&
- opts.Expiration == m.Expiration &&
- slices.Equal(opts.Tags, m.Tags)
- }
-
- return false
-}
-
-func (m OptionsMatcher) String() string {
- return fmt.Sprintf(
- "options should match (cost: %v expiration: %v tags: %v)",
- m.Cost,
- m.Expiration,
- m.Tags,
- )
-}
-
-type InvalidateOptionsMatcher struct {
- Tags []string
-}
-
-func (m InvalidateOptionsMatcher) Matches(x interface{}) bool {
- switch values := x.(type) {
- case []InvalidateOption:
- opts := &InvalidateOptions{}
- for _, value := range values {
- value(opts)
- }
-
- return slices.Equal(opts.Tags, m.Tags)
- }
-
- return false
-}
-
-func (m InvalidateOptionsMatcher) String() string {
- return fmt.Sprintf(
- "invalidate options should match (tags: %v)",
- m.Tags,
- )
-}
diff --git a/toolkit/gocache/lib/store/store_mock.go b/toolkit/gocache/lib/store/store_mock.go
deleted file mode 100644
index d5178f17..00000000
--- a/toolkit/gocache/lib/store/store_mock.go
+++ /dev/null
@@ -1,147 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: lib/store/interface.go
-
-// Package store is a generated GoMock package.
-package store
-
-import (
- context "context"
- reflect "reflect"
- time "time"
-
- gomock "go.uber.org/mock/gomock"
-)
-
-// MockStoreInterface is a mock of StoreInterface interface.
-type MockStoreInterface struct {
- ctrl *gomock.Controller
- recorder *MockStoreInterfaceMockRecorder
-}
-
-// MockStoreInterfaceMockRecorder is the mock recorder for MockStoreInterface.
-type MockStoreInterfaceMockRecorder struct {
- mock *MockStoreInterface
-}
-
-// NewMockStoreInterface creates a new mock instance.
-func NewMockStoreInterface(ctrl *gomock.Controller) *MockStoreInterface {
- mock := &MockStoreInterface{ctrl: ctrl}
- mock.recorder = &MockStoreInterfaceMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockStoreInterface) EXPECT() *MockStoreInterfaceMockRecorder {
- return m.recorder
-}
-
-// Clear mocks base method.
-func (m *MockStoreInterface) Clear(ctx context.Context) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Clear", ctx)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Clear indicates an expected call of Clear.
-func (mr *MockStoreInterfaceMockRecorder) Clear(ctx interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockStoreInterface)(nil).Clear), ctx)
-}
-
-// Delete mocks base method.
-func (m *MockStoreInterface) Delete(ctx context.Context, key any) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Delete", ctx, key)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Delete indicates an expected call of Delete.
-func (mr *MockStoreInterfaceMockRecorder) Delete(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockStoreInterface)(nil).Delete), ctx, key)
-}
-
-// Get mocks base method.
-func (m *MockStoreInterface) Get(ctx context.Context, key any) (any, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", ctx, key)
- ret0, _ := ret[0].(any)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockStoreInterfaceMockRecorder) Get(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockStoreInterface)(nil).Get), ctx, key)
-}
-
-// GetType mocks base method.
-func (m *MockStoreInterface) GetType() string {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetType")
- ret0, _ := ret[0].(string)
- return ret0
-}
-
-// GetType indicates an expected call of GetType.
-func (mr *MockStoreInterfaceMockRecorder) GetType() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetType", reflect.TypeOf((*MockStoreInterface)(nil).GetType))
-}
-
-// GetWithTTL mocks base method.
-func (m *MockStoreInterface) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetWithTTL", ctx, key)
- ret0, _ := ret[0].(any)
- ret1, _ := ret[1].(time.Duration)
- ret2, _ := ret[2].(error)
- return ret0, ret1, ret2
-}
-
-// GetWithTTL indicates an expected call of GetWithTTL.
-func (mr *MockStoreInterfaceMockRecorder) GetWithTTL(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWithTTL", reflect.TypeOf((*MockStoreInterface)(nil).GetWithTTL), ctx, key)
-}
-
-// Invalidate mocks base method.
-func (m *MockStoreInterface) Invalidate(ctx context.Context, options ...InvalidateOption) error {
- m.ctrl.T.Helper()
- varargs := []interface{}{ctx}
- for _, a := range options {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "Invalidate", varargs...)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Invalidate indicates an expected call of Invalidate.
-func (mr *MockStoreInterfaceMockRecorder) Invalidate(ctx interface{}, options ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{ctx}, options...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invalidate", reflect.TypeOf((*MockStoreInterface)(nil).Invalidate), varargs...)
-}
-
-// Set mocks base method.
-func (m *MockStoreInterface) Set(ctx context.Context, key, value any, options ...Option) error {
- m.ctrl.T.Helper()
- varargs := []interface{}{ctx, key, value}
- for _, a := range options {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "Set", varargs...)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Set indicates an expected call of Set.
-func (mr *MockStoreInterfaceMockRecorder) Set(ctx, key, value interface{}, options ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{ctx, key, value}, options...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockStoreInterface)(nil).Set), varargs...)
-}
diff --git a/toolkit/gocache/store/bigcache/bigcache.go b/toolkit/gocache/store/bigcache/bigcache.go
deleted file mode 100644
index 4df44ebe..00000000
--- a/toolkit/gocache/store/bigcache/bigcache.go
+++ /dev/null
@@ -1,152 +0,0 @@
-package bigcache
-
-import (
- "context"
- "errors"
- "fmt"
- "strings"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-// BigcacheClientInterface represents a allegro/bigcache client
-type BigcacheClientInterface interface {
- Get(key string) ([]byte, error)
- Set(key string, entry []byte) error
- Delete(key string) error
- Reset() error
-}
-
-const (
- // BigcacheType represents the storage type as a string value
- BigcacheType = "bigcache"
- // BigcacheTagPattern represents the tag pattern to be used as a key in specified storage
- BigcacheTagPattern = "gocache_tag_%s"
-)
-
-// BigcacheStore is a store for Bigcache
-type BigcacheStore struct {
- client BigcacheClientInterface
- options *store.Options
-}
-
-// NewBigcache creates a new store to Bigcache instance(s)
-func NewBigcache(client BigcacheClientInterface, options ...store.Option) *BigcacheStore {
- return &BigcacheStore{
- client: client,
- options: store.ApplyOptions(options...),
- }
-}
-
-// Get returns data stored from a given key
-func (s *BigcacheStore) Get(_ context.Context, key any) (any, error) {
- item, err := s.client.Get(key.(string))
- if err != nil {
- return nil, err
- }
- if item == nil {
- return nil, store.NotFoundWithCause(errors.New("unable to retrieve data from bigcache"))
- }
-
- return item, err
-}
-
-// Not implemented for BigcacheStore
-func (s *BigcacheStore) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
- return nil, 0, errors.New("method not implemented for codec, use Get() instead")
-}
-
-// Set defines data in Bigcache for given key identifier
-func (s *BigcacheStore) Set(ctx context.Context, key any, value any, options ...store.Option) error {
- opts := store.ApplyOptionsWithDefault(s.options, options...)
-
- var val []byte
- switch v := value.(type) {
- case string:
- val = []byte(v)
- case []byte:
- val = v
- default:
- return errors.New("value type not supported by Bigcache store")
- }
-
- err := s.client.Set(key.(string), val)
- if err != nil {
- return err
- }
-
- if tags := opts.Tags; len(tags) > 0 {
- s.setTags(ctx, key, tags)
- }
-
- return nil
-}
-
-func (s *BigcacheStore) setTags(ctx context.Context, key any, tags []string) {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(BigcacheTagPattern, tag)
- cacheKeys := []string{}
-
- if result, err := s.Get(ctx, tagKey); err == nil {
- if bytes, ok := result.([]byte); ok {
- cacheKeys = strings.Split(string(bytes), ",")
- }
- }
-
- alreadyInserted := false
- for _, cacheKey := range cacheKeys {
- if cacheKey == key.(string) {
- alreadyInserted = true
- break
- }
- }
-
- if !alreadyInserted {
- cacheKeys = append(cacheKeys, key.(string))
- }
-
- s.Set(ctx, tagKey, []byte(strings.Join(cacheKeys, ",")), store.WithExpiration(720*time.Hour))
- }
-}
-
-// Delete removes data from Bigcache for given key identifier
-func (s *BigcacheStore) Delete(_ context.Context, key any) error {
- return s.client.Delete(key.(string))
-}
-
-// Invalidate invalidates some cache data in Bigcache for given options
-func (s *BigcacheStore) Invalidate(ctx context.Context, options ...store.InvalidateOption) error {
- opts := store.ApplyInvalidateOptions(options...)
-
- if tags := opts.Tags; len(tags) > 0 {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(BigcacheTagPattern, tag)
- result, err := s.Get(ctx, tagKey)
- if err != nil {
- return nil
- }
-
- cacheKeys := []string{}
- if bytes, ok := result.([]byte); ok {
- cacheKeys = strings.Split(string(bytes), ",")
- }
-
- for _, cacheKey := range cacheKeys {
- s.Delete(ctx, cacheKey)
- }
- }
- }
-
- return nil
-}
-
-// Clear resets all data in the store
-func (s *BigcacheStore) Clear(_ context.Context) error {
- return s.client.Reset()
-}
-
-// GetType returns the store type
-func (s *BigcacheStore) GetType() string {
- return BigcacheType
-}
diff --git a/toolkit/gocache/store/bigcache/bigcache_bench_test.go b/toolkit/gocache/store/bigcache/bigcache_bench_test.go
deleted file mode 100644
index 1cdc9a58..00000000
--- a/toolkit/gocache/store/bigcache/bigcache_bench_test.go
+++ /dev/null
@@ -1,52 +0,0 @@
-package bigcache
-
-import (
- "context"
- "fmt"
- "math"
- "testing"
- "time"
-
- "github.com/allegro/bigcache/v3"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-func BenchmarkBigcacheSet(b *testing.B) {
- ctx := context.Background()
-
- client, _ := bigcache.NewBigCache(bigcache.DefaultConfig(5 * time.Minute))
- store := NewBigcache(client, nil)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- key := fmt.Sprintf("test-%d", n)
- value := []byte(fmt.Sprintf("value-%d", n))
-
- store.Set(ctx, key, value, lib_store.WithTags([]string{fmt.Sprintf("tag-%d", n)}))
- }
- })
- }
-}
-
-func BenchmarkBigcacheGet(b *testing.B) {
- ctx := context.Background()
-
- client, _ := bigcache.NewBigCache(bigcache.DefaultConfig(5 * time.Minute))
- store := NewBigcache(client, nil)
-
- key := "test"
- value := []byte("value")
-
- store.Set(ctx, key, value, nil)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- _, _ = store.Get(ctx, key)
- }
- })
- }
-}
diff --git a/toolkit/gocache/store/bigcache/bigcache_mock.go b/toolkit/gocache/store/bigcache/bigcache_mock.go
deleted file mode 100644
index b412a795..00000000
--- a/toolkit/gocache/store/bigcache/bigcache_mock.go
+++ /dev/null
@@ -1,91 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: store/bigcache/bigcache.go
-
-// Package bigcache is a generated GoMock package.
-package bigcache
-
-import (
- reflect "reflect"
-
- gomock "go.uber.org/mock/gomock"
-)
-
-// MockBigcacheClientInterface is a mock of BigcacheClientInterface interface.
-type MockBigcacheClientInterface struct {
- ctrl *gomock.Controller
- recorder *MockBigcacheClientInterfaceMockRecorder
-}
-
-// MockBigcacheClientInterfaceMockRecorder is the mock recorder for MockBigcacheClientInterface.
-type MockBigcacheClientInterfaceMockRecorder struct {
- mock *MockBigcacheClientInterface
-}
-
-// NewMockBigcacheClientInterface creates a new mock instance.
-func NewMockBigcacheClientInterface(ctrl *gomock.Controller) *MockBigcacheClientInterface {
- mock := &MockBigcacheClientInterface{ctrl: ctrl}
- mock.recorder = &MockBigcacheClientInterfaceMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockBigcacheClientInterface) EXPECT() *MockBigcacheClientInterfaceMockRecorder {
- return m.recorder
-}
-
-// Delete mocks base method.
-func (m *MockBigcacheClientInterface) Delete(key string) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Delete", key)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Delete indicates an expected call of Delete.
-func (mr *MockBigcacheClientInterfaceMockRecorder) Delete(key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockBigcacheClientInterface)(nil).Delete), key)
-}
-
-// Get mocks base method.
-func (m *MockBigcacheClientInterface) Get(key string) ([]byte, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", key)
- ret0, _ := ret[0].([]byte)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockBigcacheClientInterfaceMockRecorder) Get(key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockBigcacheClientInterface)(nil).Get), key)
-}
-
-// Reset mocks base method.
-func (m *MockBigcacheClientInterface) Reset() error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Reset")
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Reset indicates an expected call of Reset.
-func (mr *MockBigcacheClientInterfaceMockRecorder) Reset() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Reset", reflect.TypeOf((*MockBigcacheClientInterface)(nil).Reset))
-}
-
-// Set mocks base method.
-func (m *MockBigcacheClientInterface) Set(key string, entry []byte) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Set", key, entry)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Set indicates an expected call of Set.
-func (mr *MockBigcacheClientInterfaceMockRecorder) Set(key, entry interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockBigcacheClientInterface)(nil).Set), key, entry)
-}
diff --git a/toolkit/gocache/store/bigcache/bigcache_test.go b/toolkit/gocache/store/bigcache/bigcache_test.go
deleted file mode 100644
index 66b6c362..00000000
--- a/toolkit/gocache/store/bigcache/bigcache_test.go
+++ /dev/null
@@ -1,340 +0,0 @@
-package bigcache
-
-import (
- "context"
- "errors"
- "testing"
-
- "github.com/stretchr/testify/assert"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "go.uber.org/mock/gomock"
-)
-
-func TestNewBigcache(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockBigcacheClientInterface(ctrl)
-
- // When
- store := NewBigcache(client)
-
- // Then
- assert.IsType(t, new(BigcacheStore), store)
- assert.Equal(t, client, store.client)
- assert.Equal(t, new(lib_store.Options), store.options)
-}
-
-func TestBigcacheGet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(cacheValue, nil)
-
- store := NewBigcache(client)
-
- // When
- value, err := store.Get(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-}
-
-func TestBigcacheGetWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- expectedErr := errors.New("an unexpected error occurred")
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(nil, expectedErr)
-
- store := NewBigcache(client)
-
- // When
- value, err := store.Get(ctx, cacheKey)
-
- // Then
- assert.Equal(t, expectedErr, err)
- assert.Nil(t, value)
-}
-
-func TestBigcacheGetWithTTL(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- client := NewMockBigcacheClientInterface(ctrl)
- store := NewBigcache(client)
-
- expectedErr := errors.New("method not implemented for codec, use Get() instead")
-
- // When
- value, _, err := store.GetWithTTL(ctx, cacheKey)
-
- // Then
- assert.Equal(t, expectedErr, err)
- assert.Nil(t, value)
-}
-
-func TestBigcacheSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Set(cacheKey, cacheValue).Return(nil)
-
- store := NewBigcache(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestBigcacheSetString(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- // The value is string when failback from Redis
- cacheValue := "my-cache-value"
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Set(cacheKey, []byte(cacheValue)).Return(nil)
-
- store := NewBigcache(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestBigcacheSetWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- expectedErr := errors.New("an unexpected error occurred")
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Set(cacheKey, cacheValue).Return(expectedErr)
-
- store := NewBigcache(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestBigcacheSetWithTags(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Set(cacheKey, cacheValue).Return(nil)
- client.EXPECT().Get("gocache_tag_tag1").Return(nil, nil)
- client.EXPECT().Set("gocache_tag_tag1", []byte("my-key")).Return(nil)
-
- store := NewBigcache(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestBigcacheSetWithTagsWhenAlreadyInserted(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Set(cacheKey, cacheValue).Return(nil)
- client.EXPECT().Get("gocache_tag_tag1").Return([]byte("my-key,a-second-key"), nil)
- client.EXPECT().Set("gocache_tag_tag1", []byte("my-key,a-second-key")).Return(nil)
-
- store := NewBigcache(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestBigcacheDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Delete(cacheKey).Return(nil)
-
- store := NewBigcache(client)
-
- // When
- err := store.Delete(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestBigcacheDeleteWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unable to delete key")
-
- cacheKey := "my-key"
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Delete(cacheKey).Return(expectedErr)
-
- store := NewBigcache(client)
-
- // When
- err := store.Delete(ctx, cacheKey)
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestBigcacheInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := []byte("a23fdf987h2svc23,jHG2372x38hf74")
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Get("gocache_tag_tag1").Return(cacheKeys, nil)
- client.EXPECT().Delete("a23fdf987h2svc23").Return(nil)
- client.EXPECT().Delete("jHG2372x38hf74").Return(nil)
-
- store := NewBigcache(client)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestBigcacheInvalidateWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := []byte("a23fdf987h2svc23,jHG2372x38hf74")
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Get("gocache_tag_tag1").Return(cacheKeys, nil)
- client.EXPECT().Delete("a23fdf987h2svc23").Return(errors.New("unexpected error"))
- client.EXPECT().Delete("jHG2372x38hf74").Return(nil)
-
- store := NewBigcache(client)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestBigcacheClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Reset().Return(nil)
-
- store := NewBigcache(client)
-
- // When
- err := store.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestBigcacheClearWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("an unexpected error occurred")
-
- client := NewMockBigcacheClientInterface(ctrl)
- client.EXPECT().Reset().Return(expectedErr)
-
- store := NewBigcache(client)
-
- // When
- err := store.Clear(ctx)
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestBigcacheGetType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockBigcacheClientInterface(ctrl)
-
- store := NewBigcache(client)
-
- // When - Then
- assert.Equal(t, BigcacheType, store.GetType())
-}
diff --git a/toolkit/gocache/store/freecache/freecache.go b/toolkit/gocache/store/freecache/freecache.go
deleted file mode 100644
index 5232a9b1..00000000
--- a/toolkit/gocache/store/freecache/freecache.go
+++ /dev/null
@@ -1,189 +0,0 @@
-package freecache
-
-import (
- "context"
- "errors"
- "fmt"
- "strings"
- "time"
-
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-const (
- // FreecacheType represents the storage type as a string value
- FreecacheType = "freecache"
- // FreecacheTagPattern represents the tag pattern to be used as a key in specified storage
- FreecacheTagPattern = "freecache_tag_%s"
-)
-
-// FreecacheClientInterface represents a coocood/freecache client
-type FreecacheClientInterface interface {
- Get(key []byte) (value []byte, err error)
- GetInt(key int64) (value []byte, err error)
- TTL(key []byte) (timeLeft uint32, err error)
- Set(key, value []byte, expireSeconds int) (err error)
- SetInt(key int64, value []byte, expireSeconds int) (err error)
- Del(key []byte) (affected bool)
- DelInt(key int64) (affected bool)
- Clear()
-}
-
-// FreecacheStore is a store for freecache
-type FreecacheStore struct {
- client FreecacheClientInterface
- options *lib_store.Options
-}
-
-// NewFreecache creates a new store to freecache instance(s)
-func NewFreecache(client FreecacheClientInterface, options ...lib_store.Option) *FreecacheStore {
- return &FreecacheStore{
- client: client,
- options: lib_store.ApplyOptions(options...),
- }
-}
-
-// Get returns data stored from a given key. It returns the value or not found error
-func (f *FreecacheStore) Get(_ context.Context, key any) (any, error) {
- var err error
- var result any
- if k, ok := key.(string); ok {
- result, err = f.client.Get([]byte(k))
- if err != nil {
- return nil, lib_store.NotFoundWithCause(errors.New("value not found in Freecache store"))
- }
- return result, err
- }
-
- return nil, errors.New("key type not supported by Freecache store")
-}
-
-// GetWithTTL returns data stored from a given key and its corresponding TTL
-func (f *FreecacheStore) GetWithTTL(_ context.Context, key any) (any, time.Duration, error) {
- if k, ok := key.(string); ok {
- result, err := f.client.Get([]byte(k))
- if err != nil {
- return nil, 0, lib_store.NotFoundWithCause(errors.New("value not found in Freecache store"))
- }
-
- ttl, err := f.client.TTL([]byte(k))
- if err != nil {
- return nil, 0, lib_store.NotFoundWithCause(errors.New("value not found in Freecache store"))
- }
-
- return result, time.Duration(ttl) * time.Second, err
- }
-
- return nil, 0, errors.New("key type not supported by Freecache store")
-}
-
-// Set sets a key, value and expiration for a cache entry and stores it in the cache.
-// If the key is larger than 65535 or value is larger than 1/1024 of the cache size,
-// the entry will not be written to the cache. expireSeconds <= 0 means no expire,
-// but it can be evicted when cache is full.
-func (f *FreecacheStore) Set(ctx context.Context, key any, value any, options ...lib_store.Option) error {
- var err error
- var val []byte
-
- // Using default options set during cache initialization
- opts := lib_store.ApplyOptionsWithDefault(f.options, options...)
-
- // type check for value, as freecache only supports value of type []byte
- switch v := value.(type) {
- case []byte:
- val = v
- default:
- return errors.New("value type not supported by Freecache store")
- }
-
- if k, ok := key.(string); ok {
- err = f.client.Set([]byte(k), val, int(opts.Expiration.Seconds()))
- if err != nil {
- return fmt.Errorf("size of key: %v, value: %v, err: %v", k, len(val), err)
- }
- if tags := opts.Tags; len(tags) > 0 {
- f.setTags(ctx, key, tags)
- }
- return nil
- }
- return errors.New("key type not supported by Freecache store")
-}
-
-func (f *FreecacheStore) setTags(ctx context.Context, key any, tags []string) {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(FreecacheTagPattern, tag)
- cacheKeys := f.getCacheKeysForTag(ctx, tagKey)
-
- alreadyInserted := false
- for _, cacheKey := range cacheKeys {
- if cacheKey == key.(string) {
- alreadyInserted = true
- break
- }
- }
-
- if !alreadyInserted {
- cacheKeys = append(cacheKeys, key.(string))
- }
-
- f.Set(ctx, tagKey, []byte(strings.Join(cacheKeys, ",")), lib_store.WithExpiration(720*time.Hour))
- }
-}
-
-func (f *FreecacheStore) getCacheKeysForTag(ctx context.Context, tagKey string) []string {
- cacheKeys := []string{}
- if result, err := f.Get(ctx, tagKey); err == nil && result != nil {
- if str, ok := result.([]byte); ok {
- cacheKeys = strings.Split(string(str), ",")
- }
- }
- return cacheKeys
-}
-
-// Delete deletes an item in the cache by key and returns err or nil if a delete occurred
-func (f *FreecacheStore) Delete(_ context.Context, key any) error {
- if v, ok := key.(string); ok {
- if f.client.Del([]byte(v)) {
- return nil
- }
- return fmt.Errorf("failed to delete key %v", key)
- }
- return errors.New("key type not supported by Freecache store")
-}
-
-// Invalidate invalidates some cache data in freecache for given options
-func (f *FreecacheStore) Invalidate(ctx context.Context, options ...lib_store.InvalidateOption) error {
- opts := lib_store.ApplyInvalidateOptions(options...)
-
- if tags := opts.Tags; len(tags) > 0 {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(FreecacheTagPattern, tag)
- cacheKeys := f.getCacheKeysForTag(ctx, tagKey)
-
- for _, cacheKey := range cacheKeys {
- err := f.Delete(ctx, cacheKey)
- if err != nil {
- return err
- }
- }
-
- err := f.Delete(ctx, tagKey)
- if err != nil {
- return err
- }
- }
- }
-
- return nil
-}
-
-// Clear resets all data in the store
-func (f *FreecacheStore) Clear(_ context.Context) error {
- f.client.Clear()
- return nil
-}
-
-// GetType returns the store type
-func (f *FreecacheStore) GetType() string {
- return FreecacheType
-}
diff --git a/toolkit/gocache/store/freecache/freecache_bench_test.go b/toolkit/gocache/store/freecache/freecache_bench_test.go
deleted file mode 100644
index 49082200..00000000
--- a/toolkit/gocache/store/freecache/freecache_bench_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package freecache
-
-import (
- "context"
- "fmt"
- "math"
- "testing"
- "time"
-
- "github.com/coocood/freecache"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-func BenchmarkFreecacheSet(b *testing.B) {
- ctx := context.Background()
-
- c := freecache.NewCache(1000)
- freecacheStore := NewFreecache(c, lib_store.WithExpiration(10*time.Second))
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- key := fmt.Sprintf("test-%d", n)
- value := []byte(fmt.Sprintf("value-%d", n))
-
- _ = freecacheStore.Set(ctx, key, value)
- }
- })
- }
-}
-
-func BenchmarkFreecacheGet(b *testing.B) {
- ctx := context.Background()
-
- c := freecache.NewCache(1000)
- freecacheStore := NewFreecache(c, lib_store.WithExpiration(10*time.Second))
- key := "test"
- value := []byte("value")
-
- err := freecacheStore.Set(ctx, key, value)
- if err != nil {
- b.Error(err)
- }
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- _, _ = freecacheStore.Get(ctx, key)
- }
- })
- }
-}
diff --git a/toolkit/gocache/store/freecache/freecache_mock.go b/toolkit/gocache/store/freecache/freecache_mock.go
deleted file mode 100644
index 3ee86e23..00000000
--- a/toolkit/gocache/store/freecache/freecache_mock.go
+++ /dev/null
@@ -1,147 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: store/freecache/freecache.go
-
-// Package freecache is a generated GoMock package.
-package freecache
-
-import (
- reflect "reflect"
-
- gomock "go.uber.org/mock/gomock"
-)
-
-// MockFreecacheClientInterface is a mock of FreecacheClientInterface interface.
-type MockFreecacheClientInterface struct {
- ctrl *gomock.Controller
- recorder *MockFreecacheClientInterfaceMockRecorder
-}
-
-// MockFreecacheClientInterfaceMockRecorder is the mock recorder for MockFreecacheClientInterface.
-type MockFreecacheClientInterfaceMockRecorder struct {
- mock *MockFreecacheClientInterface
-}
-
-// NewMockFreecacheClientInterface creates a new mock instance.
-func NewMockFreecacheClientInterface(ctrl *gomock.Controller) *MockFreecacheClientInterface {
- mock := &MockFreecacheClientInterface{ctrl: ctrl}
- mock.recorder = &MockFreecacheClientInterfaceMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockFreecacheClientInterface) EXPECT() *MockFreecacheClientInterfaceMockRecorder {
- return m.recorder
-}
-
-// Clear mocks base method.
-func (m *MockFreecacheClientInterface) Clear() {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Clear")
-}
-
-// Clear indicates an expected call of Clear.
-func (mr *MockFreecacheClientInterfaceMockRecorder) Clear() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockFreecacheClientInterface)(nil).Clear))
-}
-
-// Del mocks base method.
-func (m *MockFreecacheClientInterface) Del(key []byte) bool {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Del", key)
- ret0, _ := ret[0].(bool)
- return ret0
-}
-
-// Del indicates an expected call of Del.
-func (mr *MockFreecacheClientInterfaceMockRecorder) Del(key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Del", reflect.TypeOf((*MockFreecacheClientInterface)(nil).Del), key)
-}
-
-// DelInt mocks base method.
-func (m *MockFreecacheClientInterface) DelInt(key int64) bool {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DelInt", key)
- ret0, _ := ret[0].(bool)
- return ret0
-}
-
-// DelInt indicates an expected call of DelInt.
-func (mr *MockFreecacheClientInterfaceMockRecorder) DelInt(key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DelInt", reflect.TypeOf((*MockFreecacheClientInterface)(nil).DelInt), key)
-}
-
-// Get mocks base method.
-func (m *MockFreecacheClientInterface) Get(key []byte) ([]byte, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", key)
- ret0, _ := ret[0].([]byte)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockFreecacheClientInterfaceMockRecorder) Get(key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockFreecacheClientInterface)(nil).Get), key)
-}
-
-// GetInt mocks base method.
-func (m *MockFreecacheClientInterface) GetInt(key int64) ([]byte, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetInt", key)
- ret0, _ := ret[0].([]byte)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetInt indicates an expected call of GetInt.
-func (mr *MockFreecacheClientInterfaceMockRecorder) GetInt(key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetInt", reflect.TypeOf((*MockFreecacheClientInterface)(nil).GetInt), key)
-}
-
-// Set mocks base method.
-func (m *MockFreecacheClientInterface) Set(key, value []byte, expireSeconds int) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Set", key, value, expireSeconds)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Set indicates an expected call of Set.
-func (mr *MockFreecacheClientInterfaceMockRecorder) Set(key, value, expireSeconds interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockFreecacheClientInterface)(nil).Set), key, value, expireSeconds)
-}
-
-// SetInt mocks base method.
-func (m *MockFreecacheClientInterface) SetInt(key int64, value []byte, expireSeconds int) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "SetInt", key, value, expireSeconds)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// SetInt indicates an expected call of SetInt.
-func (mr *MockFreecacheClientInterfaceMockRecorder) SetInt(key, value, expireSeconds interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetInt", reflect.TypeOf((*MockFreecacheClientInterface)(nil).SetInt), key, value, expireSeconds)
-}
-
-// TTL mocks base method.
-func (m *MockFreecacheClientInterface) TTL(key []byte) (uint32, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "TTL", key)
- ret0, _ := ret[0].(uint32)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// TTL indicates an expected call of TTL.
-func (mr *MockFreecacheClientInterfaceMockRecorder) TTL(key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TTL", reflect.TypeOf((*MockFreecacheClientInterface)(nil).TTL), key)
-}
diff --git a/toolkit/gocache/store/freecache/freecache_test.go b/toolkit/gocache/store/freecache/freecache_test.go
deleted file mode 100644
index ca1cf1d3..00000000
--- a/toolkit/gocache/store/freecache/freecache_test.go
+++ /dev/null
@@ -1,501 +0,0 @@
-package freecache
-
-import (
- "context"
- "errors"
- "fmt"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "go.uber.org/mock/gomock"
-)
-
-func TestNewFreecache(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockFreecacheClientInterface(ctrl)
-
- // When
- store := NewFreecache(client, lib_store.WithExpiration(6*time.Second))
-
- // Then
- assert.IsType(t, new(FreecacheStore), store)
- assert.Equal(t, client, store.client)
- assert.Equal(t, &lib_store.Options{
- Expiration: 6 * time.Second,
- }, store.options)
-}
-
-func TestNewFreecacheDefaultOptions(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockFreecacheClientInterface(ctrl)
-
- // When
- store := NewFreecache(client)
-
- // Then
- assert.IsType(t, new(FreecacheStore), store)
- assert.Equal(t, client, store.client)
- assert.Equal(t, new(lib_store.Options), store.options)
-}
-
-func TestFreecacheGet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Get([]byte("key1")).Return([]byte("val1"), nil)
- client.EXPECT().Get([]byte("key2")).Return([]byte("val2"), nil)
-
- s := NewFreecache(client)
-
- value, err := s.Get(ctx, "key1")
- assert.Nil(t, err)
- assert.Equal(t, []byte("val1"), value)
-
- value, err = s.Get(ctx, "key2")
- assert.Nil(t, err)
- assert.Equal(t, []byte("val2"), value)
-}
-
-func TestFreecacheGetNotFound(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Get([]byte("key1")).Return(nil, errors.New("value not found in store"))
-
- s := NewFreecache(client)
-
- value, err := s.Get(ctx, "key1")
- assert.EqualError(t, err, "value not found in store")
- assert.Nil(t, value)
-}
-
-func TestFreecacheGetWithInvalidKey(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockFreecacheClientInterface(ctrl)
-
- s := NewFreecache(client)
-
- value, err := s.Get(ctx, []byte("key1"))
- assert.Error(t, err, "key type not supported by Freecache store")
- assert.Nil(t, value)
-}
-
-func TestFreecacheGetWithTTL(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Get([]byte(cacheKey)).Return(cacheValue, nil)
- client.EXPECT().TTL([]byte(cacheKey)).Return(uint32(5), nil)
-
- store := NewFreecache(client, lib_store.WithExpiration(3*time.Second))
-
- // When
- value, ttl, err := store.GetWithTTL(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
- assert.Equal(t, 5*time.Second, ttl)
-}
-
-func TestFreecacheGetWithTTLWhenMissingItem(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Get([]byte(cacheKey)).Return(nil, lib_store.NotFound{})
-
- store := NewFreecache(client, lib_store.WithExpiration(3*time.Second))
-
- // When
- value, ttl, err := store.GetWithTTL(ctx, cacheKey)
-
- // Then
- assert.ErrorIs(t, err, lib_store.NotFound{})
- assert.Nil(t, value)
- assert.Equal(t, 0*time.Second, ttl)
-}
-
-func TestFreecacheGetWithTTLWhenErrorAtTTL(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Get([]byte(cacheKey)).Return(cacheValue, nil)
- client.EXPECT().TTL([]byte(cacheKey)).Return(uint32(0), lib_store.NotFound{})
-
- store := NewFreecache(client, lib_store.WithExpiration(3*time.Second))
-
- // When
- value, ttl, err := store.GetWithTTL(ctx, cacheKey)
-
- // Then
- assert.ErrorIs(t, err, lib_store.NotFound{})
- assert.Nil(t, value)
- assert.Equal(t, 0*time.Second, ttl)
-}
-
-func TestFreecacheGetWithTTLWhenInvalidKey(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockFreecacheClientInterface(ctrl)
-
- s := NewFreecache(client)
-
- value, ttl, err := s.GetWithTTL(ctx, []byte("key1"))
- assert.Error(t, err, "key type not supported by Freecache store")
- assert.Nil(t, value)
- assert.Equal(t, 0*time.Second, ttl)
-}
-
-func TestFreecacheSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Set([]byte(cacheKey), cacheValue, 6).Return(nil)
-
- s := NewFreecache(client, lib_store.WithExpiration(6*time.Second))
- err := s.Set(ctx, cacheKey, cacheValue, lib_store.WithExpiration(6*time.Second))
- assert.Nil(t, err)
-}
-
-func TestFreecacheSetWithDefaultOptions(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Set([]byte(cacheKey), cacheValue, 0).Return(nil)
-
- s := NewFreecache(client)
- err := s.Set(ctx, cacheKey, cacheValue)
- assert.Nil(t, err)
-}
-
-func TestFreecacheSetInvalidValue(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
- expectedErr := errors.New("value type not supported by Freecache store")
-
- client := NewMockFreecacheClientInterface(ctrl)
-
- s := NewFreecache(client, lib_store.WithExpiration(6*time.Second))
- err := s.Set(ctx, cacheKey, cacheValue, lib_store.WithExpiration(6*time.Second))
- assert.Equal(t, expectedErr, err)
-}
-
-func TestFreecacheSetInvalidSize(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
- expectedErr := fmt.Errorf("size of key: %v, value: %v, err: %v", cacheKey, cacheValue, errors.New(""))
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Set([]byte(cacheKey), cacheValue, 6).Return(expectedErr)
-
- s := NewFreecache(client, lib_store.WithExpiration(6*time.Second))
- err := s.Set(ctx, cacheKey, cacheValue, lib_store.WithExpiration(6*time.Second))
- assert.NotNil(t, err)
-}
-
-func TestFreecacheSetInvalidKey(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := 1
- cacheValue := []byte("my-cache-value")
-
- expectedErr := errors.New("key type not supported by Freecache store")
-
- client := NewMockFreecacheClientInterface(ctrl)
-
- s := NewFreecache(client, lib_store.WithExpiration(6*time.Second))
- err := s.Set(ctx, cacheKey, cacheValue, lib_store.WithExpiration(6*time.Second))
- assert.Equal(t, expectedErr, err)
-}
-
-func TestFreecacheDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "key"
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Del(gomock.Any()).Return(true)
-
- s := NewFreecache(client)
- err := s.Delete(ctx, cacheKey)
- assert.Nil(t, err)
-}
-
-func TestFreecacheDeleteFailed(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "key"
- expectedErr := fmt.Errorf("failed to delete key %v", cacheKey)
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Del(gomock.Any()).Return(false)
-
- s := NewFreecache(client)
- err := s.Delete(ctx, cacheKey)
- assert.Equal(t, expectedErr, err)
-}
-
-func TestFreecacheDeleteInvalidKey(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := 1
- expectedErr := errors.New("key type not supported by Freecache store")
- client := NewMockFreecacheClientInterface(ctrl)
-
- s := NewFreecache(client)
- err := s.Delete(ctx, cacheKey)
- assert.Equal(t, expectedErr, err)
-}
-
-func TestFreecacheSetWithTags(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Set([]byte(cacheKey), cacheValue, 6).Return(nil)
- client.EXPECT().Get([]byte("freecache_tag_tag1")).MaxTimes(1).Return(nil, errors.New("value not found in store"))
- client.EXPECT().Set([]byte("freecache_tag_tag1"), []byte("my-key"), 2592000).Return(nil)
-
- s := NewFreecache(client, lib_store.WithExpiration(6*time.Second))
- err := s.Set(ctx, cacheKey, cacheValue, lib_store.WithExpiration(6*time.Second), lib_store.WithTags([]string{"tag1"}))
- assert.Nil(t, err)
-}
-
-func TestFreecacheInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := []byte("my-key")
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Get([]byte("freecache_tag_tag1")).Return(cacheKeys, nil)
- client.EXPECT().Del([]byte("my-key")).Return(true)
- client.EXPECT().Del([]byte("freecache_tag_tag1")).Return(true)
-
- s := NewFreecache(client, lib_store.WithExpiration(6*time.Second))
-
- // When
- err := s.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestFreecacheTagsAlreadyPresent(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- oldCacheKeys := []byte("key1,key2")
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Set([]byte(cacheKey), cacheValue, 6).Return(nil)
- client.EXPECT().Get([]byte("freecache_tag_tag1")).MaxTimes(1).Return(oldCacheKeys, nil)
- client.EXPECT().Set([]byte("freecache_tag_tag1"), []byte("key1,key2,my-key"), 2592000).Return(nil)
-
- s := NewFreecache(client, lib_store.WithExpiration(6*time.Second))
- err := s.Set(ctx, cacheKey, cacheValue, lib_store.WithExpiration(6*time.Second), lib_store.WithTags([]string{"tag1"}))
- assert.Nil(t, err)
-}
-
-func TestFreecacheTagsRefreshTime(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- oldCacheKeys := []byte("my-key")
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Set([]byte(cacheKey), cacheValue, 6).Return(nil)
- client.EXPECT().Get([]byte("freecache_tag_tag1")).MaxTimes(1).Return(oldCacheKeys, nil)
- client.EXPECT().Set([]byte("freecache_tag_tag1"), []byte("my-key"), 2592000).Return(nil)
-
- s := NewFreecache(client, lib_store.WithExpiration(6*time.Second))
- err := s.Set(ctx, cacheKey, cacheValue, lib_store.WithExpiration(6*time.Second), lib_store.WithTags([]string{"tag1"}))
- assert.Nil(t, err)
-}
-
-func TestFreecacheInvalidateMultipleKeys(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := []byte("my-key,key1,key2")
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Get([]byte("freecache_tag_tag1")).Return(cacheKeys, nil)
- client.EXPECT().Del([]byte("my-key")).Return(true)
- client.EXPECT().Del([]byte("key1")).Return(true)
- client.EXPECT().Del([]byte("key2")).Return(true)
- client.EXPECT().Del([]byte("freecache_tag_tag1")).Return(true)
-
- s := NewFreecache(client, lib_store.WithExpiration(6*time.Second))
-
- // When
- err := s.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestFreecacheFailedInvalidateMultipleKeys(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := []byte("my-key,key1,key2")
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Get([]byte("freecache_tag_tag1")).Return(cacheKeys, nil)
- client.EXPECT().Del([]byte("my-key")).Return(false)
-
- s := NewFreecache(client, lib_store.WithExpiration(6*time.Second))
-
- // When
- err := s.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.EqualError(t, err, "failed to delete key my-key")
-}
-
-func TestFreecacheFailedInvalidatePattern(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := []byte("my-key,key1,key2")
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Get([]byte("freecache_tag_tag1")).Return(cacheKeys, nil)
- client.EXPECT().Del([]byte("my-key")).Return(true)
- client.EXPECT().Del([]byte("key1")).Return(true)
- client.EXPECT().Del([]byte("key2")).Return(true)
- client.EXPECT().Del([]byte("freecache_tag_tag1")).Return(false)
-
- s := NewFreecache(client, lib_store.WithExpiration(6*time.Second))
-
- // When
- err := s.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.EqualError(t, err, "failed to delete key freecache_tag_tag1")
-}
-
-func TestFreecacheClearAll(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockFreecacheClientInterface(ctrl)
- client.EXPECT().Clear()
-
- s := NewFreecache(client)
-
- // When
- err := s.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestFreecacheGetType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockFreecacheClientInterface(ctrl)
-
- s := NewFreecache(client)
-
- // When
- ty := s.GetType()
-
- // Then
- assert.Equal(t, FreecacheType, ty)
-}
diff --git a/toolkit/gocache/store/go_cache/go_cache.go b/toolkit/gocache/store/go_cache/go_cache.go
deleted file mode 100644
index b1983eb4..00000000
--- a/toolkit/gocache/store/go_cache/go_cache.go
+++ /dev/null
@@ -1,158 +0,0 @@
-package go_cache
-
-import (
- "context"
- "errors"
- "fmt"
- "sync"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
-
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-const (
- // GoCacheType represents the storage type as a string value
- GoCacheType = "go-cache"
- // GoCacheTagPattern represents the tag pattern to be used as a key in specified storage
- GoCacheTagPattern = "gocache_tag_%s"
-)
-
-// GoCacheClientInterface represents a github.com/patrickmn/go-cache client
-type GoCacheClientInterface interface {
- Get(k string) (any, bool)
- GetWithExpiration(k string) (any, time.Time, bool)
- Set(k string, x any, d time.Duration)
- Delete(k string)
- Flush()
-}
-
-// GoCacheStore is a store for GoCache (memory) library
-type GoCacheStore struct {
- mu sync.RWMutex
- client GoCacheClientInterface
- options *lib_store.Options
-}
-
-// NewGoCache creates a new store to GoCache (memory) library instance
-func NewGoCache(client GoCacheClientInterface, options ...lib_store.Option) *GoCacheStore {
- return &GoCacheStore{
- client: client,
- options: lib_store.ApplyOptions(options...),
- }
-}
-
-// Get returns data stored from a given key
-func (s *GoCacheStore) Get(_ context.Context, key any) (any, error) {
- var err error
- keyStr := key.(string)
- value, exists := s.client.Get(keyStr)
- if !exists {
- err = lib_store.NotFoundWithCause(errors.New("value not found in GoCache store"))
- }
-
- return value, err
-}
-
-// GetWithTTL returns data stored from a given key and its corresponding TTL
-func (s *GoCacheStore) GetWithTTL(_ context.Context, key any) (any, time.Duration, error) {
- data, t, exists := s.client.GetWithExpiration(key.(string))
- if !exists {
- return data, 0, lib_store.NotFoundWithCause(errors.New("value not found in GoCache store"))
- }
- duration := time.Until(t)
- return data, duration, nil
-}
-
-// Set defines data in GoCache memoey cache for given key identifier
-func (s *GoCacheStore) Set(ctx context.Context, key any, value any, options ...lib_store.Option) error {
- opts := lib_store.ApplyOptions(options...)
- if opts == nil {
- opts = s.options
- }
-
- s.client.Set(key.(string), value, opts.Expiration)
-
- if tags := opts.Tags; len(tags) > 0 {
- s.setTags(ctx, key, tags)
- }
-
- return nil
-}
-
-func (s *GoCacheStore) setTags(ctx context.Context, key any, tags []string) {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(GoCacheTagPattern, tag)
- var cacheKeys map[string]struct{}
-
- if result, err := s.Get(ctx, tagKey); err == nil {
- if bytes, ok := result.(map[string]struct{}); ok {
- cacheKeys = bytes
- }
- }
-
- s.mu.RLock()
- if _, exists := cacheKeys[key.(string)]; exists {
- s.mu.RUnlock()
- continue
- }
- s.mu.RUnlock()
-
- if cacheKeys == nil {
- cacheKeys = make(map[string]struct{})
- }
-
- s.mu.Lock()
- cacheKeys[key.(string)] = struct{}{}
- s.mu.Unlock()
-
- s.client.Set(tagKey, cacheKeys, 720*time.Hour)
- }
-}
-
-// Delete removes data in GoCache memoey cache for given key identifier
-func (s *GoCacheStore) Delete(_ context.Context, key any) error {
- s.client.Delete(key.(string))
- return nil
-}
-
-// Invalidate invalidates some cache data in GoCache memoey cache for given options
-func (s *GoCacheStore) Invalidate(ctx context.Context, options ...lib_store.InvalidateOption) error {
- opts := lib_store.ApplyInvalidateOptions(options...)
-
- if tags := opts.Tags; len(tags) > 0 {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(GoCacheTagPattern, tag)
- result, err := s.Get(ctx, tagKey)
- if err != nil {
- zlogger.Warn().Msg(err.Error())
- continue
- }
-
- var cacheKeys map[string]struct{}
- if bytes, ok := result.(map[string]struct{}); ok {
- cacheKeys = bytes
- }
-
- s.mu.RLock()
- for cacheKey := range cacheKeys {
- _ = s.Delete(ctx, cacheKey)
- }
- s.mu.RUnlock()
- }
- }
-
- return nil
-}
-
-// GetType returns the store type
-func (s *GoCacheStore) GetType() string {
- return GoCacheType
-}
-
-// Clear resets all data in the store
-func (s *GoCacheStore) Clear(_ context.Context) error {
- s.client.Flush()
- return nil
-}
diff --git a/toolkit/gocache/store/go_cache/go_cache_bench_test.go b/toolkit/gocache/store/go_cache/go_cache_bench_test.go
deleted file mode 100644
index 540a8ca8..00000000
--- a/toolkit/gocache/store/go_cache/go_cache_bench_test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package go_cache
-
-import (
- "context"
- "fmt"
- "math"
- "testing"
- "time"
-
- cache "github.com/patrickmn/go-cache"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-func BenchmarkGoCacheSet(b *testing.B) {
- ctx := context.Background()
-
- client := cache.New(10*time.Second, 30*time.Second)
-
- store := NewGoCache(client, nil)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- key := fmt.Sprintf("test-%d", n)
- value := []byte(fmt.Sprintf("value-%d", n))
-
- store.Set(ctx, key, value, lib_store.WithTags([]string{fmt.Sprintf("tag-%d", n)}))
- }
- })
- }
-}
-
-func BenchmarkGoCacheGet(b *testing.B) {
- ctx := context.Background()
-
- client := cache.New(10*time.Second, 30*time.Second)
-
- store := NewGoCache(client, nil)
-
- key := "test"
- value := []byte("value")
-
- store.Set(ctx, key, value, nil)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- _, _ = store.Get(ctx, key)
- }
- })
- }
-}
diff --git a/toolkit/gocache/store/go_cache/go_cache_mock.go b/toolkit/gocache/store/go_cache/go_cache_mock.go
deleted file mode 100644
index 3562647d..00000000
--- a/toolkit/gocache/store/go_cache/go_cache_mock.go
+++ /dev/null
@@ -1,102 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: store/go_cache/go_cache.go
-
-// Package go_cache is a generated GoMock package.
-package go_cache
-
-import (
- reflect "reflect"
- time "time"
-
- gomock "go.uber.org/mock/gomock"
-)
-
-// MockGoCacheClientInterface is a mock of GoCacheClientInterface interface.
-type MockGoCacheClientInterface struct {
- ctrl *gomock.Controller
- recorder *MockGoCacheClientInterfaceMockRecorder
-}
-
-// MockGoCacheClientInterfaceMockRecorder is the mock recorder for MockGoCacheClientInterface.
-type MockGoCacheClientInterfaceMockRecorder struct {
- mock *MockGoCacheClientInterface
-}
-
-// NewMockGoCacheClientInterface creates a new mock instance.
-func NewMockGoCacheClientInterface(ctrl *gomock.Controller) *MockGoCacheClientInterface {
- mock := &MockGoCacheClientInterface{ctrl: ctrl}
- mock.recorder = &MockGoCacheClientInterfaceMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockGoCacheClientInterface) EXPECT() *MockGoCacheClientInterfaceMockRecorder {
- return m.recorder
-}
-
-// Delete mocks base method.
-func (m *MockGoCacheClientInterface) Delete(k string) {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Delete", k)
-}
-
-// Delete indicates an expected call of Delete.
-func (mr *MockGoCacheClientInterfaceMockRecorder) Delete(k interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockGoCacheClientInterface)(nil).Delete), k)
-}
-
-// Flush mocks base method.
-func (m *MockGoCacheClientInterface) Flush() {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Flush")
-}
-
-// Flush indicates an expected call of Flush.
-func (mr *MockGoCacheClientInterfaceMockRecorder) Flush() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Flush", reflect.TypeOf((*MockGoCacheClientInterface)(nil).Flush))
-}
-
-// Get mocks base method.
-func (m *MockGoCacheClientInterface) Get(k string) (any, bool) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", k)
- ret0, _ := ret[0].(any)
- ret1, _ := ret[1].(bool)
- return ret0, ret1
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockGoCacheClientInterfaceMockRecorder) Get(k interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockGoCacheClientInterface)(nil).Get), k)
-}
-
-// GetWithExpiration mocks base method.
-func (m *MockGoCacheClientInterface) GetWithExpiration(k string) (any, time.Time, bool) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetWithExpiration", k)
- ret0, _ := ret[0].(any)
- ret1, _ := ret[1].(time.Time)
- ret2, _ := ret[2].(bool)
- return ret0, ret1, ret2
-}
-
-// GetWithExpiration indicates an expected call of GetWithExpiration.
-func (mr *MockGoCacheClientInterfaceMockRecorder) GetWithExpiration(k interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetWithExpiration", reflect.TypeOf((*MockGoCacheClientInterface)(nil).GetWithExpiration), k)
-}
-
-// Set mocks base method.
-func (m *MockGoCacheClientInterface) Set(k string, x any, d time.Duration) {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Set", k, x, d)
-}
-
-// Set indicates an expected call of Set.
-func (mr *MockGoCacheClientInterfaceMockRecorder) Set(k, x, d interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockGoCacheClientInterface)(nil).Set), k, x, d)
-}
diff --git a/toolkit/gocache/store/go_cache/go_cache_test.go b/toolkit/gocache/store/go_cache/go_cache_test.go
deleted file mode 100644
index 21abbdbd..00000000
--- a/toolkit/gocache/store/go_cache/go_cache_test.go
+++ /dev/null
@@ -1,346 +0,0 @@
-package go_cache
-
-import (
- "context"
- "fmt"
- "testing"
- "time"
-
- "github.com/patrickmn/go-cache"
- "github.com/stretchr/testify/assert"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "go.uber.org/mock/gomock"
-)
-
-func TestNewGoCache(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockGoCacheClientInterface(ctrl)
- // When
- store := NewGoCache(client, lib_store.WithCost(8))
-
- // Then
- assert.IsType(t, new(GoCacheStore), store)
- assert.Equal(t, client, store.client)
- assert.Equal(t, &lib_store.Options{Cost: 8}, store.options)
-}
-
-func TestGoCacheGet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockGoCacheClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(cacheValue, true)
-
- store := NewGoCache(client)
-
- // When
- value, err := store.Get(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-}
-
-func TestGoCacheGetWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := NewMockGoCacheClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(nil, false)
-
- store := NewGoCache(client)
-
- // When
- value, err := store.Get(ctx, cacheKey)
-
- // Then
- assert.Nil(t, value)
- assert.Error(t, err, lib_store.NotFound{})
-}
-
-func TestGoCacheGetWithTTL(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockGoCacheClientInterface(ctrl)
- client.EXPECT().GetWithExpiration(cacheKey).Return(cacheValue, time.Now(), true)
-
- store := NewGoCache(client)
-
- // When
- value, ttl, err := store.GetWithTTL(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
- assert.Equal(t, int64(0), ttl.Milliseconds())
-}
-
-func TestGoCacheGetWithTTLWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := NewMockGoCacheClientInterface(ctrl)
- client.EXPECT().GetWithExpiration(cacheKey).Return(nil, time.Now(), false)
-
- store := NewGoCache(client)
-
- // When
- value, ttl, err := store.GetWithTTL(ctx, cacheKey)
-
- // Then
- assert.Nil(t, value)
- assert.Error(t, err, lib_store.NotFound{})
- assert.Equal(t, 0*time.Second, ttl)
-}
-
-func TestGoCacheSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockGoCacheClientInterface(ctrl)
- client.EXPECT().Set(cacheKey, cacheValue, 0*time.Second)
-
- store := NewGoCache(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithCost(4))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestGoCacheSetWhenNoOptionsGiven(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockGoCacheClientInterface(ctrl)
- client.EXPECT().Set(cacheKey, cacheValue, 0*time.Second)
-
- store := NewGoCache(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestGoCacheSetWithTags(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockGoCacheClientInterface(ctrl)
- client.EXPECT().Set(cacheKey, cacheValue, 0*time.Second)
- client.EXPECT().Get("gocache_tag_tag1").Return(nil, true)
- cacheKeys := map[string]struct{}{"my-key": {}}
- client.EXPECT().Set("gocache_tag_tag1", cacheKeys, 720*time.Hour)
-
- store := NewGoCache(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestGoCacheSetWithTagsWhenAlreadyInserted(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockGoCacheClientInterface(ctrl)
- client.EXPECT().Set(cacheKey, cacheValue, 0*time.Second)
-
- cacheKeys := map[string]struct{}{"my-key": {}, "a-second-key": {}}
- client.EXPECT().Get("gocache_tag_tag1").Return(cacheKeys, true)
-
- store := NewGoCache(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestGoCacheDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := NewMockGoCacheClientInterface(ctrl)
- client.EXPECT().Delete(cacheKey)
-
- store := NewGoCache(client)
-
- // When
- err := store.Delete(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestGoCacheInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := map[string]struct{}{"a23fdf987h2svc23": {}, "jHG2372x38hf74": {}}
-
- client := NewMockGoCacheClientInterface(ctrl)
- client.EXPECT().Get("gocache_tag_tag1").Return(cacheKeys, true)
- client.EXPECT().Delete("a23fdf987h2svc23")
- client.EXPECT().Delete("jHG2372x38hf74")
-
- store := NewGoCache(client)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestGoCacheInvalidateWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := []byte("a23fdf987h2svc23,jHG2372x38hf74")
-
- client := NewMockGoCacheClientInterface(ctrl)
- client.EXPECT().Get("gocache_tag_tag1").Return(cacheKeys, false)
-
- store := NewGoCache(client)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestGoCacheClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockGoCacheClientInterface(ctrl)
- client.EXPECT().Flush()
-
- store := NewGoCache(client)
-
- // When
- err := store.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestGoCacheGetType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockGoCacheClientInterface(ctrl)
-
- store := NewGoCache(client)
-
- // When - Then
- assert.Equal(t, GoCacheType, store.GetType())
-}
-
-func TestGoCacheSetTagsConcurrency(t *testing.T) {
- ctx := context.Background()
-
- client := cache.New(10*time.Second, 30*time.Second)
- store := NewGoCache(client)
-
- for i := 0; i < 200; i++ {
- go func(i int) {
- key := fmt.Sprintf("%d", i)
-
- err := store.Set(
- ctx,
- key,
- []string{"one", "two"},
- lib_store.WithTags([]string{"tag1", "tag2", "tag3"}),
- )
- assert.Nil(t, err, err)
- }(i)
- }
-}
-
-func TestGoCacheInvalidateConcurrency(t *testing.T) {
- ctx := context.Background()
-
- client := cache.New(10*time.Second, 30*time.Second)
- store := NewGoCache(client)
-
- var tags []string
- for i := 0; i < 200; i++ {
- tags = append(tags, fmt.Sprintf("tag%d", i))
- }
-
- for i := 0; i < 200; i++ {
-
- go func(i int) {
- key := fmt.Sprintf("%d", i)
-
- err := store.Set(ctx, key, []string{"one", "two"}, lib_store.WithTags(tags))
- assert.Nil(t, err, err)
- }(i)
-
- go func(i int) {
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{fmt.Sprintf("tag%d", i)}))
- assert.Nil(t, err, err)
- }(i)
-
- }
-}
diff --git a/toolkit/gocache/store/hazelcast/hazelcast.go b/toolkit/gocache/store/hazelcast/hazelcast.go
deleted file mode 100644
index 03320a46..00000000
--- a/toolkit/gocache/store/hazelcast/hazelcast.go
+++ /dev/null
@@ -1,184 +0,0 @@
-package hazelcast
-
-import (
- "context"
- "errors"
- "fmt"
- "strings"
- "time"
-
- "github.com/hazelcast/hazelcast-go-client"
- "github.com/hazelcast/hazelcast-go-client/types"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "golang.org/x/sync/errgroup"
-)
-
-// HazelcastMapInterface represents a hazelcast/hazelcast-go-client map
-type HazelcastMapInterface interface {
- Get(ctx context.Context, key any) (any, error)
- GetEntryView(ctx context.Context, key any) (*types.SimpleEntryView, error)
- SetWithTTL(ctx context.Context, key any, value any, ttl time.Duration) error
- Remove(ctx context.Context, key any) (any, error)
- Clear(ctx context.Context) error
-}
-
-type HazelcastMapInterfaceProvider func(ctx context.Context) (HazelcastMapInterface, error)
-
-const (
- // HazelcastType represents the storage type as a string value
- HazelcastType = "hazelcast"
- // HazelcastTagPattern represents the tag pattern to be used as a key in specified storage
- HazelcastTagPattern = "gocache_tag_%s"
-
- TagKeyExpiry = 720 * time.Hour
-)
-
-// HazelcastStore is a store for Hazelcast
-type HazelcastStore struct {
- mapProvider HazelcastMapInterfaceProvider
- options *lib_store.Options
-}
-
-// NewHazelcast creates a new store to Hazelcast instance(s)
-func NewHazelcast(hzClient *hazelcast.Client, mapName string, options ...lib_store.Option) *HazelcastStore {
- return &HazelcastStore{
- mapProvider: func(ctx context.Context) (HazelcastMapInterface, error) {
- return hzClient.GetMap(ctx, mapName)
- },
- options: lib_store.ApplyOptions(options...),
- }
-}
-
-// newHazelcast creates a new store with given HazelcastMapInterface for test purpose
-func newHazelcast(hzMap HazelcastMapInterface, options ...lib_store.Option) *HazelcastStore {
- return &HazelcastStore{
- mapProvider: func(ctx context.Context) (HazelcastMapInterface, error) {
- return hzMap, nil
- },
- options: lib_store.ApplyOptions(options...),
- }
-}
-
-// Get returns data stored from a given key
-func (s *HazelcastStore) Get(ctx context.Context, key any) (any, error) {
- hzMap, err := s.mapProvider(ctx)
- if err != nil {
- return nil, err
- }
- value, err := hzMap.Get(ctx, key)
- if err != nil {
- return nil, err
- }
- if value == nil {
- return nil, lib_store.NotFoundWithCause(errors.New("unable to retrieve data from hazelcast"))
- }
- return value, err
-}
-
-// GetWithTTL returns data stored from a given key and its corresponding TTL
-func (s *HazelcastStore) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
- hzMap, err := s.mapProvider(ctx)
- if err != nil {
- return nil, 0, err
- }
- entryView, err := hzMap.GetEntryView(ctx, key)
- if err != nil {
- return nil, 0, err
- }
- if entryView == nil {
- return nil, 0, lib_store.NotFoundWithCause(errors.New("unable to retrieve data from hazelcast"))
- }
- return entryView.Value, time.Duration(entryView.TTL) * time.Millisecond, err
-}
-
-// Set defines data in Hazelcast for given key identifier
-func (s *HazelcastStore) Set(ctx context.Context, key any, value any, options ...lib_store.Option) error {
- opts := lib_store.ApplyOptionsWithDefault(s.options, options...)
- hzMap, err := s.mapProvider(ctx)
- if err != nil {
- return err
- }
- err = hzMap.SetWithTTL(ctx, key, value, opts.Expiration)
- if err != nil {
- return err
- }
- if tags := opts.Tags; len(tags) > 0 {
- s.setTags(ctx, hzMap, key, tags)
- }
- return nil
-}
-
-func (s *HazelcastStore) setTags(ctx context.Context, hzMap HazelcastMapInterface, key any, tags []string) {
- group, ctx := errgroup.WithContext(ctx)
- for _, tag := range tags {
- currentTag := tag
- group.Go(func() error {
- tagKey := fmt.Sprintf(HazelcastTagPattern, currentTag)
- tagValue, err := hzMap.Get(ctx, tagKey)
- if err != nil {
- return err
- }
- if tagValue == nil {
- return hzMap.SetWithTTL(ctx, tagKey, key.(string), TagKeyExpiry)
- }
- cacheKeys := strings.Split(tagValue.(string), ",")
- for _, cacheKey := range cacheKeys {
- if key == cacheKey {
- return nil
- }
- }
- cacheKeys = append(cacheKeys, key.(string))
- newTagValue := strings.Join(cacheKeys, ",")
- return hzMap.SetWithTTL(ctx, tagKey, newTagValue, TagKeyExpiry)
- })
- }
- group.Wait()
-}
-
-// Delete removes data from Hazelcast for given key identifier
-func (s *HazelcastStore) Delete(ctx context.Context, key any) error {
- hzMap, err := s.mapProvider(ctx)
- if err != nil {
- return err
- }
- _, err = hzMap.Remove(ctx, key)
- return err
-}
-
-// Invalidate invalidates some cache data in Hazelcast for given options
-func (s *HazelcastStore) Invalidate(ctx context.Context, options ...lib_store.InvalidateOption) error {
- opts := lib_store.ApplyInvalidateOptions(options...)
- if tags := opts.Tags; len(tags) > 0 {
- hzMap, err := s.mapProvider(ctx)
- if err != nil {
- return err
- }
- for _, tag := range tags {
- tagKey := fmt.Sprintf(HazelcastTagPattern, tag)
- tagValue, err := hzMap.Get(ctx, tagKey)
- if err != nil || tagValue == nil {
- continue
- }
- cacheKeys := strings.Split(tagValue.(string), ",")
- for _, cacheKey := range cacheKeys {
- hzMap.Remove(ctx, cacheKey)
- }
- hzMap.Remove(ctx, tagKey)
- }
- }
- return nil
-}
-
-// Clear resets all data in the store
-func (s *HazelcastStore) Clear(ctx context.Context) error {
- hzMap, err := s.mapProvider(ctx)
- if err != nil {
- return err
- }
- return hzMap.Clear(ctx)
-}
-
-// GetType returns the store type
-func (s *HazelcastStore) GetType() string {
- return HazelcastType
-}
diff --git a/toolkit/gocache/store/hazelcast/hazelcast_benchmark_test.go b/toolkit/gocache/store/hazelcast/hazelcast_benchmark_test.go
deleted file mode 100644
index 8b098e72..00000000
--- a/toolkit/gocache/store/hazelcast/hazelcast_benchmark_test.go
+++ /dev/null
@@ -1,58 +0,0 @@
-package hazelcast
-
-import (
- "context"
- "fmt"
- "math"
- "testing"
-
- "github.com/hazelcast/hazelcast-go-client"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-func BenchmarkHazelcastSet(b *testing.B) {
- ctx := context.Background()
-
- hzClient, err := hazelcast.StartNewClient(ctx)
- if err != nil {
- b.Fatalf("Failed to start client: %v", err)
- }
-
- store := NewHazelcast(hzClient, "gocache")
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- key := fmt.Sprintf("test-%d", n)
- value := []byte(fmt.Sprintf("value-%d", n))
- store.Set(ctx, key, value, lib_store.WithTags([]string{fmt.Sprintf("tag-%d", n)}))
- }
- })
- }
-}
-
-func BenchmarkHazelcastGet(b *testing.B) {
- ctx := context.Background()
-
- hzClient, err := hazelcast.StartNewClient(ctx)
- if err != nil {
- b.Fatalf("Failed to start client: %v", err)
- }
-
- store := NewHazelcast(hzClient, "gocache")
-
- key := "test"
- value := []byte("value")
-
- store.Set(ctx, key, value)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- _, _ = store.Get(ctx, key)
- }
- })
- }
-}
diff --git a/toolkit/gocache/store/hazelcast/hazelcast_mock.go b/toolkit/gocache/store/hazelcast/hazelcast_mock.go
deleted file mode 100644
index 1b306501..00000000
--- a/toolkit/gocache/store/hazelcast/hazelcast_mock.go
+++ /dev/null
@@ -1,110 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: store/hazelcast/hazelcast.go
-
-// Package hazelcast is a generated GoMock package.
-package hazelcast
-
-import (
- context "context"
- reflect "reflect"
- time "time"
-
- types "github.com/hazelcast/hazelcast-go-client/types"
- gomock "go.uber.org/mock/gomock"
-)
-
-// MockHazelcastMapInterface is a mock of HazelcastMapInterface interface.
-type MockHazelcastMapInterface struct {
- ctrl *gomock.Controller
- recorder *MockHazelcastMapInterfaceMockRecorder
-}
-
-// MockHazelcastMapInterfaceMockRecorder is the mock recorder for MockHazelcastMapInterface.
-type MockHazelcastMapInterfaceMockRecorder struct {
- mock *MockHazelcastMapInterface
-}
-
-// NewMockHazelcastMapInterface creates a new mock instance.
-func NewMockHazelcastMapInterface(ctrl *gomock.Controller) *MockHazelcastMapInterface {
- mock := &MockHazelcastMapInterface{ctrl: ctrl}
- mock.recorder = &MockHazelcastMapInterfaceMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockHazelcastMapInterface) EXPECT() *MockHazelcastMapInterfaceMockRecorder {
- return m.recorder
-}
-
-// Clear mocks base method.
-func (m *MockHazelcastMapInterface) Clear(ctx context.Context) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Clear", ctx)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Clear indicates an expected call of Clear.
-func (mr *MockHazelcastMapInterfaceMockRecorder) Clear(ctx interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockHazelcastMapInterface)(nil).Clear), ctx)
-}
-
-// Get mocks base method.
-func (m *MockHazelcastMapInterface) Get(ctx context.Context, key any) (any, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", ctx, key)
- ret0, _ := ret[0].(any)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockHazelcastMapInterfaceMockRecorder) Get(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockHazelcastMapInterface)(nil).Get), ctx, key)
-}
-
-// GetEntryView mocks base method.
-func (m *MockHazelcastMapInterface) GetEntryView(ctx context.Context, key any) (*types.SimpleEntryView, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetEntryView", ctx, key)
- ret0, _ := ret[0].(*types.SimpleEntryView)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// GetEntryView indicates an expected call of GetEntryView.
-func (mr *MockHazelcastMapInterfaceMockRecorder) GetEntryView(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetEntryView", reflect.TypeOf((*MockHazelcastMapInterface)(nil).GetEntryView), ctx, key)
-}
-
-// Remove mocks base method.
-func (m *MockHazelcastMapInterface) Remove(ctx context.Context, key any) (any, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Remove", ctx, key)
- ret0, _ := ret[0].(any)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Remove indicates an expected call of Remove.
-func (mr *MockHazelcastMapInterfaceMockRecorder) Remove(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Remove", reflect.TypeOf((*MockHazelcastMapInterface)(nil).Remove), ctx, key)
-}
-
-// SetWithTTL mocks base method.
-func (m *MockHazelcastMapInterface) SetWithTTL(ctx context.Context, key, value any, ttl time.Duration) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "SetWithTTL", ctx, key, value, ttl)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// SetWithTTL indicates an expected call of SetWithTTL.
-func (mr *MockHazelcastMapInterfaceMockRecorder) SetWithTTL(ctx, key, value, ttl interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWithTTL", reflect.TypeOf((*MockHazelcastMapInterface)(nil).SetWithTTL), ctx, key, value, ttl)
-}
diff --git a/toolkit/gocache/store/hazelcast/hazelcast_test.go b/toolkit/gocache/store/hazelcast/hazelcast_test.go
deleted file mode 100644
index 8bff8d76..00000000
--- a/toolkit/gocache/store/hazelcast/hazelcast_test.go
+++ /dev/null
@@ -1,201 +0,0 @@
-package hazelcast
-
-import (
- "context"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- "go.uber.org/mock/gomock"
-
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-func TestNewHazelcast(t *testing.T) {
- // Given
- mapName := "gocache"
-
- // When
- store := NewHazelcast(nil, mapName, lib_store.WithExpiration(6*time.Second))
-
- // Then
- assert.IsType(t, new(HazelcastStore), store)
- assert.NotNil(t, store.mapProvider)
- assert.Equal(t, &lib_store.Options{Expiration: 6 * time.Second}, store.options)
-}
-
-func TestHazelcastGet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- hzMap := NewMockHazelcastMapInterface(ctrl)
- hzMap.EXPECT().Get(ctx, "my-key").Return("my-value", nil)
-
- store := newHazelcast(hzMap)
-
- // When
- value, err := store.Get(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
- assert.NotNil(t, value)
-}
-
-func TestHazelcastSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- hzMap := NewMockHazelcastMapInterface(ctrl)
- hzMap.EXPECT().SetWithTTL(ctx, cacheKey, cacheValue, 5*time.Second).Return(nil)
-
- store := newHazelcast(hzMap, lib_store.WithExpiration(6*time.Second))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithExpiration(5*time.Second))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestHazelcastSetWhenNoOptionsGiven(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- hzMap := NewMockHazelcastMapInterface(ctrl)
- hzMap.EXPECT().SetWithTTL(ctx, cacheKey, cacheValue, 6*time.Second).Return(nil)
-
- store := newHazelcast(hzMap, lib_store.WithExpiration(6*time.Second))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestHazelcastSetWithTags(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- hzMap := NewMockHazelcastMapInterface(ctrl)
- hzMap.EXPECT().SetWithTTL(ctx, cacheKey, cacheValue, time.Duration(0)).Return(nil)
- hzMap.EXPECT().SetWithTTL(gomock.Any(), "gocache_tag_tag1", cacheKey, TagKeyExpiry).Return(nil)
- hzMap.EXPECT().Get(gomock.Any(), "gocache_tag_tag1").Return(nil, nil)
-
- store := newHazelcast(hzMap)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestHazelcastDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- hzMap := NewMockHazelcastMapInterface(ctrl)
- hzMap.EXPECT().Remove(ctx, "my-key").Return(0, nil)
-
- store := newHazelcast(hzMap)
-
- // When
- err := store.Delete(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestHazelcastInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- hzMap := NewMockHazelcastMapInterface(ctrl)
- hzMap.EXPECT().Get(ctx, "gocache_tag_tag1").Return(nil, nil)
-
- store := newHazelcast(hzMap)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestHazelcastInvalidateWhenCacheKeysExist(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := "my-key0,my-key1,my-key2"
-
- hzMap := NewMockHazelcastMapInterface(ctrl)
- hzMap.EXPECT().Get(ctx, "gocache_tag_tag1").Return(cacheKeys, nil)
- hzMap.EXPECT().Remove(ctx, "my-key0").Return("my-value0", nil)
- hzMap.EXPECT().Remove(ctx, "my-key1").Return("my-value1", nil)
- hzMap.EXPECT().Remove(ctx, "my-key2").Return("my-value2", nil)
- hzMap.EXPECT().Remove(ctx, "gocache_tag_tag1").Return(cacheKeys, nil)
-
- store := newHazelcast(hzMap)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestHazelcastClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- hzMap := NewMockHazelcastMapInterface(ctrl)
- hzMap.EXPECT().Clear(ctx).Return(nil)
-
- store := newHazelcast(hzMap, lib_store.WithExpiration(6*time.Second))
-
- // When
- err := store.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestHazelcastType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- hzMap := NewMockHazelcastMapInterface(ctrl)
-
- store := newHazelcast(hzMap)
-
- // When - Then
- assert.Equal(t, HazelcastType, store.GetType())
-}
diff --git a/toolkit/gocache/store/memcache/memcache.go b/toolkit/gocache/store/memcache/memcache.go
deleted file mode 100644
index 238d4d07..00000000
--- a/toolkit/gocache/store/memcache/memcache.go
+++ /dev/null
@@ -1,199 +0,0 @@
-package memcache
-
-import (
- "context"
- "errors"
- "fmt"
- "strings"
- "time"
-
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "golang.org/x/sync/errgroup"
-
- "github.com/bradfitz/gomemcache/memcache"
-)
-
-// MemcacheClientInterface represents a bradfitz/gomemcache client
-type MemcacheClientInterface interface {
- Get(key string) (item *memcache.Item, err error)
- Set(item *memcache.Item) error
- Delete(item string) error
- FlushAll() error
- CompareAndSwap(item *memcache.Item) error
- Add(item *memcache.Item) error
-}
-
-const (
- // MemcacheType represents the storage type as a string value
- MemcacheType = "memcache"
- // MemcacheTagPattern represents the tag pattern to be used as a key in specified storage
- MemcacheTagPattern = "gocache_tag_%s"
-
- TagKeyExpiry = 720 * time.Hour
-)
-
-// MemcacheStore is a store for Memcache
-type MemcacheStore struct {
- client MemcacheClientInterface
- options *lib_store.Options
-}
-
-// NewMemcache creates a new store to Memcache instance(s)
-func NewMemcache(client MemcacheClientInterface, options ...lib_store.Option) *MemcacheStore {
- return &MemcacheStore{
- client: client,
- options: lib_store.ApplyOptions(options...),
- }
-}
-
-// Get returns data stored from a given key
-func (s *MemcacheStore) Get(_ context.Context, key any) (any, error) {
- item, err := s.client.Get(key.(string))
- if err != nil {
- return nil, err
- }
- if item == nil {
- return nil, lib_store.NotFoundWithCause(errors.New("unable to retrieve data from memcache"))
- }
-
- return item.Value, err
-}
-
-// GetWithTTL returns data stored from a given key and its corresponding TTL
-func (s *MemcacheStore) GetWithTTL(_ context.Context, key any) (any, time.Duration, error) {
- item, err := s.client.Get(key.(string))
- if err != nil {
- return nil, 0, err
- }
- if item == nil {
- return nil, 0, lib_store.NotFoundWithCause(errors.New("unable to retrieve data from memcache"))
- }
-
- return item.Value, time.Duration(item.Expiration) * time.Second, err
-}
-
-// Set defines data in Memcache for given key identifier
-func (s *MemcacheStore) Set(ctx context.Context, key any, value any, options ...lib_store.Option) error {
- opts := lib_store.ApplyOptionsWithDefault(s.options, options...)
-
- item := &memcache.Item{
- Key: key.(string),
- Value: value.([]byte),
- Expiration: int32(opts.Expiration.Seconds()),
- }
-
- err := s.client.Set(item)
- if err != nil {
- return err
- }
-
- if tags := opts.Tags; len(tags) > 0 {
- s.setTags(ctx, key, tags)
- }
-
- return nil
-}
-
-func (s *MemcacheStore) setTags(ctx context.Context, key any, tags []string) {
- group, ctx := errgroup.WithContext(ctx)
- for _, tag := range tags {
- currentTag := tag
- group.Go(func() error {
- tagKey := fmt.Sprintf(MemcacheTagPattern, currentTag)
-
- var err error
- for i := 0; i < 3; i++ {
- if err = s.addKeyToTagValue(tagKey, key); err == nil {
- return nil
- }
- // loop to retry any failure (including race conditions)
- }
-
- return err
- })
- }
-
- group.Wait()
-}
-
-func (s *MemcacheStore) addKeyToTagValue(tagKey string, key any) error {
- var (
- cacheKeys = []string{}
- result *memcache.Item
- err error
- )
-
- result, err = s.client.Get(tagKey)
- if err == nil {
- cacheKeys = strings.Split(string(result.Value), ",")
- } else if !errors.Is(err, memcache.ErrCacheMiss) {
- return err
- }
-
- for _, cacheKey := range cacheKeys {
- // if key already exists, nothing to do
- if cacheKey == key.(string) {
- return nil
- }
- }
-
- cacheKeys = append(cacheKeys, key.(string))
-
- newVal := []byte(strings.Join(cacheKeys, ","))
-
- if result == nil {
- // if key didnt exist, use Add to create only if still not there
- return s.client.Add(&memcache.Item{
- Key: tagKey,
- Value: newVal,
- Expiration: int32(TagKeyExpiry.Seconds()),
- })
- }
-
- // update existing value
- // using CompareAndSwap to ensure not to run over writes between Get and here
- result.Value = newVal
- result.Expiration = int32(TagKeyExpiry.Seconds())
- return s.client.CompareAndSwap(result)
-}
-
-// Delete removes data from Memcache for given key identifier
-func (s *MemcacheStore) Delete(_ context.Context, key any) error {
- return s.client.Delete(key.(string))
-}
-
-// Invalidate invalidates some cache data in Memcache for given options
-func (s *MemcacheStore) Invalidate(ctx context.Context, options ...lib_store.InvalidateOption) error {
- opts := lib_store.ApplyInvalidateOptions(options...)
-
- if tags := opts.Tags; len(tags) > 0 {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(MemcacheTagPattern, tag)
- result, err := s.Get(ctx, tagKey)
- if err != nil {
- return nil
- }
-
- cacheKeys := []string{}
- if bytes, ok := result.([]byte); ok {
- cacheKeys = strings.Split(string(bytes), ",")
- }
-
- for _, cacheKey := range cacheKeys {
- s.Delete(ctx, cacheKey)
- }
- }
- }
-
- return nil
-}
-
-// Clear resets all data in the store
-func (s *MemcacheStore) Clear(_ context.Context) error {
- return s.client.FlushAll()
-}
-
-// GetType returns the store type
-func (s *MemcacheStore) GetType() string {
- return MemcacheType
-}
diff --git a/toolkit/gocache/store/memcache/memcache_bench_test.go b/toolkit/gocache/store/memcache/memcache_bench_test.go
deleted file mode 100644
index cf8db474..00000000
--- a/toolkit/gocache/store/memcache/memcache_bench_test.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package memcache
-
-import (
- "context"
- "fmt"
- "math"
- "testing"
- "time"
-
- "github.com/bradfitz/gomemcache/memcache"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-func BenchmarkMemcacheSet(b *testing.B) {
- ctx := context.Background()
-
- store := NewMemcache(
- memcache.New("127.0.0.1:11211"),
- lib_store.WithExpiration(100*time.Second),
- )
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- key := fmt.Sprintf("test-%d", n)
- value := []byte(fmt.Sprintf("value-%d", n))
-
- store.Set(ctx, key, value, lib_store.WithTags([]string{fmt.Sprintf("tag-%d", n)}))
- }
- })
- }
-}
-
-func BenchmarkMemcacheGet(b *testing.B) {
- ctx := context.Background()
-
- store := NewMemcache(
- memcache.New("127.0.0.1:11211"),
- lib_store.WithExpiration(100*time.Second),
- )
-
- key := "test"
- value := []byte("value")
-
- store.Set(ctx, key, value, nil)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- _, _ = store.Get(ctx, key)
- }
- })
- }
-}
diff --git a/toolkit/gocache/store/memcache/memcache_mock.go b/toolkit/gocache/store/memcache/memcache_mock.go
deleted file mode 100644
index 55742280..00000000
--- a/toolkit/gocache/store/memcache/memcache_mock.go
+++ /dev/null
@@ -1,120 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: store/memcache/memcache.go
-
-// Package memcache is a generated GoMock package.
-package memcache
-
-import (
- reflect "reflect"
-
- memcache "github.com/bradfitz/gomemcache/memcache"
- gomock "go.uber.org/mock/gomock"
-)
-
-// MockMemcacheClientInterface is a mock of MemcacheClientInterface interface.
-type MockMemcacheClientInterface struct {
- ctrl *gomock.Controller
- recorder *MockMemcacheClientInterfaceMockRecorder
-}
-
-// MockMemcacheClientInterfaceMockRecorder is the mock recorder for MockMemcacheClientInterface.
-type MockMemcacheClientInterfaceMockRecorder struct {
- mock *MockMemcacheClientInterface
-}
-
-// NewMockMemcacheClientInterface creates a new mock instance.
-func NewMockMemcacheClientInterface(ctrl *gomock.Controller) *MockMemcacheClientInterface {
- mock := &MockMemcacheClientInterface{ctrl: ctrl}
- mock.recorder = &MockMemcacheClientInterfaceMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockMemcacheClientInterface) EXPECT() *MockMemcacheClientInterfaceMockRecorder {
- return m.recorder
-}
-
-// Add mocks base method.
-func (m *MockMemcacheClientInterface) Add(item *memcache.Item) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Add", item)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Add indicates an expected call of Add.
-func (mr *MockMemcacheClientInterfaceMockRecorder) Add(item interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Add", reflect.TypeOf((*MockMemcacheClientInterface)(nil).Add), item)
-}
-
-// CompareAndSwap mocks base method.
-func (m *MockMemcacheClientInterface) CompareAndSwap(item *memcache.Item) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "CompareAndSwap", item)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// CompareAndSwap indicates an expected call of CompareAndSwap.
-func (mr *MockMemcacheClientInterfaceMockRecorder) CompareAndSwap(item interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CompareAndSwap", reflect.TypeOf((*MockMemcacheClientInterface)(nil).CompareAndSwap), item)
-}
-
-// Delete mocks base method.
-func (m *MockMemcacheClientInterface) Delete(item string) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Delete", item)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Delete indicates an expected call of Delete.
-func (mr *MockMemcacheClientInterfaceMockRecorder) Delete(item interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Delete", reflect.TypeOf((*MockMemcacheClientInterface)(nil).Delete), item)
-}
-
-// FlushAll mocks base method.
-func (m *MockMemcacheClientInterface) FlushAll() error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "FlushAll")
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// FlushAll indicates an expected call of FlushAll.
-func (mr *MockMemcacheClientInterfaceMockRecorder) FlushAll() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushAll", reflect.TypeOf((*MockMemcacheClientInterface)(nil).FlushAll))
-}
-
-// Get mocks base method.
-func (m *MockMemcacheClientInterface) Get(key string) (*memcache.Item, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", key)
- ret0, _ := ret[0].(*memcache.Item)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockMemcacheClientInterfaceMockRecorder) Get(key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockMemcacheClientInterface)(nil).Get), key)
-}
-
-// Set mocks base method.
-func (m *MockMemcacheClientInterface) Set(item *memcache.Item) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Set", item)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Set indicates an expected call of Set.
-func (mr *MockMemcacheClientInterfaceMockRecorder) Set(item interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockMemcacheClientInterface)(nil).Set), item)
-}
diff --git a/toolkit/gocache/store/memcache/memcache_test.go b/toolkit/gocache/store/memcache/memcache_test.go
deleted file mode 100644
index 44ab92e4..00000000
--- a/toolkit/gocache/store/memcache/memcache_test.go
+++ /dev/null
@@ -1,417 +0,0 @@
-package memcache
-
-import (
- "context"
- "errors"
- "testing"
- "time"
-
- "github.com/bradfitz/gomemcache/memcache"
- "github.com/stretchr/testify/assert"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "go.uber.org/mock/gomock"
-)
-
-func TestNewMemcache(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockMemcacheClientInterface(ctrl)
-
- // When
- store := NewMemcache(client, lib_store.WithExpiration(3*time.Second))
-
- // Then
- assert.IsType(t, new(MemcacheStore), store)
- assert.Equal(t, client, store.client)
- assert.Equal(t, &lib_store.Options{Expiration: 3 * time.Second}, store.options)
-}
-
-func TestMemcacheGet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(&memcache.Item{
- Value: cacheValue,
- }, nil)
-
- store := NewMemcache(client, lib_store.WithExpiration(3*time.Second))
-
- // When
- value, err := store.Get(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-}
-
-func TestMemcacheGetWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- expectedErr := errors.New("an unexpected error occurred")
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(nil, expectedErr)
-
- store := NewMemcache(client, lib_store.WithExpiration(3*time.Second))
-
- // When
- value, err := store.Get(ctx, cacheKey)
-
- // Then
- assert.Equal(t, expectedErr, err)
- assert.Nil(t, value)
-}
-
-func TestMemcacheGetWithTTL(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(&memcache.Item{
- Value: cacheValue,
- Expiration: int32(5),
- }, nil)
-
- store := NewMemcache(client, lib_store.WithExpiration(3*time.Second))
-
- // When
- value, ttl, err := store.GetWithTTL(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
- assert.Equal(t, 5*time.Second, ttl)
-}
-
-func TestMemcacheGetWithTTLWhenMissingItem(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(nil, nil)
-
- store := NewMemcache(client, lib_store.WithExpiration(3*time.Second))
-
- // When
- value, ttl, err := store.GetWithTTL(ctx, cacheKey)
-
- // Then
- assert.NotNil(t, err)
- assert.Nil(t, value)
- assert.Equal(t, 0*time.Second, ttl)
-}
-
-func TestMemcacheGetWithTTLWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- expectedErr := errors.New("an unexpected error occurred")
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(nil, expectedErr)
-
- store := NewMemcache(client, lib_store.WithExpiration(3*time.Second))
-
- // When
- value, ttl, err := store.GetWithTTL(ctx, cacheKey)
-
- // Then
- assert.Equal(t, expectedErr, err)
- assert.Nil(t, value)
- assert.Equal(t, 0*time.Second, ttl)
-}
-
-func TestMemcacheSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Set(&memcache.Item{
- Key: cacheKey,
- Value: cacheValue,
- Expiration: int32(5),
- }).Return(nil)
-
- store := NewMemcache(client, lib_store.WithExpiration(3*time.Second))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithExpiration(5*time.Second))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestMemcacheSetWhenNoOptionsGiven(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Set(&memcache.Item{
- Key: cacheKey,
- Value: cacheValue,
- Expiration: int32(3),
- }).Return(nil)
-
- store := NewMemcache(client, lib_store.WithExpiration(3*time.Second))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestMemcacheSetWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- expectedErr := errors.New("an unexpected error occurred")
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Set(&memcache.Item{
- Key: cacheKey,
- Value: cacheValue,
- Expiration: int32(3),
- }).Return(expectedErr)
-
- store := NewMemcache(client, lib_store.WithExpiration(3*time.Second))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestMemcacheSetWithTags(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- tagKey := "gocache_tag_tag1"
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Set(gomock.Any()).AnyTimes().Return(nil)
- client.EXPECT().Get(tagKey).Return(nil, memcache.ErrCacheMiss)
- client.EXPECT().Add(&memcache.Item{
- Key: tagKey,
- Value: []byte(cacheKey),
- Expiration: int32(TagKeyExpiry.Seconds()),
- }).Return(nil)
-
- store := NewMemcache(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestMemcacheSetWithTagsWhenAlreadyInserted(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Set(gomock.Any()).AnyTimes().Return(nil)
- client.EXPECT().Get("gocache_tag_tag1").Return(&memcache.Item{
- Value: []byte("my-key,a-second-key"),
- }, nil)
-
- store := NewMemcache(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestMemcacheDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Delete(cacheKey).Return(nil)
-
- store := NewMemcache(client)
-
- // When
- err := store.Delete(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestMemcacheDeleteWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("unable to delete key")
-
- cacheKey := "my-key"
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Delete(cacheKey).Return(expectedErr)
-
- store := NewMemcache(client)
-
- // When
- err := store.Delete(ctx, cacheKey)
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestMemcacheInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := &memcache.Item{
- Value: []byte("a23fdf987h2svc23,jHG2372x38hf74"),
- }
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Get("gocache_tag_tag1").Return(cacheKeys, nil)
- client.EXPECT().Delete("a23fdf987h2svc23").Return(nil)
- client.EXPECT().Delete("jHG2372x38hf74").Return(nil)
-
- store := NewMemcache(client)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestMemcacheInvalidateWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := &memcache.Item{
- Value: []byte("a23fdf987h2svc23,jHG2372x38hf74"),
- }
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().Get("gocache_tag_tag1").Return(cacheKeys, nil)
- client.EXPECT().Delete("a23fdf987h2svc23").Return(errors.New("unexpected error"))
- client.EXPECT().Delete("jHG2372x38hf74").Return(nil)
-
- store := NewMemcache(client)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestMemcacheClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().FlushAll().Return(nil)
-
- store := NewMemcache(client)
-
- // When
- err := store.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestMemcacheClearWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- expectedErr := errors.New("an unexpected error occurred")
-
- client := NewMockMemcacheClientInterface(ctrl)
- client.EXPECT().FlushAll().Return(expectedErr)
-
- store := NewMemcache(client)
-
- // When
- err := store.Clear(ctx)
-
- // Then
- assert.Equal(t, expectedErr, err)
-}
-
-func TestMemcacheGetType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockMemcacheClientInterface(ctrl)
-
- store := NewMemcache(client)
-
- // When - Then
- assert.Equal(t, MemcacheType, store.GetType())
-}
diff --git a/toolkit/gocache/store/pegasus/pegasus.go b/toolkit/gocache/store/pegasus/pegasus.go
deleted file mode 100644
index 1e54f175..00000000
--- a/toolkit/gocache/store/pegasus/pegasus.go
+++ /dev/null
@@ -1,305 +0,0 @@
-package pegasus
-
-import (
- "context"
- "errors"
- "fmt"
- "strings"
- "time"
-
- "github.com/XiaoMi/pegasus-go-client/admin"
- "github.com/XiaoMi/pegasus-go-client/pegasus"
- "github.com/spf13/cast"
-
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-const (
- // PegasusType represents the storage type as a string value
- PegasusType = "pegasus"
- // PegasusTagPattern represents the tag pattern to be used as a key in specified storage
- PegasusTagPattern = "gocache_tag_%s"
- // Pegasus ttl(time-to-live) in seconds: -1 if ttl is not set; -2 if entry doesn't exist
- PegasusNOTTL = -1
- PegasusNOENTRY = -2
-
- DefaultTable = "gocache_pegasus"
- DefaultTablePartitionNum = 4
- DefaultScanNum = 100
-)
-
-// empty represent empty sort key, more info reference: https://github.com/XiaoMi/pegasus-go-client/blob/f3b6b08bc4c227982bb5b73106329435fda97a38/pegasus/table_connector.go#L83
-var empty = []byte("-")
-
-// OptionsPegasus is options of Pegasus
-type OptionsPegasus struct {
- *lib_store.Options
- MetaServers []string
-
- TableName string
- TablePartitionNum int
- TableScanNum int
-}
-
-// PegasusStore is a store for Pegasus
-type PegasusStore struct {
- client pegasus.Client
- options *OptionsPegasus
-}
-
-// NewPegasus creates a new store to pegasus instance(s)
-func NewPegasus(ctx context.Context, options *OptionsPegasus) (*PegasusStore, error) {
- if options == nil {
- options = &OptionsPegasus{}
- }
-
- if err := createTable(ctx, options); err != nil {
- return nil, err
- }
-
- client := pegasus.NewClient(pegasus.Config{
- MetaServers: options.MetaServers,
- })
- table, err := client.OpenTable(ctx, options.TableName)
- if err != nil {
- return nil, err
- }
- defer table.Close()
-
- return &PegasusStore{
- client: client,
- options: options,
- }, nil
-}
-
-// validateOptions validate pegasus options
-func validateOptions(options *OptionsPegasus) error {
- if len(options.MetaServers) == 0 {
- return errors.New("pegasus meta servers must fill")
- }
- if len(options.TableName) == 0 {
- options.TableName = DefaultTable
- }
- if options.TablePartitionNum < 1 {
- options.TablePartitionNum = DefaultTablePartitionNum
- }
- if options.TableScanNum < 1 {
- options.TableScanNum = DefaultScanNum
- }
-
- return nil
-}
-
-// createTable for create table by options
-func createTable(ctx context.Context, options *OptionsPegasus) error {
- if err := validateOptions(options); err != nil {
- return err
- }
-
- tableClient := admin.NewClient(admin.Config{MetaServers: options.MetaServers})
- tableList, err := tableClient.ListTables(ctx)
- if err != nil {
- return err
- }
-
- for i := range tableList {
- if tableList[i].Name == options.TableName {
- return nil
- }
- }
-
- // if not found then create table of options
- return tableClient.CreateTable(ctx, options.TableName, options.TablePartitionNum)
-}
-
-// dropTable for drop table
-func dropTable(ctx context.Context, options *OptionsPegasus) error {
- if err := validateOptions(options); err != nil {
- return err
- }
-
- tableClient := admin.NewClient(admin.Config{MetaServers: options.MetaServers})
- return tableClient.DropTable(ctx, options.TableName)
-}
-
-// Close when exit store
-func (p *PegasusStore) Close() error {
- return p.client.Close()
-}
-
-// Get returns data stored from a given key
-func (p *PegasusStore) Get(ctx context.Context, key any) (any, error) {
- table, err := p.client.OpenTable(ctx, p.options.TableName)
- if err != nil {
- return nil, err
- }
- defer table.Close()
-
- value, err := table.Get(ctx, []byte(cast.ToString(key)), empty)
- if err != nil {
- return nil, err
- }
- if value == nil {
- return nil, &lib_store.NotFound{}
- }
- return value, nil
-}
-
-// GetWithTTL returns data stored from a given key and its corresponding TTL
-func (p *PegasusStore) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
- table, err := p.client.OpenTable(ctx, p.options.TableName)
- if err != nil {
- return nil, 0, err
- }
- defer table.Close()
-
- value, err := table.Get(ctx, []byte(cast.ToString(key)), empty)
- if err != nil {
- return nil, 0, err
- }
- if value == nil {
- return nil, 0, &lib_store.NotFound{}
- }
-
- ttl, err := table.TTL(ctx, []byte(cast.ToString(key)), empty)
- if err != nil {
- return nil, 0, err
- }
-
- return value, time.Duration(ttl) * time.Second, nil
-}
-
-// Set defines data in Pegasus for given key identifier
-func (p *PegasusStore) Set(ctx context.Context, key, value any, options ...lib_store.Option) error {
- opts := lib_store.ApplyOptions(options...)
-
- table, err := p.client.OpenTable(ctx, p.options.TableName)
- if err != nil {
- return err
- }
- defer table.Close()
-
- err = table.SetTTL(ctx, []byte(cast.ToString(key)), empty, []byte(cast.ToString(value)), opts.Expiration)
- if err != nil {
- return err
- }
-
- if tags := opts.Tags; len(tags) > 0 {
- if err = p.setTags(ctx, key, tags); err != nil {
- return err
- }
- }
- return nil
-}
-
-func (p *PegasusStore) setTags(ctx context.Context, key any, tags []string) error {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(PegasusTagPattern, tag)
- cacheKeys := []string{}
-
- if result, err := p.Get(ctx, tagKey); err == nil {
- if bytes, ok := result.([]byte); ok {
- cacheKeys = strings.Split(string(bytes), ",")
- }
- }
-
- alreadyInserted := false
- for _, cacheKey := range cacheKeys {
- if cacheKey == key.(string) {
- alreadyInserted = true
- break
- }
- }
-
- if !alreadyInserted {
- cacheKeys = append(cacheKeys, key.(string))
- }
-
- if err := p.Set(ctx, tagKey, []byte(strings.Join(cacheKeys, ",")), lib_store.WithExpiration(720*time.Hour)); err != nil {
- return err
- }
- }
-
- return nil
-}
-
-// Delete removes data from Pegasus for given key identifier
-func (p *PegasusStore) Delete(ctx context.Context, key any) error {
- table, err := p.client.OpenTable(ctx, p.options.TableName)
- if err != nil {
- return err
- }
- defer table.Close()
-
- return table.Del(ctx, []byte(cast.ToString(key)), empty)
-}
-
-// Invalidate invalidates some cache data in Pegasus for given options
-func (p *PegasusStore) Invalidate(ctx context.Context, options ...lib_store.InvalidateOption) error {
- opts := lib_store.ApplyInvalidateOptions(options...)
- if tags := opts.Tags; len(tags) > 0 {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(PegasusTagPattern, tag)
- result, err := p.Get(ctx, tagKey)
- if err != nil {
- return nil
- }
-
- cacheKeys := []string{}
- if bytes, ok := result.([]byte); ok {
- cacheKeys = strings.Split(string(bytes), ",")
- }
-
- for _, cacheKey := range cacheKeys {
- if err := p.Delete(ctx, cacheKey); err != nil {
- return err
- }
- }
- }
- }
-
- return nil
-}
-
-// Clear resets all data in the store
-func (p *PegasusStore) Clear(ctx context.Context) error {
- table, err := p.client.OpenTable(ctx, p.options.TableName)
- if err != nil {
- return err
- }
- defer table.Close()
-
- // init full scan
- scanners, err := table.GetUnorderedScanners(ctx, p.options.TablePartitionNum, &pegasus.ScannerOptions{
- BatchSize: p.options.TableScanNum,
- // Values can be optimized out during scanning to reduce the workload.
- NoValue: true,
- })
- if err != nil {
- return err
- }
-
- // full scan and delete
- for _, scanner := range scanners {
- // Iterates sequentially.
- for {
- completed, hashKey, _, _, err := scanner.Next(ctx)
- if err != nil {
- return err
- }
- if completed {
- break
- }
- err = p.Delete(ctx, hashKey)
- if err != nil {
- return err
- }
- }
- }
- return nil
-}
-
-// GetType returns the store type
-func (p *PegasusStore) GetType() string {
- return PegasusType
-}
diff --git a/toolkit/gocache/store/pegasus/pegasus_bench_test.go b/toolkit/gocache/store/pegasus/pegasus_bench_test.go
deleted file mode 100644
index 5996463a..00000000
--- a/toolkit/gocache/store/pegasus/pegasus_bench_test.go
+++ /dev/null
@@ -1,51 +0,0 @@
-package pegasus
-
-import (
- "context"
- "fmt"
- "math"
- "testing"
-
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-// run go test -bench='BenchmarkPegasusStore*' -benchtime=1s -count=1 -run=none
-func BenchmarkPegasusStore_Set(b *testing.B) {
- ctx := context.Background()
-
- p, _ := NewPegasus(ctx, testPegasusOptions())
- defer p.Close()
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- key := fmt.Sprintf("test-%d", n)
- value := []byte(fmt.Sprintf("value-%d", n))
-
- p.Set(ctx, key, value, lib_store.WithTags([]string{fmt.Sprintf("tag-%d", n)}))
- }
- })
- }
-}
-
-func BenchmarkPegasusStore_Get(b *testing.B) {
- ctx := context.Background()
-
- p, _ := NewPegasus(ctx, testPegasusOptions())
- defer p.Close()
-
- key := "test"
- value := []byte("value")
-
- p.Set(ctx, key, value, nil)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- _, _ = p.Get(ctx, key)
- }
- })
- }
-}
diff --git a/toolkit/gocache/store/pegasus/pegasus_test.go b/toolkit/gocache/store/pegasus/pegasus_test.go
deleted file mode 100644
index 9a59b91b..00000000
--- a/toolkit/gocache/store/pegasus/pegasus_test.go
+++ /dev/null
@@ -1,219 +0,0 @@
-package pegasus
-
-import (
- "context"
- "testing"
- "time"
-
- "github.com/smartystreets/assertions/should"
- . "github.com/smartystreets/goconvey/convey"
- "github.com/spf13/cast"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-// run go test -run='TestPegasus*' -race -cover -coverprofile=coverage.txt -covermode=atomic -v ./...
-// install local pegasus onebox reference https://pegasus.apache.org/en/docs/build/compile-from-source/
-func testPegasusOptions() *OptionsPegasus {
- return &OptionsPegasus{
- MetaServers: []string{"127.0.0.1:34601", "127.0.0.1:34602", "127.0.0.1:34603"},
- TableName: "test_pegasus_table",
- TablePartitionNum: 1,
- }
-}
-
-func skipPegasusTest(t *testing.T) {
- t.Skip("Pegasus Test skipping because local pegasus onebox not install!")
-}
-
-func TestNewPegasus(t *testing.T) {
- Convey("Pegasus TestNewPegasus should return client and nil error", t, func() {
- skipPegasusTest(t)
-
- ctx := context.Background()
-
- p, err := NewPegasus(ctx, testPegasusOptions())
- So(err, ShouldBeNil)
- defer p.Close()
- })
-}
-
-func Test_validateOptions(t *testing.T) {
- Convey("Pegasus Test validateOptions", t, func() {
- Convey("Test nil validateOptions", func() {
- So(validateOptions(&OptionsPegasus{}), ShouldNotBeNil)
- })
- Convey("Test correct validateOptions", func() {
- So(validateOptions(testPegasusOptions()), ShouldBeNil)
- })
- })
-}
-
-func Test_createTable(t *testing.T) {
- Convey("Pegasus Test createTable should return nil", t, func() {
- skipPegasusTest(t)
-
- ctx := context.Background()
-
- err := createTable(ctx, testPegasusOptions())
- So(err, ShouldBeNil)
- })
-}
-
-func Test_dropTable(t *testing.T) {
- Convey("Pegasus Test dropTable should return nil", t, func() {
- skipPegasusTest(t)
-
- ctx := context.Background()
-
- err := dropTable(ctx, testPegasusOptions())
- So(err, ShouldBeNil)
- })
-}
-
-func TestPegasusStore_Close(t *testing.T) {
- Convey("Pegasus TestClose for pegasus store", t, func() {
- skipPegasusTest(t)
-
- ctx := context.Background()
-
- p, _ := NewPegasus(ctx, testPegasusOptions())
- So(p.Close(), ShouldBeNil)
- })
-}
-
-func TestPegasusStore_Get(t *testing.T) {
- Convey("Pegasus TestGet for pegasus store", t, func() {
- skipPegasusTest(t)
-
- ctx := context.Background()
-
- p, _ := NewPegasus(ctx, testPegasusOptions())
- defer p.Close()
-
- k, v := "test-gocache-key", "test-gocache-value"
- p.Set(ctx, k, v)
- value, err := p.Get(ctx, k)
- So(cast.ToString(value), ShouldEqual, v)
- So(err, ShouldBeNil)
- })
-}
-
-func TestPegasusStore_GetWithTTL(t *testing.T) {
- Convey("Pegasus TestGetWithTTL for pegasus store", t, func() {
- skipPegasusTest(t)
-
- ctx := context.Background()
-
- p, _ := NewPegasus(ctx, testPegasusOptions())
- defer p.Close()
-
- Convey("test set ttl that not achieve", func() {
- k, v, retention := "test-gocache-key-01", "test-gocache-value", time.Minute*10
- p.Set(ctx, k, v, lib_store.WithExpiration(retention))
-
- value, ttl, err := p.GetWithTTL(ctx, k)
- So(cast.ToString(value), ShouldEqual, v)
- So(ttl, should.BeLessThanOrEqualTo, retention)
- So(err, ShouldBeNil)
- })
- Convey("test no ttl", func() {
- k, v := "test-gocache-key-02", "test-gocache-value"
- p.Set(ctx, k, v)
-
- value, ttl, err := p.GetWithTTL(ctx, k)
- So(cast.ToString(value), ShouldEqual, v)
- So(ttl, should.BeLessThanOrEqualTo, PegasusNOTTL)
- So(err, ShouldBeNil)
- })
- Convey("test set ttl that already achieve", func() {
- k, v, retention := "test-gocache-key-03", "test-gocache-value", time.Millisecond*10
- p.Set(ctx, k, v, lib_store.WithExpiration(retention))
- time.Sleep(time.Second * 1)
-
- value, ttl, err := p.GetWithTTL(ctx, k)
- So(cast.ToString(value), ShouldBeEmpty)
- So(ttl, should.BeLessThanOrEqualTo, PegasusNOENTRY)
- So(err, ShouldBeNil)
- })
- })
-}
-
-func TestPegasusStore_Set(t *testing.T) {
- Convey("Pegasus TestSet for pegasus store", t, func() {
- skipPegasusTest(t)
-
- ctx := context.Background()
-
- p, _ := NewPegasus(ctx, testPegasusOptions())
- defer p.Close()
-
- k, v := "test-gocache-key", "test-gocache-value"
- err := p.Set(ctx, k, v)
- So(err, ShouldBeNil)
- })
-}
-
-func TestPegasusStore_setTags(t *testing.T) {
- Convey("Pegasus Test set tags for pegasus store", t, func() {
- skipPegasusTest(t)
-
- ctx := context.Background()
-
- p, _ := NewPegasus(ctx, testPegasusOptions())
- defer p.Close()
-
- k, tags := "test-gocache-tags-key", []string{"test01", "test02"}
- err := p.setTags(ctx, k, tags)
- So(err, ShouldBeNil)
- })
-}
-
-func TestPegasusStore_Delete(t *testing.T) {
- Convey("Pegasus TestDelete for pegasus store", t, func() {
- skipPegasusTest(t)
-
- ctx := context.Background()
-
- p, _ := NewPegasus(ctx, testPegasusOptions())
- defer p.Close()
-
- k, v := "test-gocache-key", "test-gocache-value"
- p.Set(ctx, k, v)
-
- err := p.Delete(ctx, k)
- So(err, ShouldBeNil)
- })
-}
-
-func TestPegasusStore_Invalidate(t *testing.T) {
- Convey("Pegasus TestInvalidate for pegasus store", t, func() {
- skipPegasusTest(t)
-
- ctx := context.Background()
-
- p, _ := NewPegasus(ctx, testPegasusOptions())
- defer p.Close()
-
- err := p.Invalidate(ctx)
- So(err, ShouldBeNil)
- })
-}
-
-func TestPegasusStore_Clear(t *testing.T) {
- Convey("Pegasus TestClear for pegasus store", t, func() {
- skipPegasusTest(t)
-
- ctx := context.Background()
-
- p, _ := NewPegasus(ctx, testPegasusOptions())
- defer p.Close()
-
- k1, v1 := "test-gocache-key-01", "test-gocache-value"
- k2, v2 := "test-gocache-key-01", "test-gocache-value"
- p.Set(ctx, k1, v1)
- p.Set(ctx, k2, v2)
-
- err := p.Clear(ctx)
- So(err, ShouldBeNil)
- })
-}
diff --git a/toolkit/gocache/store/redis/redis.go b/toolkit/gocache/store/redis/redis.go
deleted file mode 100644
index e4c5716d..00000000
--- a/toolkit/gocache/store/redis/redis.go
+++ /dev/null
@@ -1,140 +0,0 @@
-package redis
-
-import (
- "context"
- "fmt"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
-
- redis "github.com/redis/go-redis/v9"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-// RedisClientInterface represents a go-redis/redis client
-type RedisClientInterface interface {
- Get(ctx context.Context, key string) *redis.StringCmd
- TTL(ctx context.Context, key string) *redis.DurationCmd
- Expire(ctx context.Context, key string, expiration time.Duration) *redis.BoolCmd
- Set(ctx context.Context, key string, values any, expiration time.Duration) *redis.StatusCmd
- Del(ctx context.Context, keys ...string) *redis.IntCmd
- FlushAll(ctx context.Context) *redis.StatusCmd
- SAdd(ctx context.Context, key string, members ...any) *redis.IntCmd
- SMembers(ctx context.Context, key string) *redis.StringSliceCmd
-}
-
-const (
- // RedisType represents the storage type as a string value
- RedisType = "redis"
- // RedisTagPattern represents the tag pattern to be used as a key in specified storage
- RedisTagPattern = "gocache_tag_%s"
-)
-
-// RedisStore is a store for Redis
-type RedisStore struct {
- client RedisClientInterface
- options *lib_store.Options
-}
-
-// NewRedis creates a new store to Redis instance(s)
-func NewRedis(client RedisClientInterface, options ...lib_store.Option) *RedisStore {
- return &RedisStore{
- client: client,
- options: lib_store.ApplyOptions(options...),
- }
-}
-
-// Get returns data stored from a given key
-func (s *RedisStore) Get(ctx context.Context, key any) (any, error) {
- object, err := s.client.Get(ctx, key.(string)).Result()
- if err == redis.Nil {
- return nil, lib_store.NotFoundWithCause(err)
- }
- return object, err
-}
-
-// GetWithTTL returns data stored from a given key and its corresponding TTL
-func (s *RedisStore) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
- object, err := s.client.Get(ctx, key.(string)).Result()
- if err == redis.Nil {
- return nil, 0, lib_store.NotFoundWithCause(err)
- }
- if err != nil {
- return nil, 0, err
- }
-
- ttl, err := s.client.TTL(ctx, key.(string)).Result()
- if err != nil {
- return nil, 0, err
- }
-
- return object, ttl, err
-}
-
-// Set defines data in Redis for given key identifier
-func (s *RedisStore) Set(ctx context.Context, key any, value any, options ...lib_store.Option) error {
- opts := lib_store.ApplyOptionsWithDefault(s.options, options...)
-
- err := s.client.Set(ctx, key.(string), value, opts.Expiration).Err()
- if err != nil {
- return err
- }
-
- if tags := opts.Tags; len(tags) > 0 {
- s.setTags(ctx, key, tags)
- }
-
- return nil
-}
-
-func (s *RedisStore) setTags(ctx context.Context, key any, tags []string) {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(RedisTagPattern, tag)
- s.client.SAdd(ctx, tagKey, key.(string))
- s.client.Expire(ctx, tagKey, 720*time.Hour)
- }
-}
-
-// Delete removes data from Redis for given key identifier
-func (s *RedisStore) Delete(ctx context.Context, key any) error {
- _, err := s.client.Del(ctx, key.(string)).Result()
- return err
-}
-
-// Invalidate invalidates some cache data in Redis for given options
-func (s *RedisStore) Invalidate(ctx context.Context, options ...lib_store.InvalidateOption) error {
- opts := lib_store.ApplyInvalidateOptions(options...)
-
- if tags := opts.Tags; len(tags) > 0 {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(RedisTagPattern, tag)
- cacheKeys, err := s.client.SMembers(ctx, tagKey).Result()
- if err != nil {
- zlogger.Warn().Msg(err.Error())
- continue
- }
-
- for _, cacheKey := range cacheKeys {
- s.Delete(ctx, cacheKey)
- }
-
- s.Delete(ctx, tagKey)
- }
- }
-
- return nil
-}
-
-// GetType returns the store type
-func (s *RedisStore) GetType() string {
- return RedisType
-}
-
-// Clear resets all data in the store
-func (s *RedisStore) Clear(ctx context.Context) error {
- if err := s.client.FlushAll(ctx).Err(); err != nil {
- return err
- }
-
- return nil
-}
diff --git a/toolkit/gocache/store/redis/redis_bench_test.go b/toolkit/gocache/store/redis/redis_bench_test.go
deleted file mode 100644
index ed17758d..00000000
--- a/toolkit/gocache/store/redis/redis_bench_test.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package redis
-
-import (
- "context"
- "fmt"
- "math"
- "testing"
-
- redis "github.com/redis/go-redis/v9"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-func BenchmarkRedisSet(b *testing.B) {
- ctx := context.Background()
-
- store := NewRedis(redis.NewClient(&redis.Options{
- Addr: "localhost:6379",
- }))
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- key := fmt.Sprintf("test-%d", n)
- value := []byte(fmt.Sprintf("value-%d", n))
-
- store.Set(ctx, key, value, lib_store.WithTags([]string{fmt.Sprintf("tag-%d", n)}))
- }
- })
- }
-}
-
-func BenchmarkRedisGet(b *testing.B) {
- ctx := context.Background()
-
- store := NewRedis(redis.NewClient(&redis.Options{
- Addr: "localhost:6379",
- }))
-
- key := "test"
- value := []byte("value")
-
- store.Set(ctx, key, value)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- _, _ = store.Get(ctx, key)
- }
- })
- }
-}
diff --git a/toolkit/gocache/store/redis/redis_mock.go b/toolkit/gocache/store/redis/redis_mock.go
deleted file mode 100644
index fc103f45..00000000
--- a/toolkit/gocache/store/redis/redis_mock.go
+++ /dev/null
@@ -1,159 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: store/redis/redis.go
-
-// Package redis is a generated GoMock package.
-package redis
-
-import (
- context "context"
- reflect "reflect"
- time "time"
-
- v9 "github.com/redis/go-redis/v9"
- gomock "go.uber.org/mock/gomock"
-)
-
-// MockRedisClientInterface is a mock of RedisClientInterface interface.
-type MockRedisClientInterface struct {
- ctrl *gomock.Controller
- recorder *MockRedisClientInterfaceMockRecorder
-}
-
-// MockRedisClientInterfaceMockRecorder is the mock recorder for MockRedisClientInterface.
-type MockRedisClientInterfaceMockRecorder struct {
- mock *MockRedisClientInterface
-}
-
-// NewMockRedisClientInterface creates a new mock instance.
-func NewMockRedisClientInterface(ctrl *gomock.Controller) *MockRedisClientInterface {
- mock := &MockRedisClientInterface{ctrl: ctrl}
- mock.recorder = &MockRedisClientInterfaceMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockRedisClientInterface) EXPECT() *MockRedisClientInterfaceMockRecorder {
- return m.recorder
-}
-
-// Del mocks base method.
-func (m *MockRedisClientInterface) Del(ctx context.Context, keys ...string) *v9.IntCmd {
- m.ctrl.T.Helper()
- varargs := []interface{}{ctx}
- for _, a := range keys {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "Del", varargs...)
- ret0, _ := ret[0].(*v9.IntCmd)
- return ret0
-}
-
-// Del indicates an expected call of Del.
-func (mr *MockRedisClientInterfaceMockRecorder) Del(ctx interface{}, keys ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{ctx}, keys...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Del", reflect.TypeOf((*MockRedisClientInterface)(nil).Del), varargs...)
-}
-
-// Expire mocks base method.
-func (m *MockRedisClientInterface) Expire(ctx context.Context, key string, expiration time.Duration) *v9.BoolCmd {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Expire", ctx, key, expiration)
- ret0, _ := ret[0].(*v9.BoolCmd)
- return ret0
-}
-
-// Expire indicates an expected call of Expire.
-func (mr *MockRedisClientInterfaceMockRecorder) Expire(ctx, key, expiration interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Expire", reflect.TypeOf((*MockRedisClientInterface)(nil).Expire), ctx, key, expiration)
-}
-
-// FlushAll mocks base method.
-func (m *MockRedisClientInterface) FlushAll(ctx context.Context) *v9.StatusCmd {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "FlushAll", ctx)
- ret0, _ := ret[0].(*v9.StatusCmd)
- return ret0
-}
-
-// FlushAll indicates an expected call of FlushAll.
-func (mr *MockRedisClientInterfaceMockRecorder) FlushAll(ctx interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushAll", reflect.TypeOf((*MockRedisClientInterface)(nil).FlushAll), ctx)
-}
-
-// Get mocks base method.
-func (m *MockRedisClientInterface) Get(ctx context.Context, key string) *v9.StringCmd {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", ctx, key)
- ret0, _ := ret[0].(*v9.StringCmd)
- return ret0
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockRedisClientInterfaceMockRecorder) Get(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRedisClientInterface)(nil).Get), ctx, key)
-}
-
-// SAdd mocks base method.
-func (m *MockRedisClientInterface) SAdd(ctx context.Context, key string, members ...any) *v9.IntCmd {
- m.ctrl.T.Helper()
- varargs := []interface{}{ctx, key}
- for _, a := range members {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "SAdd", varargs...)
- ret0, _ := ret[0].(*v9.IntCmd)
- return ret0
-}
-
-// SAdd indicates an expected call of SAdd.
-func (mr *MockRedisClientInterfaceMockRecorder) SAdd(ctx, key interface{}, members ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{ctx, key}, members...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SAdd", reflect.TypeOf((*MockRedisClientInterface)(nil).SAdd), varargs...)
-}
-
-// SMembers mocks base method.
-func (m *MockRedisClientInterface) SMembers(ctx context.Context, key string) *v9.StringSliceCmd {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "SMembers", ctx, key)
- ret0, _ := ret[0].(*v9.StringSliceCmd)
- return ret0
-}
-
-// SMembers indicates an expected call of SMembers.
-func (mr *MockRedisClientInterfaceMockRecorder) SMembers(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMembers", reflect.TypeOf((*MockRedisClientInterface)(nil).SMembers), ctx, key)
-}
-
-// Set mocks base method.
-func (m *MockRedisClientInterface) Set(ctx context.Context, key string, values any, expiration time.Duration) *v9.StatusCmd {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Set", ctx, key, values, expiration)
- ret0, _ := ret[0].(*v9.StatusCmd)
- return ret0
-}
-
-// Set indicates an expected call of Set.
-func (mr *MockRedisClientInterfaceMockRecorder) Set(ctx, key, values, expiration interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockRedisClientInterface)(nil).Set), ctx, key, values, expiration)
-}
-
-// TTL mocks base method.
-func (m *MockRedisClientInterface) TTL(ctx context.Context, key string) *v9.DurationCmd {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "TTL", ctx, key)
- ret0, _ := ret[0].(*v9.DurationCmd)
- return ret0
-}
-
-// TTL indicates an expected call of TTL.
-func (mr *MockRedisClientInterfaceMockRecorder) TTL(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TTL", reflect.TypeOf((*MockRedisClientInterface)(nil).TTL), ctx, key)
-}
diff --git a/toolkit/gocache/store/redis/redis_test.go b/toolkit/gocache/store/redis/redis_test.go
deleted file mode 100644
index 725b847a..00000000
--- a/toolkit/gocache/store/redis/redis_test.go
+++ /dev/null
@@ -1,183 +0,0 @@
-package redis
-
-import (
- "context"
- "testing"
- "time"
-
- redis "github.com/redis/go-redis/v9"
- "github.com/stretchr/testify/assert"
- "go.uber.org/mock/gomock"
-
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-func TestNewRedis(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockRedisClientInterface(ctrl)
-
- // When
- store := NewRedis(client, lib_store.WithExpiration(6*time.Second))
-
- // Then
- assert.IsType(t, new(RedisStore), store)
- assert.Equal(t, client, store.client)
- assert.Equal(t, &lib_store.Options{Expiration: 6 * time.Second}, store.options)
-}
-
-func TestRedisGet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockRedisClientInterface(ctrl)
- client.EXPECT().Get(ctx, "my-key").Return(&redis.StringCmd{})
-
- store := NewRedis(client)
-
- // When
- value, err := store.Get(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
- assert.NotNil(t, value)
-}
-
-func TestRedisSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockRedisClientInterface(ctrl)
- client.EXPECT().Set(ctx, "my-key", cacheValue, 5*time.Second).Return(&redis.StatusCmd{})
-
- store := NewRedis(client, lib_store.WithExpiration(6*time.Second))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithExpiration(5*time.Second))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisSetWhenNoOptionsGiven(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockRedisClientInterface(ctrl)
- client.EXPECT().Set(ctx, "my-key", cacheValue, 6*time.Second).Return(&redis.StatusCmd{})
-
- store := NewRedis(client, lib_store.WithExpiration(6*time.Second))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisSetWithTags(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockRedisClientInterface(ctrl)
- client.EXPECT().Set(ctx, cacheKey, cacheValue, time.Duration(0)).Return(&redis.StatusCmd{})
- client.EXPECT().SAdd(ctx, "gocache_tag_tag1", "my-key").Return(&redis.IntCmd{})
- client.EXPECT().Expire(ctx, "gocache_tag_tag1", 720*time.Hour).Return(&redis.BoolCmd{})
-
- store := NewRedis(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := NewMockRedisClientInterface(ctrl)
- client.EXPECT().Del(ctx, "my-key").Return(&redis.IntCmd{})
-
- store := NewRedis(client)
-
- // When
- err := store.Delete(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := &redis.StringSliceCmd{}
-
- client := NewMockRedisClientInterface(ctrl)
- client.EXPECT().SMembers(ctx, "gocache_tag_tag1").Return(cacheKeys)
- client.EXPECT().Del(ctx, "gocache_tag_tag1").Return(&redis.IntCmd{})
-
- store := NewRedis(client)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockRedisClientInterface(ctrl)
- client.EXPECT().FlushAll(ctx).Return(&redis.StatusCmd{})
-
- store := NewRedis(client)
-
- // When
- err := store.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisGetType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockRedisClientInterface(ctrl)
-
- store := NewRedis(client)
-
- // When - Then
- assert.Equal(t, RedisType, store.GetType())
-}
diff --git a/toolkit/gocache/store/rediscluster/rediscluster.go b/toolkit/gocache/store/rediscluster/rediscluster.go
deleted file mode 100644
index 9e078cbe..00000000
--- a/toolkit/gocache/store/rediscluster/rediscluster.go
+++ /dev/null
@@ -1,137 +0,0 @@
-package rediscluster
-
-import (
- "context"
- "fmt"
- "time"
-
- redis "github.com/redis/go-redis/v9"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-// RedisClusterClientInterface represents a go-redis/redis clusclient
-type RedisClusterClientInterface interface {
- Get(ctx context.Context, key string) *redis.StringCmd
- TTL(ctx context.Context, key string) *redis.DurationCmd
- Expire(ctx context.Context, key string, expiration time.Duration) *redis.BoolCmd
- Set(ctx context.Context, key string, values any, expiration time.Duration) *redis.StatusCmd
- Del(ctx context.Context, keys ...string) *redis.IntCmd
- FlushAll(ctx context.Context) *redis.StatusCmd
- SAdd(ctx context.Context, key string, members ...any) *redis.IntCmd
- SMembers(ctx context.Context, key string) *redis.StringSliceCmd
-}
-
-const (
- // RedisType represents the storage type as a string value
- RedisClusterType = "rediscluster"
- // RedisTagPattern represents the tag pattern to be used as a key in specified storage
- RedisClusterTagPattern = "gocache_tag_%s"
-)
-
-// RedisStore is a store for Redis
-type RedisClusterStore struct {
- clusclient RedisClusterClientInterface
- options *lib_store.Options
-}
-
-// NewRedis creates a new store to Redis instance(s)
-func NewRedisCluster(client RedisClusterClientInterface, options ...lib_store.Option) *RedisClusterStore {
- return &RedisClusterStore{
- clusclient: client,
- options: lib_store.ApplyOptions(options...),
- }
-}
-
-// Get returns data stored from a given key
-func (s *RedisClusterStore) Get(ctx context.Context, key any) (any, error) {
- object, err := s.clusclient.Get(ctx, key.(string)).Result()
- if err == redis.Nil {
- return nil, lib_store.NotFoundWithCause(err)
- }
- return object, err
-}
-
-// GetWithTTL returns data stored from a given key and its corresponding TTL
-func (s *RedisClusterStore) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
- object, err := s.clusclient.Get(ctx, key.(string)).Result()
- if err == redis.Nil {
- return nil, 0, lib_store.NotFoundWithCause(err)
- }
- if err != nil {
- return nil, 0, err
- }
-
- ttl, err := s.clusclient.TTL(ctx, key.(string)).Result()
- if err != nil {
- return nil, 0, err
- }
-
- return object, ttl, err
-}
-
-// Set defines data in Redis for given key identifier
-func (s *RedisClusterStore) Set(ctx context.Context, key any, value any, options ...lib_store.Option) error {
- opts := lib_store.ApplyOptionsWithDefault(s.options, options...)
-
- err := s.clusclient.Set(ctx, key.(string), value, opts.Expiration).Err()
- if err != nil {
- return err
- }
-
- if tags := opts.Tags; len(tags) > 0 {
- s.setTags(ctx, key, tags)
- }
-
- return nil
-}
-
-func (s *RedisClusterStore) setTags(ctx context.Context, key any, tags []string) {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(RedisClusterTagPattern, tag)
- s.clusclient.SAdd(ctx, tagKey, key.(string))
- s.clusclient.Expire(ctx, tagKey, 720*time.Hour)
- }
-}
-
-// Delete removes data from Redis for given key identifier
-func (s *RedisClusterStore) Delete(ctx context.Context, key any) error {
- _, err := s.clusclient.Del(ctx, key.(string)).Result()
- return err
-}
-
-// Invalidate invalidates some cache data in Redis for given options
-func (s *RedisClusterStore) Invalidate(ctx context.Context, options ...lib_store.InvalidateOption) error {
- opts := lib_store.ApplyInvalidateOptions(options...)
-
- if tags := opts.Tags; len(tags) > 0 {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(RedisClusterTagPattern, tag)
- cacheKeys, err := s.clusclient.SMembers(ctx, tagKey).Result()
- if err != nil {
- continue
- }
-
- for _, cacheKey := range cacheKeys {
- s.Delete(ctx, cacheKey)
- }
-
- s.Delete(ctx, tagKey)
- }
- }
-
- return nil
-}
-
-// Clear resets all data in the store
-func (s *RedisClusterStore) Clear(ctx context.Context) error {
- if err := s.clusclient.FlushAll(ctx).Err(); err != nil {
- return err
- }
-
- return nil
-}
-
-// GetType returns the store type
-func (s *RedisClusterStore) GetType() string {
- return RedisClusterType
-}
diff --git a/toolkit/gocache/store/rediscluster/rediscluster_bench_test.go b/toolkit/gocache/store/rediscluster/rediscluster_bench_test.go
deleted file mode 100644
index 71aa275d..00000000
--- a/toolkit/gocache/store/rediscluster/rediscluster_bench_test.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package rediscluster
-
-import (
- "context"
- "fmt"
- "math"
- "strings"
- "testing"
-
- redis "github.com/redis/go-redis/v9"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-// should be configured to connect to real Redis Cluster
-func BenchmarkRedisClusterSet(b *testing.B) {
- ctx := context.Background()
-
- addr := strings.Split("redis:6379", ",")
- store := NewRedisCluster(redis.NewClusterClient(&redis.ClusterOptions{
- Addrs: addr,
- }), nil)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- key := fmt.Sprintf("test-%d", n)
- value := []byte(fmt.Sprintf("value-%d", n))
-
- store.Set(ctx, key, value, lib_store.WithTags([]string{fmt.Sprintf("tag-%d", n)}))
- }
- })
- }
-}
-
-func BenchmarkRedisClusterGet(b *testing.B) {
- ctx := context.Background()
-
- addr := strings.Split("redis:6379", ",")
- store := NewRedisCluster(redis.NewClusterClient(&redis.ClusterOptions{
- Addrs: addr,
- }), nil)
-
- key := "test"
- value := []byte("value")
-
- store.Set(ctx, key, value, nil)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- _, _ = store.Get(ctx, key)
- }
- })
- }
-}
diff --git a/toolkit/gocache/store/rediscluster/rediscluster_mock.go b/toolkit/gocache/store/rediscluster/rediscluster_mock.go
deleted file mode 100644
index 2dbe81d2..00000000
--- a/toolkit/gocache/store/rediscluster/rediscluster_mock.go
+++ /dev/null
@@ -1,159 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: store/rediscluster/rediscluster.go
-
-// Package rediscluster is a generated GoMock package.
-package rediscluster
-
-import (
- context "context"
- reflect "reflect"
- time "time"
-
- v9 "github.com/redis/go-redis/v9"
- gomock "go.uber.org/mock/gomock"
-)
-
-// MockRedisClusterClientInterface is a mock of RedisClusterClientInterface interface.
-type MockRedisClusterClientInterface struct {
- ctrl *gomock.Controller
- recorder *MockRedisClusterClientInterfaceMockRecorder
-}
-
-// MockRedisClusterClientInterfaceMockRecorder is the mock recorder for MockRedisClusterClientInterface.
-type MockRedisClusterClientInterfaceMockRecorder struct {
- mock *MockRedisClusterClientInterface
-}
-
-// NewMockRedisClusterClientInterface creates a new mock instance.
-func NewMockRedisClusterClientInterface(ctrl *gomock.Controller) *MockRedisClusterClientInterface {
- mock := &MockRedisClusterClientInterface{ctrl: ctrl}
- mock.recorder = &MockRedisClusterClientInterfaceMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockRedisClusterClientInterface) EXPECT() *MockRedisClusterClientInterfaceMockRecorder {
- return m.recorder
-}
-
-// Del mocks base method.
-func (m *MockRedisClusterClientInterface) Del(ctx context.Context, keys ...string) *v9.IntCmd {
- m.ctrl.T.Helper()
- varargs := []interface{}{ctx}
- for _, a := range keys {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "Del", varargs...)
- ret0, _ := ret[0].(*v9.IntCmd)
- return ret0
-}
-
-// Del indicates an expected call of Del.
-func (mr *MockRedisClusterClientInterfaceMockRecorder) Del(ctx interface{}, keys ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{ctx}, keys...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Del", reflect.TypeOf((*MockRedisClusterClientInterface)(nil).Del), varargs...)
-}
-
-// Expire mocks base method.
-func (m *MockRedisClusterClientInterface) Expire(ctx context.Context, key string, expiration time.Duration) *v9.BoolCmd {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Expire", ctx, key, expiration)
- ret0, _ := ret[0].(*v9.BoolCmd)
- return ret0
-}
-
-// Expire indicates an expected call of Expire.
-func (mr *MockRedisClusterClientInterfaceMockRecorder) Expire(ctx, key, expiration interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Expire", reflect.TypeOf((*MockRedisClusterClientInterface)(nil).Expire), ctx, key, expiration)
-}
-
-// FlushAll mocks base method.
-func (m *MockRedisClusterClientInterface) FlushAll(ctx context.Context) *v9.StatusCmd {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "FlushAll", ctx)
- ret0, _ := ret[0].(*v9.StatusCmd)
- return ret0
-}
-
-// FlushAll indicates an expected call of FlushAll.
-func (mr *MockRedisClusterClientInterfaceMockRecorder) FlushAll(ctx interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FlushAll", reflect.TypeOf((*MockRedisClusterClientInterface)(nil).FlushAll), ctx)
-}
-
-// Get mocks base method.
-func (m *MockRedisClusterClientInterface) Get(ctx context.Context, key string) *v9.StringCmd {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", ctx, key)
- ret0, _ := ret[0].(*v9.StringCmd)
- return ret0
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockRedisClusterClientInterfaceMockRecorder) Get(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRedisClusterClientInterface)(nil).Get), ctx, key)
-}
-
-// SAdd mocks base method.
-func (m *MockRedisClusterClientInterface) SAdd(ctx context.Context, key string, members ...any) *v9.IntCmd {
- m.ctrl.T.Helper()
- varargs := []interface{}{ctx, key}
- for _, a := range members {
- varargs = append(varargs, a)
- }
- ret := m.ctrl.Call(m, "SAdd", varargs...)
- ret0, _ := ret[0].(*v9.IntCmd)
- return ret0
-}
-
-// SAdd indicates an expected call of SAdd.
-func (mr *MockRedisClusterClientInterfaceMockRecorder) SAdd(ctx, key interface{}, members ...interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- varargs := append([]interface{}{ctx, key}, members...)
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SAdd", reflect.TypeOf((*MockRedisClusterClientInterface)(nil).SAdd), varargs...)
-}
-
-// SMembers mocks base method.
-func (m *MockRedisClusterClientInterface) SMembers(ctx context.Context, key string) *v9.StringSliceCmd {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "SMembers", ctx, key)
- ret0, _ := ret[0].(*v9.StringSliceCmd)
- return ret0
-}
-
-// SMembers indicates an expected call of SMembers.
-func (mr *MockRedisClusterClientInterfaceMockRecorder) SMembers(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SMembers", reflect.TypeOf((*MockRedisClusterClientInterface)(nil).SMembers), ctx, key)
-}
-
-// Set mocks base method.
-func (m *MockRedisClusterClientInterface) Set(ctx context.Context, key string, values any, expiration time.Duration) *v9.StatusCmd {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Set", ctx, key, values, expiration)
- ret0, _ := ret[0].(*v9.StatusCmd)
- return ret0
-}
-
-// Set indicates an expected call of Set.
-func (mr *MockRedisClusterClientInterfaceMockRecorder) Set(ctx, key, values, expiration interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Set", reflect.TypeOf((*MockRedisClusterClientInterface)(nil).Set), ctx, key, values, expiration)
-}
-
-// TTL mocks base method.
-func (m *MockRedisClusterClientInterface) TTL(ctx context.Context, key string) *v9.DurationCmd {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "TTL", ctx, key)
- ret0, _ := ret[0].(*v9.DurationCmd)
- return ret0
-}
-
-// TTL indicates an expected call of TTL.
-func (mr *MockRedisClusterClientInterfaceMockRecorder) TTL(ctx, key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TTL", reflect.TypeOf((*MockRedisClusterClientInterface)(nil).TTL), ctx, key)
-}
diff --git a/toolkit/gocache/store/rediscluster/rediscluster_test.go b/toolkit/gocache/store/rediscluster/rediscluster_test.go
deleted file mode 100644
index 5b8946c5..00000000
--- a/toolkit/gocache/store/rediscluster/rediscluster_test.go
+++ /dev/null
@@ -1,182 +0,0 @@
-package rediscluster
-
-import (
- "context"
- "testing"
- "time"
-
- redis "github.com/redis/go-redis/v9"
- "github.com/stretchr/testify/assert"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "go.uber.org/mock/gomock"
-)
-
-func TestNewRedisCluster(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockRedisClusterClientInterface(ctrl)
-
- // When
- store := NewRedisCluster(client, lib_store.WithExpiration(6*time.Second))
-
- // Then
- assert.IsType(t, new(RedisClusterStore), store)
- assert.Equal(t, client, store.clusclient)
- assert.Equal(t, &lib_store.Options{Expiration: 6 * time.Second}, store.options)
-}
-
-func TestRedisClusterGet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockRedisClusterClientInterface(ctrl)
- client.EXPECT().Get(ctx, "my-key").Return(&redis.StringCmd{})
-
- store := NewRedisCluster(client)
-
- // When
- value, err := store.Get(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
- assert.NotNil(t, value)
-}
-
-func TestRedisClusterSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockRedisClusterClientInterface(ctrl)
- client.EXPECT().Set(ctx, "my-key", cacheValue, 5*time.Second).Return(&redis.StatusCmd{})
-
- store := NewRedisCluster(client, lib_store.WithExpiration(6*time.Second))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithExpiration(5*time.Second))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisClusterSetWhenNoOptionsGiven(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockRedisClusterClientInterface(ctrl)
- client.EXPECT().Set(ctx, "my-key", cacheValue, 6*time.Second).Return(&redis.StatusCmd{})
-
- store := NewRedisCluster(client, lib_store.WithExpiration(6*time.Second))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisClusterSetWithTags(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockRedisClusterClientInterface(ctrl)
- client.EXPECT().Set(ctx, cacheKey, cacheValue, time.Duration(0)).Return(&redis.StatusCmd{})
- client.EXPECT().SAdd(ctx, "gocache_tag_tag1", "my-key").Return(&redis.IntCmd{})
- client.EXPECT().Expire(ctx, "gocache_tag_tag1", 720*time.Hour).Return(&redis.BoolCmd{})
-
- store := NewRedisCluster(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisClusterDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := NewMockRedisClusterClientInterface(ctrl)
- client.EXPECT().Del(ctx, "my-key").Return(&redis.IntCmd{})
-
- store := NewRedisCluster(client)
-
- // When
- err := store.Delete(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisClusterInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := &redis.StringSliceCmd{}
-
- client := NewMockRedisClusterClientInterface(ctrl)
- client.EXPECT().SMembers(ctx, "gocache_tag_tag1").Return(cacheKeys)
- client.EXPECT().Del(ctx, "gocache_tag_tag1").Return(&redis.IntCmd{})
-
- store := NewRedisCluster(client)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisClusterClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockRedisClusterClientInterface(ctrl)
- client.EXPECT().FlushAll(ctx).Return(&redis.StatusCmd{})
-
- store := NewRedisCluster(client)
-
- // When
- err := store.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisClusterGetType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockRedisClusterClientInterface(ctrl)
-
- store := NewRedisCluster(client)
-
- // When - Then
- assert.Equal(t, RedisClusterType, store.GetType())
-}
diff --git a/toolkit/gocache/store/ristretto/ristretto.go b/toolkit/gocache/store/ristretto/ristretto.go
deleted file mode 100644
index dc938e35..00000000
--- a/toolkit/gocache/store/ristretto/ristretto.go
+++ /dev/null
@@ -1,157 +0,0 @@
-package ristretto
-
-import (
- "context"
- "errors"
- "fmt"
- "strings"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
-
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-const (
- // RistrettoType represents the storage type as a string value
- RistrettoType = "ristretto"
- // RistrettoTagPattern represents the tag pattern to be used as a key in specified storage
- RistrettoTagPattern = "gocache_tag_%s"
-)
-
-// RistrettoClientInterface represents a dgraph-io/ristretto client
-type RistrettoClientInterface interface {
- Get(key any) (any, bool)
- SetWithTTL(key, value any, cost int64, ttl time.Duration) bool
- Del(key any)
- Clear()
- Wait()
-}
-
-// RistrettoStore is a store for Ristretto (memory) library
-type RistrettoStore struct {
- client RistrettoClientInterface
- options *lib_store.Options
-}
-
-// NewRistretto creates a new store to Ristretto (memory) library instance
-func NewRistretto(client RistrettoClientInterface, options ...lib_store.Option) *RistrettoStore {
- return &RistrettoStore{
- client: client,
- options: lib_store.ApplyOptions(options...),
- }
-}
-
-// Get returns data stored from a given key
-func (s *RistrettoStore) Get(_ context.Context, key any) (any, error) {
- var err error
-
- value, exists := s.client.Get(key)
- if !exists {
- err = lib_store.NotFoundWithCause(errors.New("value not found in Ristretto store"))
- }
-
- return value, err
-}
-
-// GetWithTTL returns data stored from a given key and its corresponding TTL
-func (s *RistrettoStore) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
- value, err := s.Get(ctx, key)
- return value, 0, err
-}
-
-// Set defines data in Ristretto memory cache for given key identifier
-func (s *RistrettoStore) Set(ctx context.Context, key any, value any, options ...lib_store.Option) error {
- opts := lib_store.ApplyOptionsWithDefault(s.options, options...)
-
- var err error
-
- if set := s.client.SetWithTTL(key, value, opts.Cost, opts.Expiration); !set {
- err = fmt.Errorf("An error has occurred while setting value '%v' on key '%v'", value, key)
- }
-
- if err != nil {
- return err
- }
-
- if opts.SynchronousSet {
- s.client.Wait()
- }
-
- if tags := opts.Tags; len(tags) > 0 {
- s.setTags(ctx, key, tags)
- }
-
- return nil
-}
-
-func (s *RistrettoStore) setTags(ctx context.Context, key any, tags []string) {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(RistrettoTagPattern, tag)
- cacheKeys := []string{}
-
- if result, err := s.Get(ctx, tagKey); err == nil {
- if bytes, ok := result.([]byte); ok {
- cacheKeys = strings.Split(string(bytes), ",")
- }
- }
-
- alreadyInserted := false
- for _, cacheKey := range cacheKeys {
- if cacheKey == key.(string) {
- alreadyInserted = true
- break
- }
- }
-
- if !alreadyInserted {
- cacheKeys = append(cacheKeys, key.(string))
- }
-
- s.Set(ctx, tagKey, []byte(strings.Join(cacheKeys, ",")), lib_store.WithExpiration(720*time.Hour))
- }
-}
-
-// Delete removes data in Ristretto memory cache for given key identifier
-func (s *RistrettoStore) Delete(_ context.Context, key any) error {
- s.client.Del(key)
- return nil
-}
-
-// Invalidate invalidates some cache data in Redis for given options
-func (s *RistrettoStore) Invalidate(ctx context.Context, options ...lib_store.InvalidateOption) error {
- opts := lib_store.ApplyInvalidateOptions(options...)
-
- if tags := opts.Tags; len(tags) > 0 {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(RistrettoTagPattern, tag)
- result, err := s.Get(ctx, tagKey)
- if err != nil {
- zlogger.Warn().Msg(err.Error())
- continue
- }
-
- cacheKeys := []string{}
- if bytes, ok := result.([]byte); ok {
- cacheKeys = strings.Split(string(bytes), ",")
- }
-
- for _, cacheKey := range cacheKeys {
- s.Delete(ctx, cacheKey)
- }
- }
- }
-
- return nil
-}
-
-// Clear resets all data in the store
-func (s *RistrettoStore) Clear(_ context.Context) error {
- s.client.Clear()
- return nil
-}
-
-// GetType returns the store type
-func (s *RistrettoStore) GetType() string {
- return RistrettoType
-}
diff --git a/toolkit/gocache/store/ristretto/ristretto_bench_test.go b/toolkit/gocache/store/ristretto/ristretto_bench_test.go
deleted file mode 100644
index 8d64c010..00000000
--- a/toolkit/gocache/store/ristretto/ristretto_bench_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package ristretto
-
-import (
- "context"
- "fmt"
- "math"
- "testing"
-
- "github.com/dgraph-io/ristretto"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-func BenchmarkRistrettoSet(b *testing.B) {
- ctx := context.Background()
-
- client, err := ristretto.NewCache(&ristretto.Config{
- NumCounters: 1000,
- MaxCost: 100,
- BufferItems: 64,
- })
- if err != nil {
- panic(err)
- }
- store := NewRistretto(client)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- key := fmt.Sprintf("test-%d", n)
- value := []byte(fmt.Sprintf("value-%d", n))
-
- store.Set(ctx, key, value, lib_store.WithTags([]string{fmt.Sprintf("tag-%d", n)}))
- }
- })
- }
-}
-
-func BenchmarkRistrettoSetWithSynchronousSet(b *testing.B) {
- ctx := context.Background()
-
- client, err := ristretto.NewCache(&ristretto.Config{
- NumCounters: 1000,
- MaxCost: 100,
- BufferItems: 64,
- })
- if err != nil {
- panic(err)
- }
- store := NewRistretto(client, lib_store.WithSynchronousSet())
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- key := fmt.Sprintf("test-%d", n)
- value := []byte(fmt.Sprintf("value-%d", n))
-
- store.Set(ctx, key, value, lib_store.WithTags([]string{fmt.Sprintf("tag-%d", n)}))
- }
- })
- }
-}
-
-func BenchmarkRistrettoGet(b *testing.B) {
- ctx := context.Background()
-
- client, err := ristretto.NewCache(&ristretto.Config{
- NumCounters: 1000,
- MaxCost: 100,
- BufferItems: 64,
- })
- if err != nil {
- panic(err)
- }
- store := NewRistretto(client)
-
- key := "test"
- value := []byte("value")
-
- store.Set(ctx, key, value)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- _, _ = store.Get(ctx, key)
- }
- })
- }
-}
diff --git a/toolkit/gocache/store/ristretto/ristretto_mock.go b/toolkit/gocache/store/ristretto/ristretto_mock.go
deleted file mode 100644
index 1c96d9b8..00000000
--- a/toolkit/gocache/store/ristretto/ristretto_mock.go
+++ /dev/null
@@ -1,100 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: store/ristretto/ristretto.go
-
-// Package ristretto is a generated GoMock package.
-package ristretto
-
-import (
- reflect "reflect"
- time "time"
-
- gomock "go.uber.org/mock/gomock"
-)
-
-// MockRistrettoClientInterface is a mock of RistrettoClientInterface interface.
-type MockRistrettoClientInterface struct {
- ctrl *gomock.Controller
- recorder *MockRistrettoClientInterfaceMockRecorder
-}
-
-// MockRistrettoClientInterfaceMockRecorder is the mock recorder for MockRistrettoClientInterface.
-type MockRistrettoClientInterfaceMockRecorder struct {
- mock *MockRistrettoClientInterface
-}
-
-// NewMockRistrettoClientInterface creates a new mock instance.
-func NewMockRistrettoClientInterface(ctrl *gomock.Controller) *MockRistrettoClientInterface {
- mock := &MockRistrettoClientInterface{ctrl: ctrl}
- mock.recorder = &MockRistrettoClientInterfaceMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockRistrettoClientInterface) EXPECT() *MockRistrettoClientInterfaceMockRecorder {
- return m.recorder
-}
-
-// Clear mocks base method.
-func (m *MockRistrettoClientInterface) Clear() {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Clear")
-}
-
-// Clear indicates an expected call of Clear.
-func (mr *MockRistrettoClientInterfaceMockRecorder) Clear() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Clear", reflect.TypeOf((*MockRistrettoClientInterface)(nil).Clear))
-}
-
-// Del mocks base method.
-func (m *MockRistrettoClientInterface) Del(key any) {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Del", key)
-}
-
-// Del indicates an expected call of Del.
-func (mr *MockRistrettoClientInterfaceMockRecorder) Del(key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Del", reflect.TypeOf((*MockRistrettoClientInterface)(nil).Del), key)
-}
-
-// Get mocks base method.
-func (m *MockRistrettoClientInterface) Get(key any) (any, bool) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Get", key)
- ret0, _ := ret[0].(any)
- ret1, _ := ret[1].(bool)
- return ret0, ret1
-}
-
-// Get indicates an expected call of Get.
-func (mr *MockRistrettoClientInterfaceMockRecorder) Get(key interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Get", reflect.TypeOf((*MockRistrettoClientInterface)(nil).Get), key)
-}
-
-// SetWithTTL mocks base method.
-func (m *MockRistrettoClientInterface) SetWithTTL(key, value any, cost int64, ttl time.Duration) bool {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "SetWithTTL", key, value, cost, ttl)
- ret0, _ := ret[0].(bool)
- return ret0
-}
-
-// SetWithTTL indicates an expected call of SetWithTTL.
-func (mr *MockRistrettoClientInterfaceMockRecorder) SetWithTTL(key, value, cost, ttl interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWithTTL", reflect.TypeOf((*MockRistrettoClientInterface)(nil).SetWithTTL), key, value, cost, ttl)
-}
-
-// Wait mocks base method.
-func (m *MockRistrettoClientInterface) Wait() {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Wait")
-}
-
-// Wait indicates an expected call of Wait.
-func (mr *MockRistrettoClientInterfaceMockRecorder) Wait() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Wait", reflect.TypeOf((*MockRistrettoClientInterface)(nil).Wait))
-}
diff --git a/toolkit/gocache/store/ristretto/ristretto_test.go b/toolkit/gocache/store/ristretto/ristretto_test.go
deleted file mode 100644
index ac953cfd..00000000
--- a/toolkit/gocache/store/ristretto/ristretto_test.go
+++ /dev/null
@@ -1,338 +0,0 @@
-package ristretto
-
-import (
- "context"
- "fmt"
- "testing"
- "time"
-
- "github.com/stretchr/testify/assert"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "go.uber.org/mock/gomock"
-)
-
-func TestNewRistretto(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockRistrettoClientInterface(ctrl)
-
- // When
- store := NewRistretto(client, lib_store.WithCost(8))
-
- // Then
- assert.IsType(t, new(RistrettoStore), store)
- assert.Equal(t, client, store.client)
- assert.Equal(t, &lib_store.Options{Cost: 8}, store.options)
-}
-
-func TestRistrettoGet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(cacheValue, true)
-
- store := NewRistretto(client)
-
- // When
- value, err := store.Get(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
-}
-
-func TestRistrettoGetWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(nil, false)
-
- store := NewRistretto(client)
-
- // When
- value, err := store.Get(ctx, cacheKey)
-
- // Then
- assert.Nil(t, value)
- assert.IsType(t, &lib_store.NotFound{}, err)
-}
-
-func TestRistrettoGetWithTTL(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(cacheValue, true)
-
- store := NewRistretto(client)
-
- // When
- value, ttl, err := store.GetWithTTL(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, cacheValue, value)
- assert.Equal(t, 0*time.Second, ttl)
-}
-
-func TestRistrettoGetWithTTLWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().Get(cacheKey).Return(nil, false)
-
- store := NewRistretto(client)
-
- // When
- value, ttl, err := store.GetWithTTL(ctx, cacheKey)
-
- // Then
- assert.Nil(t, value)
- assert.IsType(t, &lib_store.NotFound{}, err)
- assert.Equal(t, 0*time.Second, ttl)
-}
-
-func TestRistrettoSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().SetWithTTL(cacheKey, cacheValue, int64(4), 0*time.Second).Return(true)
-
- store := NewRistretto(client, lib_store.WithCost(7))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithCost(4))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRistrettoSetWhenNoOptionsGiven(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().SetWithTTL(cacheKey, cacheValue, int64(7), 0*time.Second).Return(true)
-
- store := NewRistretto(client, lib_store.WithCost(7))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRistrettoSetWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().SetWithTTL(cacheKey, cacheValue, int64(7), 0*time.Second).Return(false)
-
- store := NewRistretto(client, lib_store.WithCost(7))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Equal(t, fmt.Errorf("An error has occurred while setting value '%v' on key '%v'", cacheValue, cacheKey), err)
-}
-
-func TestRistrettoSetWithSynchronousSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().SetWithTTL(cacheKey, cacheValue, int64(7), 0*time.Second).Return(true)
- client.EXPECT().Wait()
-
- store := NewRistretto(client, lib_store.WithCost(7), lib_store.WithSynchronousSet())
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithSynchronousSet())
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRistrettoSetWithTags(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().SetWithTTL(cacheKey, cacheValue, int64(0), 0*time.Second).Return(true)
- client.EXPECT().Get("gocache_tag_tag1").Return(nil, true)
- client.EXPECT().SetWithTTL("gocache_tag_tag1", []byte("my-key"), int64(0), 720*time.Hour).Return(true)
-
- store := NewRistretto(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRistrettoSetWithTagsWhenAlreadyInserted(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := []byte("my-cache-value")
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().SetWithTTL(cacheKey, cacheValue, int64(0), 0*time.Second).Return(true)
- client.EXPECT().Get("gocache_tag_tag1").Return([]byte("my-key,a-second-key"), true)
- client.EXPECT().SetWithTTL("gocache_tag_tag1", []byte("my-key,a-second-key"), int64(0), 720*time.Hour).Return(true)
-
- store := NewRistretto(client)
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRistrettoDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().Del(cacheKey)
-
- store := NewRistretto(client)
-
- // When
- err := store.Delete(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRistrettoInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := []byte("a23fdf987h2svc23,jHG2372x38hf74")
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().Get("gocache_tag_tag1").Return(cacheKeys, true)
- client.EXPECT().Del("a23fdf987h2svc23")
- client.EXPECT().Del("jHG2372x38hf74")
-
- store := NewRistretto(client)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRistrettoInvalidateWhenError(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKeys := []byte("a23fdf987h2svc23,jHG2372x38hf74")
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().Get("gocache_tag_tag1").Return(cacheKeys, false)
-
- store := NewRistretto(client)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRistrettoClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := NewMockRistrettoClientInterface(ctrl)
- client.EXPECT().Clear()
-
- store := NewRistretto(client)
-
- // When
- err := store.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRistrettoGetType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := NewMockRistrettoClientInterface(ctrl)
-
- store := NewRistretto(client)
-
- // When - Then
- assert.Equal(t, RistrettoType, store.GetType())
-}
diff --git a/toolkit/gocache/store/rueidis/rueidis.go b/toolkit/gocache/store/rueidis/rueidis.go
deleted file mode 100644
index 3dfd7068..00000000
--- a/toolkit/gocache/store/rueidis/rueidis.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package rueidis
-
-import (
- "context"
- "fmt"
- "time"
-
- "github.com/redis/rueidis"
- "github.com/redis/rueidis/rueidiscompat"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-const (
- // RueidisType represents the storage type as a string value
- RueidisType = "rueidis"
- // RueidisTagPattern represents the tag pattern to be used as a key in specified storage
- RueidisTagPattern = "gocache_tag_%s"
-
- defaultClientSideCacheExpiration = 10 * time.Second
-)
-
-// RueidisStore is a store for Redis
-type RueidisStore struct {
- client rueidis.Client
- options *lib_store.Options
-}
-
-// NewRueidis creates a new store to Redis instance(s)
-func NewRueidis(client rueidis.Client, options ...lib_store.Option) *RueidisStore {
- // defaults client side cache expiration to 10s
- appliedOptions := lib_store.ApplyOptions(options...)
-
- if appliedOptions.ClientSideCacheExpiration == 0 {
- appliedOptions.ClientSideCacheExpiration = defaultClientSideCacheExpiration
- }
-
- return &RueidisStore{
- client: client,
- options: appliedOptions,
- }
-}
-
-// Get returns data stored from a given key
-func (s *RueidisStore) Get(ctx context.Context, key any) (any, error) {
- cmd := s.client.B().Get().Key(key.(string)).Cache()
- res := s.client.DoCache(ctx, cmd, s.options.ClientSideCacheExpiration)
- str, err := res.ToString()
- if rueidis.IsRedisNil(err) {
- err = lib_store.NotFoundWithCause(err)
- }
- return str, err
-}
-
-// GetWithTTL returns data stored from a given key and its corresponding TTL
-func (s *RueidisStore) GetWithTTL(ctx context.Context, key any) (any, time.Duration, error) {
- cmd := s.client.B().Get().Key(key.(string)).Cache()
- res := s.client.DoCache(ctx, cmd, s.options.ClientSideCacheExpiration)
- str, err := res.ToString()
- if rueidis.IsRedisNil(err) {
- err = lib_store.NotFoundWithCause(err)
- }
- return str, time.Duration(res.CacheTTL()) * time.Second, err
-}
-
-// Set defines data in Redis for given key identifier
-func (s *RueidisStore) Set(ctx context.Context, key any, value any, options ...lib_store.Option) error {
- opts := lib_store.ApplyOptionsWithDefault(s.options, options...)
- ttl := int64(opts.Expiration.Seconds())
- cmd := s.client.B().Set().Key(key.(string)).Value(value.(string)).ExSeconds(ttl).Build()
- err := s.client.Do(ctx, cmd).Error()
- if err != nil {
- return err
- }
-
- if tags := opts.Tags; len(tags) > 0 {
- s.setTags(ctx, key, tags)
- }
-
- return nil
-}
-
-func (s *RueidisStore) setTags(ctx context.Context, key any, tags []string) {
- ttl := 720 * time.Hour
- for _, tag := range tags {
- tagKey := fmt.Sprintf(RueidisTagPattern, tag)
- s.client.DoMulti(ctx,
- s.client.B().Sadd().Key(tagKey).Member(key.(string)).Build(),
- s.client.B().Expire().Key(tagKey).Seconds(int64(ttl.Seconds())).Build(),
- )
- }
-}
-
-// Delete removes data from Redis for given key identifier
-func (s *RueidisStore) Delete(ctx context.Context, key any) error {
- return s.client.Do(ctx, s.client.B().Del().Key(key.(string)).Build()).Error()
-}
-
-// Invalidate invalidates some cache data in Redis for given options
-func (s *RueidisStore) Invalidate(ctx context.Context, options ...lib_store.InvalidateOption) error {
- opts := lib_store.ApplyInvalidateOptions(options...)
-
- if tags := opts.Tags; len(tags) > 0 {
- for _, tag := range tags {
- tagKey := fmt.Sprintf(RueidisTagPattern, tag)
-
- cacheKeys, err := s.client.Do(ctx, s.client.B().Smembers().Key(tagKey).Build()).AsStrSlice()
- if err != nil {
- continue
- }
-
- for _, cacheKey := range cacheKeys {
- s.Delete(ctx, cacheKey)
- }
-
- s.Delete(ctx, tagKey)
- }
- }
-
- return nil
-}
-
-// GetType returns the store type
-func (s *RueidisStore) GetType() string {
- return RueidisType
-}
-
-// Clear resets all data in the store
-func (s *RueidisStore) Clear(ctx context.Context) error {
- return rueidiscompat.NewAdapter(s.client).FlushAll(ctx).Err()
-}
diff --git a/toolkit/gocache/store/rueidis/rueidis_bench_test.go b/toolkit/gocache/store/rueidis/rueidis_bench_test.go
deleted file mode 100644
index acc12ea9..00000000
--- a/toolkit/gocache/store/rueidis/rueidis_bench_test.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package rueidis
-
-import (
- "context"
- "fmt"
- "math"
- "testing"
- "time"
-
- "github.com/redis/rueidis"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
-)
-
-func BenchmarkRueidisSet(b *testing.B) {
- ctx := context.Background()
-
- ruedisClient, _ := rueidis.NewClient(rueidis.ClientOption{
- InitAddress: []string{"localhost:26379"},
- Sentinel: rueidis.SentinelOption{
- MasterSet: "mymaster",
- },
- SelectDB: 0,
- })
-
- store := NewRueidis(ruedisClient, lib_store.WithExpiration(time.Hour*4))
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- key := fmt.Sprintf("test-%d", n)
- value := fmt.Sprintf("value-%d", n)
-
- store.Set(ctx, key, value, lib_store.WithTags([]string{fmt.Sprintf("tag-%d", n)}))
- }
- })
- }
-}
-
-func BenchmarkRueidisGet(b *testing.B) {
- ctx := context.Background()
-
- ruedisClient, _ := rueidis.NewClient(rueidis.ClientOption{
- InitAddress: []string{"localhost:26379"},
- Sentinel: rueidis.SentinelOption{
- MasterSet: "mymaster",
- },
- SelectDB: 0,
- })
-
- store := NewRueidis(ruedisClient, lib_store.WithExpiration(time.Hour*4))
-
- key := "test"
- value := "value"
-
- _ = store.Set(ctx, key, value)
-
- for k := 0.; k <= 10; k++ {
- n := int(math.Pow(2, k))
- b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
- for i := 0; i < b.N*n; i++ {
- _, _ = store.Get(ctx, key)
- }
- })
- }
-}
diff --git a/toolkit/gocache/store/rueidis/rueidis_test.go b/toolkit/gocache/store/rueidis/rueidis_test.go
deleted file mode 100644
index dabffffe..00000000
--- a/toolkit/gocache/store/rueidis/rueidis_test.go
+++ /dev/null
@@ -1,214 +0,0 @@
-package rueidis
-
-import (
- "context"
- "testing"
- "time"
-
- "github.com/redis/rueidis"
- "github.com/redis/rueidis/mock"
-
- "github.com/stretchr/testify/assert"
- lib_store "github.com/unionj-cloud/go-doudou/v2/toolkit/gocache/lib/store"
- "go.uber.org/mock/gomock"
-)
-
-func TestNewRueidis(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- // rueidis mock client
- client := mock.NewClient(ctrl)
-
- // When
- store := NewRueidis(client, lib_store.WithExpiration(6*time.Second), lib_store.WithClientSideCaching(time.Second*8))
-
- // Then
- assert.IsType(t, new(RueidisStore), store)
- assert.Equal(t, client, store.client)
- assert.Equal(t, &lib_store.Options{Expiration: 6 * time.Second, ClientSideCacheExpiration: time.Second * 8}, store.options)
-}
-
-func TestRueidisGet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- // rueidis mock client
- client := mock.NewClient(ctrl)
- client.EXPECT().DoCache(ctx, mock.Match("GET", "my-key"), defaultClientSideCacheExpiration).Return(mock.Result(mock.RedisString("my-value")))
-
- store := NewRueidis(client)
-
- // When
- value, err := store.Get(ctx, "my-key")
-
- // Then
- assert.Nil(t, err)
- assert.Equal(t, value, "my-value")
-}
-
-func TestRueidisGetNotFound(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- // rueidis mock client
- client := mock.NewClient(ctrl)
- client.EXPECT().DoCache(ctx, mock.Match("GET", "my-key"), defaultClientSideCacheExpiration).Return(mock.Result(mock.RedisNil()))
-
- store := NewRueidis(client)
-
- // When
- value, err := store.Get(ctx, "my-key")
-
- // Then
- assert.NotNil(t, err)
- assert.Equal(t, value, "")
-}
-
-func TestRueidisSet(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- // rueidis mock client
- client := mock.NewClient(ctrl)
- client.EXPECT().Do(ctx, mock.Match("SET", cacheKey, cacheValue, "EX", "10")).Return(mock.Result(mock.RedisString("")))
-
- store := NewRueidis(client, lib_store.WithExpiration(time.Second*10))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRueidisSetWhenNoOptionsGiven(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := mock.NewClient(ctrl)
- client.EXPECT().Do(ctx, mock.Match("SET", cacheKey, cacheValue, "EX", "6")).Return(mock.Result(mock.RedisString("")))
-
- store := NewRueidis(client, lib_store.WithExpiration(6*time.Second))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisSetWithTags(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
- cacheValue := "my-cache-value"
-
- client := mock.NewClient(ctrl)
- client.EXPECT().Do(ctx, mock.Match("SET", cacheKey, cacheValue, "EX", "10")).Return(mock.Result(mock.RedisString("")))
- client.EXPECT().DoMulti(ctx,
- mock.Match("SADD", "gocache_tag_tag1", "my-key"),
- mock.Match("EXPIRE", "gocache_tag_tag1", "2592000"),
- ).Return([]rueidis.RedisResult{
- mock.Result(mock.RedisString("")),
- mock.Result(mock.RedisString("")),
- })
-
- store := NewRueidis(client, lib_store.WithExpiration(time.Second*10))
-
- // When
- err := store.Set(ctx, cacheKey, cacheValue, lib_store.WithTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisDelete(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- cacheKey := "my-key"
-
- client := mock.NewClient(ctrl)
- client.EXPECT().Do(ctx, mock.Match("DEL", cacheKey)).Return(mock.Result(mock.RedisInt64(1)))
-
- store := NewRueidis(client)
-
- // When
- err := store.Delete(ctx, cacheKey)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisInvalidate(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := mock.NewClient(ctrl)
- client.EXPECT().Do(ctx, mock.Match("SMEMBERS", "gocache_tag_tag1")).Return(mock.Result(mock.RedisArray()))
- client.EXPECT().Do(ctx, mock.Match("DEL", "gocache_tag_tag1")).Return(mock.Result(mock.RedisInt64(1)))
-
- store := NewRueidis(client)
-
- // When
- err := store.Invalidate(ctx, lib_store.WithInvalidateTags([]string{"tag1"}))
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisClear(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- ctx := context.Background()
-
- client := mock.NewClient(ctrl)
- client.EXPECT().Nodes().Return(map[string]rueidis.Client{
- "client1": client,
- })
- client.EXPECT().Do(ctx, mock.Match("ROLE")).Return(mock.Result(mock.RedisArray(mock.RedisString("master"))))
- client.EXPECT().Do(ctx, mock.Match("FLUSHALL")).Return(mock.Result(mock.RedisString("")))
-
- store := NewRueidis(client)
-
- // When
- err := store.Clear(ctx)
-
- // Then
- assert.Nil(t, err)
-}
-
-func TestRedisGetType(t *testing.T) {
- // Given
- ctrl := gomock.NewController(t)
-
- client := mock.NewClient(ctrl)
-
- store := NewRueidis(client)
-
- // When - Then
- assert.Equal(t, RueidisType, store.GetType())
-}
diff --git a/toolkit/gormgen/License b/toolkit/gormgen/License
deleted file mode 100644
index 6e63bec5..00000000
--- a/toolkit/gormgen/License
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2021-NOW Jinzhu
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/toolkit/gormgen/README.md b/toolkit/gormgen/README.md
deleted file mode 100644
index c9aa70b7..00000000
--- a/toolkit/gormgen/README.md
+++ /dev/null
@@ -1,37 +0,0 @@
-# GORM Gen
-
-Friendly & Safer GORM powered by Code Generation.
-
-[![Release](https://img.shields.io/github/v/release/go-gorm/gen)](https://github.com/go-gorm/gen/releases)
-[![Go Report Card](https://goreportcard.com/badge/github.com/go-gorm/gen)](https://goreportcard.com/report/github.com/go-gorm/gen)
-[![MIT license](https://img.shields.io/badge/license-MIT-brightgreen.svg)](https://opensource.org/licenses/MIT)
-[![OpenIssue](https://img.shields.io/github/issues/go-gorm/gen)](https://github.com/go-gorm/gen/issues?q=is%3Aopen+is%3Aissue)
-[![ClosedIssue](https://img.shields.io/github/issues-closed/go-gorm/gen)](https://github.com/go-gorm/gen/issues?q=is%3Aissue+is%3Aclosed)
-[![TODOs](https://badgen.net/https/api.tickgit.com/badgen/github.com/go-gorm/gen)](https://www.tickgit.com/browse?repo=github.com/go-gorm/gen)
-[![Go.Dev reference](https://img.shields.io/badge/go.dev-reference-blue?logo=go&logoColor=white)](https://pkg.go.dev/github.com/wubin1989/gen?tab=doc)
-
-## Overview
-
-- Idiomatic & Reusable API from Dynamic Raw SQL
-- 100% Type-safe DAO API without `interface{}`
-- Database To Struct follows GORM conventions
-- GORM under the hood, supports all features, plugins, DBMS that GORM supports
-
-## Getting Started
-
-* Gen Guides [https://github.com/wubin1989/gen/index.html](https://github.com/wubin1989/gen/index.html)
-* GORM Guides [http://github.com/wubin1989/docs](http://github.com/wubin1989/docs)
-
-## Maintainers
-
-[@riverchu](https://github.com/riverchu) [@iDer](https://github.com/idersec) [@qqxhb](https://github.com/qqxhb) [@dino-ma](https://github.com/dino-ma)
-
-[@jinzhu](https://github.com/jinzhu)
-
-## Contributing
-
-[You can help to deliver a better GORM/Gen, check out things you can do](https://github.com/wubin1989/contribute.html)
-
-## License
-
-Released under the [MIT License](https://github.com/go-gorm/gen/blob/master/License)
diff --git a/toolkit/gormgen/condition.go b/toolkit/gormgen/condition.go
deleted file mode 100644
index badcd3bc..00000000
--- a/toolkit/gormgen/condition.go
+++ /dev/null
@@ -1,66 +0,0 @@
-package gormgen
-
-import (
- "fmt"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
- "github.com/wubin1989/datatypes"
- "github.com/wubin1989/gorm/clause"
-)
-
-// Cond convert expression array to condition array
-func Cond(exprs ...clause.Expression) []Condition {
- return exprToCondition(exprs...)
-}
-
-var _ Condition = &condContainer{}
-
-type condContainer struct {
- value interface{}
- err error
-}
-
-func (c *condContainer) BeCond() interface{} { return c.value }
-func (c *condContainer) CondError() error { return c.err }
-
-func exprToCondition(exprs ...clause.Expression) []Condition {
- conds := make([]Condition, 0, len(exprs))
- for _, e := range exprs {
- switch e := e.(type) {
- case *datatypes.JSONQueryExpression, *datatypes.JSONOverlapsExpression, *datatypes.JSONArrayExpression:
- conds = append(conds, &condContainer{value: e})
- default:
- conds = append(conds, &condContainer{err: fmt.Errorf("unsupported Expression %T to converted to Condition", e)})
- }
- }
- return conds
-}
-
-func condToExpression(conds []Condition) ([]clause.Expression, error) {
- if len(conds) == 0 {
- return nil, nil
- }
- exprs := make([]clause.Expression, 0, len(conds))
- for _, cond := range conds {
- if cond == nil {
- continue
- }
- if err := cond.CondError(); err != nil {
- return nil, err
- }
-
- switch cond.(type) {
- case *condContainer, field.Expr, SubQuery:
- default:
- return nil, fmt.Errorf("unsupported condition: %+v", cond)
- }
-
- switch e := cond.BeCond().(type) {
- case []clause.Expression:
- exprs = append(exprs, e...)
- case clause.Expression:
- exprs = append(exprs, e)
- }
- }
- return exprs, nil
-}
diff --git a/toolkit/gormgen/config.go b/toolkit/gormgen/config.go
deleted file mode 100644
index c15c97c8..00000000
--- a/toolkit/gormgen/config.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package gormgen
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/gobwas/glob"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/utils/tests"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/model"
-)
-
-// GenerateMode generate mode
-type GenerateMode uint
-
-const (
- // WithDefaultQuery create default query in generated code
- WithDefaultQuery GenerateMode = 1 << iota
-
- // WithoutContext generate code without context constrain
- WithoutContext
-
- // WithQueryInterface generate code with exported interface object
- WithQueryInterface
-)
-
-// Config generator's basic configuration
-type Config struct {
- db *gorm.DB // db connection
-
- RootDir string // project root path
- OutPath string // query code path
- OutFile string // query code file name, default: gen.go
- ModelPkgPath string // generated model code's package name
- DtoPkgPath string // generated dto code's package name
- WithUnitTest bool // generate unit test for query code
-
- // generate model global configuration
- FieldNullable bool // generate pointer when field is nullable
- FieldCoverable bool // generate pointer when field has default value, to fix problem zero value cannot be assign: https://gorm.io/docs/create.html#Default-Values
- FieldSignable bool // detect integer field's unsigned type, adjust generated data type
- FieldWithIndexTag bool // generate with gorm index tag
- FieldWithTypeTag bool // generate with gorm column type tag
-
- Mode GenerateMode // generate mode
-
- queryPkgName string // generated query code's package name
- modelPkgPath string // model pkg path in target project
- dtoPkgPath string // dto pkg path in target project
- dbNameOpts []model.SchemaNameOpt
- importPkgPaths []string
-
- // name strategy for syncing table from db
- tableNameNS func(tableName string) (targetTableName string)
- modelNameNS func(tableName string) (modelName string)
- fileNameNS func(tableName string) (fileName string)
-
- dataTypeMap map[string]func(columnType gorm.ColumnType) (dataType string)
- fieldJSONTagNS func(columnName string, columnType string) (tagContent string)
-
- modelOpts []ModelOpt
-
- ConfigPackage string
-
- FilterTableGlob glob.Glob
- ExcludeTableGlob glob.Glob
- GenGenGo bool
-}
-
-// WithOpts set global model options
-func (cfg *Config) WithOpts(opts ...ModelOpt) {
- if cfg.modelOpts == nil {
- cfg.modelOpts = opts
- } else {
- cfg.modelOpts = append(cfg.modelOpts, opts...)
- }
-}
-
-// WithDbNameOpts set get database name function
-func (cfg *Config) WithDbNameOpts(opts ...model.SchemaNameOpt) {
- if cfg.dbNameOpts == nil {
- cfg.dbNameOpts = opts
- } else {
- cfg.dbNameOpts = append(cfg.dbNameOpts, opts...)
- }
-}
-
-// WithTableNameStrategy specify table name naming strategy, only work when syncing table from db
-func (cfg *Config) WithTableNameStrategy(ns func(tableName string) (targetTableName string)) {
- cfg.tableNameNS = ns
-}
-
-// WithModelNameStrategy specify model struct name naming strategy, only work when syncing table from db
-func (cfg *Config) WithModelNameStrategy(ns func(tableName string) (modelName string)) {
- cfg.modelNameNS = ns
-}
-
-// WithFileNameStrategy specify file name naming strategy, only work when syncing table from db
-func (cfg *Config) WithFileNameStrategy(ns func(tableName string) (fileName string)) {
- cfg.fileNameNS = ns
-}
-
-// WithDataTypeMap specify data type mapping relationship, only work when syncing table from db
-func (cfg *Config) WithDataTypeMap(newMap map[string]func(columnType gorm.ColumnType) (dataType string)) {
- cfg.dataTypeMap = newMap
-}
-
-// WithJSONTagNameStrategy specify json tag naming strategy
-func (cfg *Config) WithJSONTagNameStrategy(ns func(columnName string, columnType string) (tagContent string)) {
- cfg.fieldJSONTagNS = ns
-}
-
-// WithImportPkgPath specify import package path
-func (cfg *Config) WithImportPkgPath(paths ...string) {
- for i, path := range paths {
- path = strings.TrimSpace(path)
- if len(path) > 0 && path[0] != '"' && path[len(path)-1] != '"' { // without quote
- path = `"` + path + `"`
- }
- paths[i] = path
- }
- cfg.importPkgPaths = append(cfg.importPkgPaths, paths...)
-}
-
-// Revise format path and db
-func (cfg *Config) Revise() (err error) {
- if strings.TrimSpace(cfg.ModelPkgPath) == "" {
- cfg.ModelPkgPath = model.DefaultModelPkg
- }
- if strings.TrimSpace(cfg.DtoPkgPath) == "" {
- cfg.DtoPkgPath = model.DefaultDtoPkg
- }
-
- cfg.OutPath, err = filepath.Abs(cfg.OutPath)
- if err != nil {
- return fmt.Errorf("outpath is invalid: %w", err)
- }
- if cfg.OutPath == "" {
- cfg.OutPath = fmt.Sprintf(".%squery%s", string(os.PathSeparator), string(os.PathSeparator))
- }
- if cfg.OutFile == "" {
- cfg.OutFile = filepath.Join(cfg.OutPath, "gen.go")
- } else if !strings.Contains(cfg.OutFile, string(os.PathSeparator)) {
- cfg.OutFile = filepath.Join(cfg.OutPath, cfg.OutFile)
- }
- cfg.queryPkgName = filepath.Base(cfg.OutPath)
-
- if cfg.db == nil {
- cfg.db, _ = gorm.Open(tests.DummyDialector{})
- }
-
- return nil
-}
-
-func (cfg *Config) judgeMode(mode GenerateMode) bool { return cfg.Mode&mode != 0 }
diff --git a/toolkit/gormgen/do.go b/toolkit/gormgen/do.go
deleted file mode 100644
index f1bbbc24..00000000
--- a/toolkit/gormgen/do.go
+++ /dev/null
@@ -1,1055 +0,0 @@
-package gormgen
-
-import (
- "context"
- "database/sql"
- "fmt"
- "reflect"
- "strings"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/callbacks"
- "github.com/wubin1989/gorm/clause"
- "github.com/wubin1989/gorm/schema"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/helper"
-)
-
-// ResultInfo query/execute info
-type ResultInfo struct {
- RowsAffected int64
- Error error
-}
-
-var _ Dao = new(DO)
-
-// DO (data object): implement basic query methods
-// the structure embedded with a *gorm.DB, and has a element item "alias" will be used when used as a sub query
-type DO struct {
- *DOConfig
- db *gorm.DB
- alias string // for subquery
- modelType reflect.Type
- tableName string
-
- backfillData interface{}
-}
-
-func (d DO) getInstance(db *gorm.DB) *DO {
- d.db = db
- return &d
-}
-
-type doOptions func(*gorm.DB) *gorm.DB
-
-var (
- // Debug use DB in debug mode
- Debug doOptions = func(db *gorm.DB) *gorm.DB { return db.Debug() }
-)
-
-// UseDB specify a db connection(*gorm.DB)
-func (d *DO) UseDB(db *gorm.DB, opts ...DOOption) {
- db = db.Session(&gorm.Session{Context: context.Background()})
- d.db = db
- config := &DOConfig{}
- for _, opt := range opts {
- if opt != nil {
- if applyErr := opt.Apply(config); applyErr != nil {
- panic(applyErr)
- }
- }
- }
- d.DOConfig = config
-}
-
-// ReplaceDB replace db connection
-func (d *DO) ReplaceDB(db *gorm.DB) {
- d.db = db.Session(&gorm.Session{})
-}
-
-// ReplaceConnPool replace db connection pool
-func (d *DO) ReplaceConnPool(pool gorm.ConnPool) {
- d.db = d.db.Session(&gorm.Session{Initialized: true}).Session(&gorm.Session{})
- d.db.Statement.ConnPool = pool
-}
-
-// UseModel specify a data model structure as a source for table name
-func (d *DO) UseModel(model interface{}) {
- d.modelType = d.indirect(model)
-
- err := d.db.Statement.Parse(model)
- if err != nil {
- panic(fmt.Errorf("Cannot parse model: %+v\n%w", model, err))
- }
- d.tableName = d.db.Statement.Schema.Table
-}
-
-func (d *DO) indirect(value interface{}) reflect.Type {
- mt := reflect.TypeOf(value)
- if mt.Kind() == reflect.Ptr {
- mt = mt.Elem()
- }
- return mt
-}
-
-// UseTable specify table name
-func (d *DO) UseTable(tableName string) {
- d.db = d.db.Table(tableName).Session(new(gorm.Session))
- //d.db.Statement.Schema.Table=tableName
- d.tableName = tableName
-}
-
-// TableName return table name
-func (d DO) TableName() string {
- return d.tableName
-}
-
-// Returning backfill data
-func (d DO) Returning(value interface{}, columns ...string) Dao {
- d.backfillData = value
-
- var targetCulumns []clause.Column
- for _, column := range columns {
- targetCulumns = append(targetCulumns, clause.Column{Name: column})
- }
- d.db = d.db.Clauses(clause.Returning{Columns: targetCulumns})
- return &d
-}
-
-// Session replace db with new session
-func (d *DO) Session(config *gorm.Session) Dao { return d.getInstance(d.db.Session(config)) }
-
-// UnderlyingDB return the underlying database connection
-func (d *DO) UnderlyingDB() *gorm.DB { return d.underlyingDB() }
-
-// Quote return qutoed data
-func (d *DO) Quote(raw string) string { return d.db.Statement.Quote(raw) }
-
-// Build implement the interface of claues.Expression
-// only call WHERE clause's Build
-func (d *DO) Build(builder clause.Builder) {
- for _, e := range d.buildCondition() {
- e.Build(builder)
- }
-}
-
-func (d *DO) buildCondition() []clause.Expression {
- return d.db.Statement.BuildCondition(d.db)
-}
-
-// underlyingDO return self
-func (d *DO) underlyingDO() *DO { return d }
-
-// underlyingDB return self.db
-func (d *DO) underlyingDB() *gorm.DB { return d.db }
-
-func (d *DO) withError(err error) *DO {
- if err == nil {
- return d
- }
-
- newDB := d.db.Session(new(gorm.Session))
- _ = newDB.AddError(err)
- return d.getInstance(newDB)
-}
-
-// BeCond implements Condition
-func (d *DO) BeCond() interface{} { return d.buildCondition() }
-
-// CondError implements Condition
-func (d *DO) CondError() error { return nil }
-
-// Debug return a DO with db in debug mode
-func (d *DO) Debug() Dao { return d.getInstance(d.db.Debug()) }
-
-// WithContext return a DO with db with context
-func (d *DO) WithContext(ctx context.Context) Dao { return d.getInstance(d.db.WithContext(ctx)) }
-
-// Clauses specify Clauses
-func (d *DO) Clauses(conds ...clause.Expression) Dao {
- if err := checkConds(conds); err != nil {
- newDB := d.db.Session(new(gorm.Session))
- _ = newDB.AddError(err)
- return d.getInstance(newDB)
- }
- return d.getInstance(d.db.Clauses(conds...))
-}
-
-// As alias cannot be heired, As must used on tail
-func (d DO) As(alias string) Dao {
- d.alias = alias
- d.db = d.db.Table(fmt.Sprintf("%s AS %s", d.Quote(d.TableName()), d.Quote(alias)))
- return &d
-}
-
-// Alias return alias name
-func (d *DO) Alias() string { return d.alias }
-
-// Columns return columns for Subquery
-func (*DO) Columns(cols ...field.Expr) Columns { return cols }
-
-// ======================== chainable api ========================
-
-// Not ...
-func (d *DO) Not(conds ...Condition) Dao {
- exprs, err := condToExpression(conds)
- if err != nil {
- return d.withError(err)
- }
- if len(exprs) == 0 {
- return d
- }
- return d.getInstance(d.db.Clauses(clause.Where{Exprs: []clause.Expression{clause.Not(exprs...)}}))
-}
-
-// Or ...
-func (d *DO) Or(conds ...Condition) Dao {
- exprs, err := condToExpression(conds)
- if err != nil {
- return d.withError(err)
- }
- if len(exprs) == 0 {
- return d
- }
- return d.getInstance(d.db.Clauses(clause.Where{Exprs: []clause.Expression{clause.Or(clause.And(exprs...))}}))
-}
-
-// Select ...
-func (d *DO) Select(columns ...field.Expr) Dao {
- if len(columns) == 0 {
- return d.getInstance(d.db.Clauses(clause.Select{}))
- }
- query, args := buildExpr4Select(d.db.Statement, columns...)
- return d.getInstance(d.db.Select(query, args...))
-}
-
-// Where ...
-func (d *DO) Where(conds ...Condition) Dao {
- exprs, err := condToExpression(conds)
- if err != nil {
- return d.withError(err)
- }
- if len(exprs) == 0 {
- return d
- }
- return d.getInstance(d.db.Clauses(clause.Where{Exprs: exprs}))
-}
-
-// Order ...
-func (d *DO) Order(columns ...field.Expr) Dao {
- // lazy build Columns
- // if c, ok := d.db.Statement.Clauses[clause.OrderBy{}.Name()]; ok {
- // if order, ok := c.Expression.(clause.OrderBy); ok {
- // if expr, ok := order.Expression.(clause.CommaExpression); ok {
- // expr.Exprs = append(expr.Exprs, toExpression(columns)...)
- // return d.newInstance(d.db.Clauses(clause.OrderBy{Expression: expr}))
- // }
- // }
- // }
- // return d.newInstance(d.db.Clauses(clause.OrderBy{Expression: clause.CommaExpression{Exprs: toExpression(columns)}}))
- if len(columns) == 0 {
- return d
- }
- return d.getInstance(d.db.Order(d.toOrderValue(columns...)))
-}
-
-func (d *DO) toOrderValue(columns ...field.Expr) string {
- // eager build Columns
- stmt := &gorm.Statement{DB: d.db.Statement.DB, Table: d.db.Statement.Table, Schema: d.db.Statement.Schema}
-
- for i, c := range columns {
- if i != 0 {
- stmt.WriteByte(',')
- }
- c.Build(stmt)
- }
-
- return d.db.Dialector.Explain(stmt.SQL.String(), stmt.Vars...)
-}
-
-// Distinct ...
-func (d *DO) Distinct(columns ...field.Expr) Dao {
- return d.getInstance(d.db.Distinct(toInterfaceSlice(toColExprFullName(d.db.Statement, columns...))...))
-}
-
-// Omit ...
-func (d *DO) Omit(columns ...field.Expr) Dao {
- if len(columns) == 0 {
- return d
- }
- return d.getInstance(d.db.Omit(getColumnName(columns...)...))
-}
-
-// Group ...
-func (d *DO) Group(columns ...field.Expr) Dao {
- if len(columns) == 0 {
- return d
- }
-
- stmt := &gorm.Statement{DB: d.db.Statement.DB, Table: d.db.Statement.Table, Schema: d.db.Statement.Schema}
-
- for i, c := range columns {
- if i != 0 {
- stmt.WriteByte(',')
- }
- c.Build(stmt)
- }
-
- return d.getInstance(d.db.Group(stmt.SQL.String()))
-}
-
-// Having ...
-func (d *DO) Having(conds ...Condition) Dao {
- exprs, err := condToExpression(conds)
- if err != nil {
- return d.withError(err)
- }
- if len(exprs) == 0 {
- return d
- }
- return d.getInstance(d.db.Clauses(clause.GroupBy{Having: exprs}))
-}
-
-// Limit ...
-func (d *DO) Limit(limit int) Dao {
- return d.getInstance(d.db.Limit(limit))
-}
-
-// Offset ...
-func (d *DO) Offset(offset int) Dao {
- return d.getInstance(d.db.Offset(offset))
-}
-
-// Scopes ...
-func (d *DO) Scopes(funcs ...func(Dao) Dao) Dao {
- fcs := make([]func(*gorm.DB) *gorm.DB, len(funcs))
- for i, f := range funcs {
- sf := f
- fcs[i] = func(tx *gorm.DB) *gorm.DB { return sf(d.getInstance(tx)).(*DO).db }
- }
- return d.getInstance(d.db.Scopes(fcs...))
-}
-
-// Unscoped ...
-func (d *DO) Unscoped() Dao {
- return d.getInstance(d.db.Unscoped())
-}
-
-// Join ...
-func (d *DO) Join(table schema.Tabler, conds ...field.Expr) Dao {
- return d.join(table, clause.InnerJoin, conds)
-}
-
-// LeftJoin ...
-func (d *DO) LeftJoin(table schema.Tabler, conds ...field.Expr) Dao {
- return d.join(table, clause.LeftJoin, conds)
-}
-
-// RightJoin ...
-func (d *DO) RightJoin(table schema.Tabler, conds ...field.Expr) Dao {
- return d.join(table, clause.RightJoin, conds)
-}
-
-func (d *DO) join(table schema.Tabler, joinType clause.JoinType, conds []field.Expr) Dao {
- if len(conds) == 0 {
- return d.withError(ErrEmptyCondition)
- }
-
- join := clause.Join{
- Type: joinType,
- Table: clause.Table{Name: table.TableName()},
- ON: clause.Where{Exprs: toExpression(conds...)},
- }
- if do, ok := table.(Dao); ok {
- join.Expression = helper.NewJoinTblExpr(join, Table(do).underlyingDB().Statement.TableExpr)
- }
- if al, ok := table.(interface{ Alias() string }); ok {
- join.Table.Alias = al.Alias()
- }
-
- from := getFromClause(d.db)
- from.Joins = append(from.Joins, join)
- return d.getInstance(d.db.Clauses(from))
-}
-
-// Attrs ...
-func (d *DO) Attrs(attrs ...field.AssignExpr) Dao {
- if len(attrs) == 0 {
- return d
- }
- return d.getInstance(d.db.Attrs(d.attrsValue(attrs)...))
-}
-
-// Assign ...
-func (d *DO) Assign(attrs ...field.AssignExpr) Dao {
- if len(attrs) == 0 {
- return d
- }
- return d.getInstance(d.db.Assign(d.attrsValue(attrs)...))
-}
-
-func (d *DO) attrsValue(attrs []field.AssignExpr) []interface{} {
- values := make([]interface{}, 0, len(attrs))
- for _, attr := range attrs {
- if expr, ok := attr.AssignExpr().(clause.Eq); ok {
- values = append(values, expr)
- }
- }
- return values
-}
-
-// Joins ...
-func (d *DO) Joins(field field.RelationField) Dao {
- var args []interface{}
-
- if conds := field.GetConds(); len(conds) > 0 {
- var exprs []clause.Expression
- for _, oe := range toExpression(conds...) {
- switch e := oe.(type) {
- case clause.Eq:
- if c, ok := e.Column.(clause.Column); ok {
- c.Table = field.Name()
- e.Column = c
- }
- exprs = append(exprs, e)
- case clause.Neq:
- if c, ok := e.Column.(clause.Column); ok {
- c.Table = field.Name()
- e.Column = c
- }
- exprs = append(exprs, e)
- case clause.Gt:
- if c, ok := e.Column.(clause.Column); ok {
- c.Table = field.Name()
- e.Column = c
- }
- exprs = append(exprs, e)
- case clause.Gte:
- if c, ok := e.Column.(clause.Column); ok {
- c.Table = field.Name()
- e.Column = c
- }
- exprs = append(exprs, e)
- case clause.Lt:
- if c, ok := e.Column.(clause.Column); ok {
- c.Table = field.Name()
- e.Column = c
- }
- exprs = append(exprs, e)
- case clause.Lte:
- if c, ok := e.Column.(clause.Column); ok {
- c.Table = field.Name()
- e.Column = c
- }
- exprs = append(exprs, e)
- case clause.Like:
- if c, ok := e.Column.(clause.Column); ok {
- c.Table = field.Name()
- e.Column = c
- }
- exprs = append(exprs, e)
- }
- }
-
- args = append(args, d.db.Clauses(clause.Where{
- Exprs: exprs,
- }))
- }
- if columns := field.GetSelects(); len(columns) > 0 {
- colNames := make([]string, len(columns))
- for i, c := range columns {
- colNames[i] = string(c.ColumnName())
- }
- args = append(args, func(db *gorm.DB) *gorm.DB {
- return db.Select(colNames)
- })
- }
- if columns := field.GetOrderCol(); len(columns) > 0 {
- var os []string
- for _, oe := range columns {
- switch e := oe.RawExpr().(type) {
- case clause.Expr:
- vs := []interface{}{}
- for _, v := range e.Vars {
- if c, ok := v.(clause.Column); ok {
- vs = append(vs, clause.Column{
- Table: field.Name(),
- Name: c.Name,
- Alias: c.Alias,
- Raw: c.Raw,
- })
- }
- }
- e.Vars = vs
- newStmt := &gorm.Statement{DB: d.db.Statement.DB, Table: d.db.Statement.Table, Schema: d.db.Statement.Schema}
- e.Build(newStmt)
- os = append(os, newStmt.SQL.String())
- }
- }
- args = append(args, d.db.Order(strings.Join(os, ",")))
- }
- if clauses := field.GetClauses(); len(clauses) > 0 {
- args = append(args, func(db *gorm.DB) *gorm.DB {
- return db.Clauses(clauses...)
- })
- }
- if funcs := field.GetScopes(); len(funcs) > 0 {
- for _, f := range funcs {
- args = append(args, (func(*gorm.DB) *gorm.DB)(f))
- }
- }
- if offset, limit := field.GetPage(); offset|limit != 0 {
- args = append(args, func(db *gorm.DB) *gorm.DB {
- return db.Offset(offset).Limit(limit)
- })
- }
-
- return d.getInstance(d.db.Joins(field.Path(), args...))
-}
-
-// Preload ...
-func (d *DO) Preload(field field.RelationField) Dao {
- var args []interface{}
- if conds := field.GetConds(); len(conds) > 0 {
- args = append(args, toExpressionInterface(conds...)...)
- }
- if columns := field.GetSelects(); len(columns) > 0 {
- colNames := make([]string, len(columns))
- for i, c := range columns {
- colNames[i] = string(c.ColumnName())
- }
- args = append(args, func(db *gorm.DB) *gorm.DB {
- return db.Select(colNames)
- })
- }
- if columns := field.GetOrderCol(); len(columns) > 0 {
- args = append(args, func(db *gorm.DB) *gorm.DB {
- return db.Order(d.toOrderValue(columns...))
- })
- }
- if clauses := field.GetClauses(); len(clauses) > 0 {
- args = append(args, func(db *gorm.DB) *gorm.DB {
- return db.Clauses(clauses...)
- })
- }
- if funcs := field.GetScopes(); len(funcs) > 0 {
- for _, f := range funcs {
- args = append(args, (func(*gorm.DB) *gorm.DB)(f))
- }
- }
- if offset, limit := field.GetPage(); offset|limit != 0 {
- args = append(args, func(db *gorm.DB) *gorm.DB {
- return db.Offset(offset).Limit(limit)
- })
- }
- return d.getInstance(d.db.Preload(field.Path(), args...))
-}
-
-// UpdateFrom specify update sub query
-func (d *DO) UpdateFrom(q SubQuery) Dao {
- var tableName strings.Builder
- d.db.Statement.QuoteTo(&tableName, d.TableName())
- if d.alias != "" {
- tableName.WriteString(" AS ")
- d.db.Statement.QuoteTo(&tableName, d.alias)
- }
-
- tableName.WriteByte(',')
- if _, ok := q.underlyingDB().Statement.Clauses["SELECT"]; ok || len(q.underlyingDB().Statement.Selects) > 0 {
- tableName.WriteString("(" + q.underlyingDB().ToSQL(func(tx *gorm.DB) *gorm.DB { return tx.Table(q.underlyingDO().TableName()).Find(nil) }) + ")")
- } else {
- d.db.Statement.QuoteTo(&tableName, q.underlyingDO().TableName())
- }
- if alias := q.underlyingDO().alias; alias != "" {
- tableName.WriteString(" AS ")
- d.db.Statement.QuoteTo(&tableName, alias)
- }
-
- return d.getInstance(d.db.Clauses(clause.Update{Table: clause.Table{Name: tableName.String(), Raw: true}}))
-}
-
-func getFromClause(db *gorm.DB) *clause.From {
- if db == nil || db.Statement == nil {
- return &clause.From{}
- }
- c, ok := db.Statement.Clauses[clause.From{}.Name()]
- if !ok || c.Expression == nil {
- return &clause.From{}
- }
- from, ok := c.Expression.(clause.From)
- if !ok {
- return &clause.From{}
- }
- return &from
-}
-
-// ======================== finisher api ========================
-
-// Create ...
-func (d *DO) Create(value interface{}) error {
- return d.db.Create(value).Error
-}
-
-// CreateInBatches ...
-func (d *DO) CreateInBatches(value interface{}, batchSize int) error {
- return d.db.CreateInBatches(value, batchSize).Error
-}
-
-// Save ...
-func (d *DO) Save(value interface{}) error {
- return d.db.Clauses(clause.OnConflict{UpdateAll: true}).Create(value).Error
-}
-
-// First ...
-func (d *DO) First() (result interface{}, err error) {
- return d.singleQuery(d.db.First)
-}
-
-// Take ...
-func (d *DO) Take() (result interface{}, err error) {
- return d.singleQuery(d.db.Take)
-}
-
-// Last ...
-func (d *DO) Last() (result interface{}, err error) {
- return d.singleQuery(d.db.Last)
-}
-
-func (d *DO) singleQuery(query func(dest interface{}, conds ...interface{}) *gorm.DB) (result interface{}, err error) {
- if d.modelType == nil {
- return d.singleScan()
- }
-
- result = d.newResultPointer()
- if err := query(result).Error; err != nil {
- return nil, err
- }
- return result, nil
-}
-
-func (d *DO) singleScan() (result interface{}, err error) {
- result = map[string]interface{}{}
- err = d.db.Scan(result).Error
- return
-}
-
-// Find ...
-func (d *DO) Find() (results interface{}, err error) {
- return d.multiQuery(d.db.Find)
-}
-
-func (d *DO) multiQuery(query func(dest interface{}, conds ...interface{}) *gorm.DB) (results interface{}, err error) {
- if d.modelType == nil {
- return d.findToMap()
- }
-
- resultsPtr := d.newResultSlicePointer()
- err = query(resultsPtr).Error
- return reflect.Indirect(reflect.ValueOf(resultsPtr)).Interface(), err
-}
-
-func (d *DO) findToMap() (interface{}, error) {
- var results []map[string]interface{}
- err := d.db.Find(&results).Error
- return results, err
-}
-
-// FindInBatches ...
-func (d *DO) FindInBatches(dest interface{}, batchSize int, fc func(tx Dao, batch int) error) error {
- return d.db.FindInBatches(dest, batchSize, func(tx *gorm.DB, batch int) error { return fc(d.getInstance(tx), batch) }).Error
-}
-
-// FirstOrInit ...
-func (d *DO) FirstOrInit() (result interface{}, err error) {
- return d.singleQuery(d.db.FirstOrInit)
-}
-
-// FirstOrCreate ...
-func (d *DO) FirstOrCreate() (result interface{}, err error) {
- return d.singleQuery(d.db.FirstOrCreate)
-}
-
-// Update ...
-func (d *DO) Update(column field.Expr, value interface{}) (info ResultInfo, err error) {
- tx := d.db.Model(d.newResultPointer())
- columnStr := column.BuildColumn(d.db.Statement, field.WithoutQuote).String()
-
- var result *gorm.DB
- switch value := value.(type) {
- case field.AssignExpr:
- result = tx.Update(columnStr, value.AssignExpr())
- case SubQuery:
- result = tx.Update(columnStr, value.underlyingDB())
- default:
- result = tx.Update(columnStr, value)
- }
- return ResultInfo{RowsAffected: result.RowsAffected, Error: result.Error}, result.Error
-}
-
-// UpdateSimple ...
-func (d *DO) UpdateSimple(columns ...field.AssignExpr) (info ResultInfo, err error) {
- if len(columns) == 0 {
- return
- }
-
- result := d.db.Model(d.newResultPointer()).Clauses(d.assignSet(columns)).Omit("*").Updates(map[string]interface{}{})
- return ResultInfo{RowsAffected: result.RowsAffected, Error: result.Error}, result.Error
-}
-
-// Updates ...
-func (d *DO) Updates(value interface{}) (info ResultInfo, err error) {
- var rawTyp, valTyp reflect.Type
-
- rawTyp = reflect.TypeOf(value)
- if rawTyp.Kind() == reflect.Ptr {
- valTyp = rawTyp.Elem()
- } else {
- valTyp = rawTyp
- }
-
- tx := d.db
- if d.backfillData != nil {
- tx = tx.Model(d.backfillData)
- }
- switch {
- case valTyp != d.modelType: // different type with model
- if d.backfillData == nil {
- tx = tx.Model(d.newResultPointer())
- }
- case rawTyp.Kind() == reflect.Ptr: // ignore ptr value
- default: // for fixing "reflect.Value.Addr of unaddressable value" panic
- ptr := reflect.New(d.modelType)
- ptr.Elem().Set(reflect.ValueOf(value))
- value = ptr.Interface()
- }
- result := tx.Updates(value)
- return ResultInfo{RowsAffected: result.RowsAffected, Error: result.Error}, result.Error
-}
-
-// UpdateColumn ...
-func (d *DO) UpdateColumn(column field.Expr, value interface{}) (info ResultInfo, err error) {
- tx := d.db.Model(d.newResultPointer())
- columnStr := column.BuildColumn(d.db.Statement, field.WithoutQuote).String()
-
- var result *gorm.DB
- switch value := value.(type) {
- case field.Expr:
- result = tx.UpdateColumn(columnStr, value.RawExpr())
- case SubQuery:
- result = d.db.UpdateColumn(columnStr, value.underlyingDB())
- default:
- result = d.db.UpdateColumn(columnStr, value)
- }
- return ResultInfo{RowsAffected: result.RowsAffected, Error: result.Error}, result.Error
-}
-
-// UpdateColumnSimple ...
-func (d *DO) UpdateColumnSimple(columns ...field.AssignExpr) (info ResultInfo, err error) {
- if len(columns) == 0 {
- return
- }
-
- result := d.db.Model(d.newResultPointer()).Clauses(d.assignSet(columns)).Omit("*").UpdateColumns(map[string]interface{}{})
- return ResultInfo{RowsAffected: result.RowsAffected, Error: result.Error}, result.Error
-}
-
-// UpdateColumns ...
-func (d *DO) UpdateColumns(value interface{}) (info ResultInfo, err error) {
- result := d.db.Model(d.newResultPointer()).UpdateColumns(value)
- return ResultInfo{RowsAffected: result.RowsAffected, Error: result.Error}, result.Error
-}
-
-// assignSet fetch all set
-func (d *DO) assignSet(exprs []field.AssignExpr) (set clause.Set) {
- for _, expr := range exprs {
- column := clause.Column{Table: d.alias, Name: string(expr.ColumnName())}
- switch e := expr.AssignExpr().(type) {
- case clause.Expr:
- set = append(set, clause.Assignment{Column: column, Value: e})
- case clause.Eq:
- set = append(set, clause.Assignment{Column: column, Value: e.Value})
- case clause.Set:
- set = append(set, e...)
- }
- }
-
- stmt := d.db.Session(&gorm.Session{}).Statement
- stmt.Dest = map[string]interface{}{}
- return append(set, callbacks.ConvertToAssignments(stmt)...)
-}
-
-// Delete ...
-func (d *DO) Delete(models ...interface{}) (info ResultInfo, err error) {
- var result *gorm.DB
- if len(models) == 0 || reflect.ValueOf(models[0]).Len() == 0 {
- result = d.db.Model(d.newResultPointer()).Delete(reflect.New(d.modelType).Interface())
- } else {
- targets := reflect.MakeSlice(reflect.SliceOf(reflect.PtrTo(d.modelType)), 0, len(models))
- value := reflect.ValueOf(models[0])
- for i := 0; i < value.Len(); i++ {
- targets = reflect.Append(targets, value.Index(i))
- }
- result = d.db.Delete(targets.Interface())
- }
- return ResultInfo{RowsAffected: result.RowsAffected, Error: result.Error}, result.Error
-}
-
-// Count ...
-func (d *DO) Count() (count int64, err error) {
- return count, d.db.Session(&gorm.Session{}).Model(d.newResultPointer()).Count(&count).Error
-}
-
-// Row ...
-func (d *DO) Row() *sql.Row {
- return d.db.Model(d.newResultPointer()).Row()
-}
-
-// Rows ...
-func (d *DO) Rows() (*sql.Rows, error) {
- return d.db.Model(d.newResultPointer()).Rows()
-}
-
-// Scan ...
-func (d *DO) Scan(dest interface{}) error {
- return d.db.Model(d.newResultPointer()).Scan(dest).Error
-}
-
-// Fetch ...
-func (d *DO) Fetch(dest interface{}) error {
- return d.db.Model(d.newResultPointer()).Find(dest).Error
-}
-
-// Pluck ...
-func (d *DO) Pluck(column field.Expr, dest interface{}) error {
- return d.db.Model(d.newResultPointer()).Pluck(column.ColumnName().String(), dest).Error
-}
-
-// ScanRows ...
-func (d *DO) ScanRows(rows *sql.Rows, dest interface{}) error {
- return d.db.Model(d.newResultPointer()).ScanRows(rows, dest)
-}
-
-// WithResult ...
-func (d DO) WithResult(fc func(tx Dao)) ResultInfo {
- d.db = d.db.Set("", "")
- fc(&d)
- return ResultInfo{RowsAffected: d.db.RowsAffected, Error: d.db.Error}
-}
-
-func (d *DO) newResultPointer() interface{} {
- if d.backfillData != nil {
- return d.backfillData
- }
- if d.modelType == nil {
- return nil
- }
- return reflect.New(d.modelType).Interface()
-}
-
-func (d *DO) newResultSlicePointer() interface{} {
- return reflect.New(reflect.SliceOf(reflect.PtrTo(d.modelType))).Interface()
-}
-
-func toColExprFullName(stmt *gorm.Statement, columns ...field.Expr) []string {
- return buildColExpr(stmt, columns, field.WithAll)
-}
-
-func getColumnName(columns ...field.Expr) (result []string) {
- for _, c := range columns {
- result = append(result, c.ColumnName().String())
- }
- return result
-}
-
-func buildColExpr(stmt *gorm.Statement, cols []field.Expr, opts ...field.BuildOpt) []string {
- results := make([]string, len(cols))
- for i, c := range cols {
- switch c.RawExpr().(type) {
- case clause.Column:
- results[i] = c.BuildColumn(stmt, opts...).String()
- case clause.Expression:
- sql, args := c.BuildWithArgs(stmt)
- results[i] = stmt.Dialector.Explain(sql.String(), args...)
- }
- }
- return results
-}
-
-func buildExpr4Select(stmt *gorm.Statement, exprs ...field.Expr) (query string, args []interface{}) {
- if len(exprs) == 0 {
- return "", nil
- }
-
- var queryItems []string
- for _, e := range exprs {
- sql, vars := e.BuildWithArgs(stmt)
- queryItems = append(queryItems, sql.String())
- args = append(args, vars...)
- }
- if len(args) == 0 {
- return queryItems[0], toInterfaceSlice(queryItems[1:])
- }
- return strings.Join(queryItems, ","), args
-}
-
-func toExpression(exprs ...field.Expr) []clause.Expression {
- result := make([]clause.Expression, len(exprs))
- for i, e := range exprs {
- result[i] = singleExpr(e)
- }
- return result
-}
-
-func toExpressionInterface(exprs ...field.Expr) []interface{} {
- result := make([]interface{}, len(exprs))
- for i, e := range exprs {
- result[i] = singleExpr(e)
- }
- return result
-}
-
-func singleExpr(e field.Expr) clause.Expression {
- switch v := e.RawExpr().(type) {
- case clause.Expression:
- return v
- case clause.Column:
- return clause.NamedExpr{SQL: "?", Vars: []interface{}{v}}
- default:
- return clause.Expr{}
- }
-}
-
-func toInterfaceSlice(value interface{}) []interface{} {
- switch v := value.(type) {
- case string:
- return []interface{}{v}
- case []string:
- res := make([]interface{}, len(v))
- for i, item := range v {
- res[i] = item
- }
- return res
- case []clause.Column:
- res := make([]interface{}, len(v))
- for i, item := range v {
- res[i] = item
- }
- return res
- default:
- return nil
- }
-}
-
-// ======================== New Table ========================
-
-// Table return a new table produced by subquery,
-// the return value has to be used as root node
-//
-// Table(u.Select(u.ID, u.Name).Where(u.Age.Gt(18))).Select()
-//
-// the above usage is equivalent to SQL statement:
-//
-// SELECT * FROM (SELECT `id`, `name` FROM `users_info` WHERE `age` > ?)"
-func Table(subQueries ...SubQuery) Dao {
- if len(subQueries) == 0 {
- return &DO{}
- }
-
- tablePlaceholder := make([]string, len(subQueries))
- tableExprs := make([]interface{}, len(subQueries))
- for i, query := range subQueries {
- tablePlaceholder[i] = "(?)"
-
- do := query.underlyingDO()
- // ignore alias, or will misuse with sub query alias
- tableExprs[i] = do.db.Table(do.TableName())
- if do.alias != "" {
- tablePlaceholder[i] += " AS " + do.Quote(do.alias)
- }
- }
-
- return &DO{
- db: subQueries[0].underlyingDO().db.Session(&gorm.Session{NewDB: true}).
- Table(strings.Join(tablePlaceholder, ", "), tableExprs...),
- }
-}
-
-// ======================== sub query method ========================
-
-// Columns columns array
-type Columns []field.Expr
-
-// Set assign value by subquery
-func (cs Columns) Set(query SubQuery) field.AssignExpr {
- return field.AssignSubQuery(cs, query.underlyingDB())
-}
-
-// In accept query or value
-func (cs Columns) In(queryOrValue Condition) field.Expr {
- if len(cs) == 0 {
- return field.EmptyExpr()
- }
-
- switch query := queryOrValue.(type) {
- case field.Value:
- return field.ContainsValue(cs, query)
- case SubQuery:
- return field.ContainsSubQuery(cs, query.underlyingDB())
- default:
- return field.EmptyExpr()
- }
-}
-
-// NotIn ...
-func (cs Columns) NotIn(queryOrValue Condition) field.Expr {
- return field.Not(cs.In(queryOrValue))
-}
-
-// Eq ...
-func (cs Columns) Eq(query SubQuery) field.Expr {
- if len(cs) == 0 {
- return field.EmptyExpr()
- }
- return field.CompareSubQuery(field.EqOp, cs[0], query.underlyingDB())
-}
-
-// Neq ...
-func (cs Columns) Neq(query SubQuery) field.Expr {
- if len(cs) == 0 {
- return field.EmptyExpr()
- }
- return field.CompareSubQuery(field.NeqOp, cs[0], query.underlyingDB())
-}
-
-// Gt ...
-func (cs Columns) Gt(query SubQuery) field.Expr {
- if len(cs) == 0 {
- return field.EmptyExpr()
- }
- return field.CompareSubQuery(field.GtOp, cs[0], query.underlyingDB())
-}
-
-// Gte ...
-func (cs Columns) Gte(query SubQuery) field.Expr {
- if len(cs) == 0 {
- return field.EmptyExpr()
- }
- return field.CompareSubQuery(field.GteOp, cs[0], query.underlyingDB())
-}
-
-// Lt ...
-func (cs Columns) Lt(query SubQuery) field.Expr {
- if len(cs) == 0 {
- return field.EmptyExpr()
- }
- return field.CompareSubQuery(field.LtOp, cs[0], query.underlyingDB())
-}
-
-// Lte ...
-func (cs Columns) Lte(query SubQuery) field.Expr {
- if len(cs) == 0 {
- return field.EmptyExpr()
- }
- return field.CompareSubQuery(field.LteOp, cs[0], query.underlyingDB())
-}
diff --git a/toolkit/gormgen/do_options.go b/toolkit/gormgen/do_options.go
deleted file mode 100644
index 4ca7f955..00000000
--- a/toolkit/gormgen/do_options.go
+++ /dev/null
@@ -1,23 +0,0 @@
-package gormgen
-
-// DOOption gorm option interface
-type DOOption interface {
- Apply(*DOConfig) error
- AfterInitialize(*DO) error
-}
-
-type DOConfig struct {
-}
-
-// Apply update config to new config
-func (c *DOConfig) Apply(config *DOConfig) error {
- if config != c {
- *config = *c
- }
- return nil
-}
-
-// AfterInitialize initialize plugins after db connected
-func (c *DOConfig) AfterInitialize(db *DO) error {
- return nil
-}
diff --git a/toolkit/gormgen/do_test.go b/toolkit/gormgen/do_test.go
deleted file mode 100644
index f66b9135..00000000
--- a/toolkit/gormgen/do_test.go
+++ /dev/null
@@ -1,395 +0,0 @@
-package gormgen
-
-import (
- "reflect"
- "strings"
- "testing"
-
- "github.com/wubin1989/datatypes"
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/clause"
- "github.com/wubin1989/hints"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
-)
-
-var (
- createClauses = []string{"INSERT", "VALUES", "ON CONFLICT"}
- queryClauses = []string{"SELECT", "FROM", "WHERE", "GROUP BY", "ORDER BY", "LIMIT", "FOR"}
- updateClauses = []string{"UPDATE", "SET", "WHERE"}
- deleteClauses = []string{"DELETE", "FROM", "WHERE"}
-)
-
-type stmtOpt func(*gorm.Statement) *gorm.Statement
-
-var (
- // withFROM add FROM clause
- withFROM stmtOpt = func(stmt *gorm.Statement) *gorm.Statement {
- stmt.AddClause(clause.From{})
- return stmt
- }
-
- // // withSELECT add SELECT clause
- // withSELECT stmtOpt = func(stmt *gorm.Statement) *gorm.Statement {
- // if _, ok := stmt.Clauses["SELECT"]; !ok {
- // stmt.AddClause(clause.Select{Distinct: stmt.Distinct})
- // }
- // return stmt
- // }
-)
-
-func checkBuildExpr(t *testing.T, e SubQuery, opts []stmtOpt, result string, vars []interface{}) {
- stmt := build(e.underlyingDB().Statement, opts...)
-
- sql := strings.TrimSpace(stmt.SQL.String())
- if sql != result {
- t.Errorf("SQL expects %v got %v", result, sql)
- }
-
- if !reflect.DeepEqual(stmt.Vars, vars) {
- t.Errorf("Vars expects %+v got %v", vars, stmt.Vars)
- }
-}
-
-func build(stmt *gorm.Statement, opts ...stmtOpt) *gorm.Statement {
- for _, opt := range opts {
- stmt = opt(stmt)
- }
-
- if _, ok := stmt.Clauses["SELECT"]; !ok && len(stmt.Selects) > 0 {
- stmt.AddClause(clause.Select{Distinct: stmt.Distinct, Expression: clause.Expr{SQL: strings.Join(stmt.Selects, ",")}})
- }
-
- findClauses := func() []string {
- for _, cs := range [][]string{createClauses, queryClauses, updateClauses, deleteClauses} {
- if _, ok := stmt.Clauses[cs[0]]; ok {
- return cs
- }
- }
- return queryClauses
- }
-
- stmt.Build(findClauses()...)
- return stmt
-}
-
-func TestDO_methods(t *testing.T) {
- testcases := []struct {
- Expr SubQuery
- Opts []stmtOpt
- ExpectedVars []interface{}
- Result string
- }{
- {
- Expr: u.Select(),
- Result: "SELECT *",
- },
- {
- Expr: u.Select(u.ID, u.Name),
- Result: "SELECT `id`,`name`",
- },
- {
- Expr: u.Distinct(u.Name),
- Result: "SELECT DISTINCT `name`",
- },
- {
- Expr: teacher.Distinct(teacher.ID, teacher.Name),
- Result: "SELECT DISTINCT `teacher`.`id`,`teacher`.`name`",
- },
- {
- Expr: teacher.Select(teacher.ID, teacher.Name).Distinct(),
- Result: "SELECT DISTINCT `teacher`.`id`,`teacher`.`name`",
- },
- {
- Expr: teacher.Distinct().Select(teacher.ID, teacher.Name),
- Result: "SELECT DISTINCT `teacher`.`id`,`teacher`.`name`",
- },
- {
- Expr: teacher.Select(teacher.Name.As("n")).Distinct(),
- Result: "SELECT DISTINCT `teacher`.`name` AS `n`",
- },
- {
- Expr: teacher.Select(field.ALL),
- Result: "SELECT *",
- },
- {
- Expr: teacher.Select(field.ALL.Count()),
- Result: "SELECT COUNT(*)",
- },
- {
- Expr: teacher.Select(field.ALL.Distinct().Count()),
- Result: "SELECT COUNT(DISTINCT *)",
- },
- {
- Expr: teacher.Select(teacher.ALL),
- Result: "SELECT `teacher`.*",
- },
- {
- Expr: teacher.Select(teacher.ALL.Count()),
- Result: "SELECT COUNT(`teacher`.*)",
- },
- {
- Expr: teacher.Select(teacher.ALL.Distinct().Count()),
- Result: "SELECT COUNT(DISTINCT `teacher`.*)",
- },
- {
- Expr: teacher.Select(teacher.ID.As("i"), teacher.Name.As("n")).Distinct(),
- Result: "SELECT DISTINCT `teacher`.`id` AS `i`,`teacher`.`name` AS `n`",
- },
- {
- Expr: u.Where(u.ID.Eq(10)),
- ExpectedVars: []interface{}{uint(10)},
- Result: "WHERE `id` = ?",
- },
- {
- Expr: u.Where(u.Name.Eq("tom"), u.Age.Gt(18)),
- ExpectedVars: []interface{}{"tom", 18},
- Result: "WHERE `name` = ? AND `age` > ?",
- },
- {
- Expr: u.Order(u.ID),
- Result: "ORDER BY `id`",
- },
- {
- Expr: u.Order(u.ID.Desc()),
- Result: "ORDER BY `id` DESC",
- },
- {
- Expr: u.Order(u.ID.Desc(), u.Age),
- Result: "ORDER BY `id` DESC,`age`",
- },
- {
- Expr: u.Order(u.ID.Desc()).Order(u.Age),
- Result: "ORDER BY `id` DESC,`age`",
- },
- {
- Expr: u.Clauses(hints.New("hint")).Select(),
- Result: "SELECT /*+ hint */ *",
- },
- {
- Expr: u.Clauses(hints.Comment("select", "hint")).Select(),
- Result: "SELECT /* hint */ *",
- },
- {
- Expr: u.Clauses(hints.CommentBefore("select", "hint")).Select(),
- Result: "/* hint */ SELECT *",
- },
- {
- Expr: u.Clauses(hints.CommentAfter("select", "hint")).Select(),
- Result: "SELECT * /* hint */",
- },
- {
- Expr: u.Clauses(hints.CommentAfter("where", "hint")).Select().Where(u.ID.Gt(0)),
- ExpectedVars: []interface{}{uint(0)},
- Result: "SELECT * WHERE `id` > ? /* hint */",
- },
- {
- Expr: u.Clauses(hints.UseIndex("user_name")).Select(),
- Opts: []stmtOpt{withFROM},
- Result: "SELECT * FROM `users_info` USE INDEX (`user_name`)",
- },
- {
- Expr: u.Clauses(hints.ForceIndex("user_name", "user_id").ForJoin()).Select(),
- Opts: []stmtOpt{withFROM},
- Result: "SELECT * FROM `users_info` FORCE INDEX FOR JOIN (`user_name`,`user_id`)",
- },
- {
- Expr: u.Clauses(
- hints.ForceIndex("user_name", "user_id").ForJoin(),
- hints.IgnoreIndex("user_name").ForGroupBy(),
- ).Select(),
- Opts: []stmtOpt{withFROM},
- Result: "SELECT * FROM `users_info` FORCE INDEX FOR JOIN (`user_name`,`user_id`) IGNORE INDEX FOR GROUP BY (`user_name`)",
- },
- // ======================== where conditions ========================
- {
- Expr: u.Where(u.Where(u.ID.Neq(0)), u.Where(u.Age.Gt(18))),
- ExpectedVars: []interface{}{uint(0), 18},
- Result: "WHERE `id` <> ? AND `age` > ?",
- },
- {
- Expr: u.Where(u.Age.Lte(18)).Or(u.Where(u.Name.Eq("tom"))),
- ExpectedVars: []interface{}{18, "tom"},
- Result: "WHERE `age` <= ? OR `name` = ?",
- },
- {
- Expr: u.Where(u.Age.Lte(18)).Or(u.Name.Eq("tom"), u.Famous.Is(true)),
- ExpectedVars: []interface{}{18, "tom", true},
- Result: "WHERE `age` <= ? OR (`name` = ? AND `famous` = ?)",
- },
- {
- Expr: u.Where(u.Columns(u.ID, u.Age).In(field.Values([][]int{{1, 18}, {2, 19}}))),
- ExpectedVars: []interface{}{1, 18, 2, 19},
- Result: "WHERE (`id`, `age`) IN ((?,?),(?,?))",
- },
- {
- Expr: u.Where(u.Columns(u.ID, u.Age).NotIn(field.Values([][]int{{1, 18}, {2, 19}}))),
- ExpectedVars: []interface{}{1, 18, 2, 19},
- Result: "WHERE NOT (`id`, `age`) IN ((?,?),(?,?))",
- },
- {
- Expr: u.Where(u.Columns(u.ID, u.Name).In(field.Values([][]interface{}{{1, "modi"}, {2, "tom"}}))),
- ExpectedVars: []interface{}{1, "modi", 2, "tom"},
- Result: "WHERE (`id`, `name`) IN ((?,?),(?,?))",
- },
- {
- Expr: u.Where(u.Where(u.Name.Eq("tom"), u.Famous.Is(true))).Or(u.Age.Lte(18)),
- ExpectedVars: []interface{}{"tom", true, 18},
- Result: "WHERE (`name` = ? AND `famous` = ?) OR `age` <= ?",
- },
- {
- Expr: u.Where(u.Name.Eq("tom")).Where(u.Where(u.Famous.Is(true)).Or(u.Age.Lte(18))),
- ExpectedVars: []interface{}{"tom", true, 18},
- Result: "WHERE `name` = ? AND (`famous` = ? OR `age` <= ?)",
- },
- {
- Expr: u.Where(u.Name.Eq("tom"), field.Or(u.Famous.Is(true), u.Age.Lte(18))),
- ExpectedVars: []interface{}{"tom", true, 18},
- Result: "WHERE `name` = ? AND (`famous` = ? OR `age` <= ?)",
- },
- {
- Expr: u.Where(Cond(datatypes.JSONQuery("attributes").HasKey("role", "name"))...),
- ExpectedVars: []interface{}{"$.role.name"},
- Result: "WHERE JSON_EXTRACT(`attributes`,?) IS NOT NULL",
- },
- {
- Expr: u.Where(
- u.Where(u.ID.Neq(0)).Where(u.Score.Gt(89.9)),
- u.Where(u.Age.Gt(18)).Where(u.Address.Eq("New York")),
- ),
- ExpectedVars: []interface{}{uint(0), 89.9, 18, "New York"},
- Result: "WHERE (`id` <> ? AND `score` > ?) AND (`age` > ? AND `address` = ?)",
- },
- {
- Expr: u.Where(
- u.Where(u.Age.Gt(18)).Where(u.Where(u.Famous.Is(true)).Or(u.Score.Gte(100.0))),
- ).Or(
- u.Where(u.Age.Lte(18)).Where(u.Name.Eq("tom")),
- ),
- ExpectedVars: []interface{}{18, true, 100.0, 18, "tom"},
- Result: "WHERE (`age` > ? AND (`famous` = ? OR `score` >= ?)) OR (`age` <= ? AND `name` = ?)",
- },
- {
- Expr: u.Select(u.ID, u.Name).Where(u.Age.Gt(18), u.Score.Gte(100)),
- ExpectedVars: []interface{}{18, 100.0},
- Result: "SELECT `id`,`name` WHERE `age` > ? AND `score` >= ?",
- },
- {
- Expr: u.Select().Where(Cond(datatypes.JSONQuery("attributes").HasKey("role"))...),
- Opts: []stmtOpt{withFROM},
- ExpectedVars: []interface{}{"$.role"},
- Result: "SELECT * FROM `users_info` WHERE JSON_EXTRACT(`attributes`,?) IS NOT NULL",
- },
- // ======================== subquery ========================
- {
- Expr: u.Select().Where(u.Columns(u.ID).Eq(u.Select(u.ID.Max()))),
- Result: "SELECT * WHERE `id` = (SELECT MAX(`id`) FROM `users_info`)",
- },
- {
- Expr: u.Select().Where(
- u.Columns(u.ID).Eq(
- u.Select(u.ID).Where(
- u.Columns(u.Name).Eq(
- student.Select(student.Name).Where(student.ID.Eq(1)),
- ),
- ),
- )),
- ExpectedVars: []interface{}{int64(1)},
- Result: "SELECT * WHERE `id` = (SELECT `id` FROM `users_info` WHERE `name` = (SELECT `student`.`name` FROM `student` WHERE `student`.`id` = ?))",
- },
- {
- Expr: u.Select().Where(u.Columns(u.ID).Neq(u.Select(u.ID.Max()))),
- Result: "SELECT * WHERE `id` <> (SELECT MAX(`id`) FROM `users_info`)",
- },
- {
- Expr: u.Select(u.ID).Where(u.Columns(u.Score.Mul(2)).Lte(u.Select(u.Score.Avg()))),
- ExpectedVars: []interface{}{2.0},
- Result: "SELECT `id` WHERE `score`*? <= (SELECT AVG(`score`) FROM `users_info`)",
- },
- {
- Expr: u.Select(u.ID).Where(u.Columns(u.Score).Gt(u.Select(u.Score.Avg()))),
- Result: "SELECT `id` WHERE `score` > (SELECT AVG(`score`) FROM `users_info`)",
- },
- {
- Expr: u.Select(u.ID, u.Name).Where(u.Columns(u.Score).Lte(u.Select(u.Score.Avg()).Where(u.Age.Gte(18)))),
- ExpectedVars: []interface{}{18},
- Result: "SELECT `id`,`name` WHERE `score` <= (SELECT AVG(`score`) FROM `users_info` WHERE `age` >= ?)",
- },
- {
- Expr: u.Select(u.ID).Where(u.Columns(u.Score).In(u.Select(u.Score).Where(u.Age.Gte(18)))),
- ExpectedVars: []interface{}{18},
- Result: "SELECT `id` WHERE `score` IN (SELECT `score` FROM `users_info` WHERE `age` >= ?)",
- },
- {
- Expr: u.Select(u.ID).Where(u.Columns(u.ID, u.Age).In(u.Select(u.ID, u.Age).Where(u.Score.Eq(100)))),
- ExpectedVars: []interface{}{100.0},
- Result: "SELECT `id` WHERE (`id`,`age`) IN (SELECT `id`,`age` FROM `users_info` WHERE `score` = ?)",
- },
- {
- Expr: u.Select(u.Age.Avg().As("avgage")).Group(u.Name).Having(u.Columns(u.Age.Avg()).Gt(u.Select(u.Age.Avg()).Where(u.Name.Like("name%")))),
- Opts: []stmtOpt{withFROM},
- ExpectedVars: []interface{}{"name%"},
- Result: "SELECT AVG(`age`) AS `avgage` FROM `users_info` GROUP BY `name` HAVING AVG(`age`) > (SELECT AVG(`age`) FROM `users_info` WHERE `name` LIKE ?)",
- },
- {
- Expr: student.Select().LeftJoin(teacher, teacher.ID.EqCol(student.Instructor)).Group(student.ID),
- Result: "SELECT * FROM `student` LEFT JOIN `teacher` ON `teacher`.`id` = `student`.`instructor` GROUP BY `student`.`id`",
- },
- // ======================== from subquery ========================
- {
- Expr: Table(u.Select(u.ID, u.Name).Where(u.Age.Gt(18))).Select(),
- Opts: []stmtOpt{withFROM},
- ExpectedVars: []interface{}{18},
- Result: "SELECT * FROM (SELECT `id`,`name` FROM `users_info` WHERE `age` > ?)",
- },
- {
- Expr: Table(u.Select(u.ID).Where(u.Age.Gt(18)), u.Select(u.ID).Where(u.Score.Gte(100))).Select(),
- Opts: []stmtOpt{withFROM},
- ExpectedVars: []interface{}{18, 100.0},
- Result: "SELECT * FROM (SELECT `id` FROM `users_info` WHERE `age` > ?), (SELECT `id` FROM `users_info` WHERE `score` >= ?)",
- },
- {
- Expr: Table(u.Select().Where(u.Age.Gt(18)), u.Where(u.Score.Gte(100))).Select(),
- Opts: []stmtOpt{withFROM},
- ExpectedVars: []interface{}{18, 100.0},
- Result: "SELECT * FROM (SELECT * FROM `users_info` WHERE `age` > ?), (SELECT * FROM `users_info` WHERE `score` >= ?)",
- },
- {
- Expr: Table(u.Select().Where(u.Age.Gt(18)).As("a"), u.Where(u.Score.Gte(100)).As("b")).Select(),
- Opts: []stmtOpt{withFROM},
- ExpectedVars: []interface{}{18, 100.0},
- Result: "SELECT * FROM (SELECT * FROM `users_info` WHERE `age` > ?) AS `a`, (SELECT * FROM `users_info` WHERE `score` >= ?) AS `b`",
- },
-
- // ======================== join subquery ========================
- {
- Expr: student.Join(teacher, student.Instructor.EqCol(teacher.ID)).Select(),
- Result: "SELECT * FROM `student` INNER JOIN `teacher` ON `student`.`instructor` = `teacher`.`id`",
- },
- {
- Expr: student.LeftJoin(teacher, student.Instructor.EqCol(teacher.ID)).Where(teacher.ID.Gt(0)).Select(student.Name, teacher.Name),
- Result: "SELECT `student`.`name`,`teacher`.`name` FROM `student` LEFT JOIN `teacher` ON `student`.`instructor` = `teacher`.`id` WHERE `teacher`.`id` > ?",
- ExpectedVars: []interface{}{int64(0)},
- },
- {
- Expr: student.RightJoin(teacher, student.Instructor.EqCol(teacher.ID), student.ID.Eq(666)).Select(),
- Result: "SELECT * FROM `student` RIGHT JOIN `teacher` ON `student`.`instructor` = `teacher`.`id` AND `student`.`id` = ?",
- ExpectedVars: []interface{}{int64(666)},
- },
- {
- Expr: student.Join(teacher, student.Instructor.EqCol(teacher.ID)).LeftJoin(teacher, student.ID.Gt(100)).Select(student.ID, student.Name, teacher.Name.As("teacher_name")),
- Result: "SELECT `student`.`id`,`student`.`name`,`teacher`.`name` AS `teacher_name` FROM `student` INNER JOIN `teacher` ON `student`.`instructor` = `teacher`.`id` LEFT JOIN `teacher` ON `student`.`id` > ?",
- ExpectedVars: []interface{}{int64(100)},
- },
- }
-
- // _ = u.Update(u.Age, u.Age.Add(1))
- // _ = u.Update(u.Age, gorm.Expr("age+1"))
- // _ = u.UpdateSimple(u.Age.Add(1))
-
- // _ = u.Find(u.ID.In(1, 2, 3))
-
- for _, testcase := range testcases {
- checkBuildExpr(t, testcase.Expr, testcase.Opts, testcase.Result, testcase.ExpectedVars)
- }
-}
diff --git a/toolkit/gormgen/errors.go b/toolkit/gormgen/errors.go
deleted file mode 100644
index a3164ec6..00000000
--- a/toolkit/gormgen/errors.go
+++ /dev/null
@@ -1,8 +0,0 @@
-package gormgen
-
-import "errors"
-
-var (
- // ErrEmptyCondition empty condition
- ErrEmptyCondition = errors.New("empty condition")
-)
diff --git a/toolkit/gormgen/field/association.go b/toolkit/gormgen/field/association.go
deleted file mode 100644
index 04850d40..00000000
--- a/toolkit/gormgen/field/association.go
+++ /dev/null
@@ -1,255 +0,0 @@
-package field
-
-import (
- "fmt"
- "strings"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/clause"
- "github.com/wubin1989/gorm/schema"
-)
-
-// RelationshipType table relationship
-type RelationshipType schema.RelationshipType
-
-const (
- // HasOne a has one association sets up a one-to-one connection with another model. Reference https://github.com/wubin1989/docs/has_one.html
- HasOne RelationshipType = RelationshipType(schema.HasOne) // HasOneRel has one relationship
- // HasMany a has many association sets up a one-to-many connection with another model. Reference https://github.com/wubin1989/docs/has_many.html
- HasMany RelationshipType = RelationshipType(schema.HasMany) // HasManyRel has many relationships
- // BelongsTo A belongs to association sets up a one-to-one connection with another model. Reference https://github.com/wubin1989/docs/belongs_to.html
- BelongsTo RelationshipType = RelationshipType(schema.BelongsTo) // BelongsToRel belongs to relationship
- // Many2Many Many to Many add a join table between two models. Reference https://github.com/wubin1989/docs/many2many.html
- Many2Many RelationshipType = RelationshipType(schema.Many2Many) // Many2ManyRel many to many relationship
-)
-
-type relationScope func(*gorm.DB) *gorm.DB
-
-var (
- // RelationFieldUnscoped relation fild unscoped
- RelationFieldUnscoped relationScope = func(tx *gorm.DB) *gorm.DB {
- return tx.Unscoped()
- }
-)
-
-var ns = schema.NamingStrategy{}
-
-// RelationField interface for relation field
-type RelationField interface {
- Name() string
- Path() string
- // Field return expr for Select
- // Field() return "" field name in struct
- // Field("RelateField") return ".RelateField" for Select
- // Field("RelateField", "RelateRelateField") return ".RelateField.RelateRelateField" for Select
- // ex:
- // Select(u.CreditCards.Field()) equals to GORM: Select("CreditCards")
- // Select(u.CreditCards.Field("Bank")) equals to GORM: Select("CreditCards.Bank")
- // Select(u.CreditCards.Field("Bank","Owner")) equals to GORM: Select("CreditCards.Bank.Owner")
- Field(fields ...string) Expr
-
- On(conds ...Expr) RelationField
- Select(conds ...Expr) RelationField
- Order(columns ...Expr) RelationField
- Clauses(hints ...clause.Expression) RelationField
- Scopes(funcs ...relationScope) RelationField
- Offset(offset int) RelationField
- Limit(limit int) RelationField
-
- GetConds() []Expr
- GetSelects() []Expr
- GetOrderCol() []Expr
- GetClauses() []clause.Expression
- GetScopes() []relationScope
- GetPage() (offset, limit int)
-}
-
-// Relation relation meta info
-type Relation struct {
- relationship RelationshipType
-
- fieldName string
- fieldType string
- fieldPath string
- fieldModel interface{} // store relaiton model
-
- childRelations []Relation
-
- conds []Expr
- selects []Expr
- order []Expr
- clauses []clause.Expression
- scopes []relationScope
- limit, offset int
-}
-
-// Name relation field' name
-func (r Relation) Name() string { return r.fieldName }
-
-// Path relation field's path
-func (r Relation) Path() string { return r.fieldPath }
-
-// Type relation field's type
-func (r Relation) Type() string { return r.fieldType }
-
-// Model relation field's model
-func (r Relation) Model() interface{} { return r.fieldModel }
-
-// Relationship relationship between field and table struct
-func (r Relation) Relationship() RelationshipType { return r.relationship }
-
-// RelationshipName relationship's name
-func (r Relation) RelationshipName() string { return ns.SchemaName(string(r.relationship)) }
-
-// ChildRelations return child relations
-func (r Relation) ChildRelations() []Relation { return r.childRelations }
-
-// Field build field
-func (r Relation) Field(fields ...string) Expr {
- if len(fields) > 0 {
- return NewString("", r.fieldName+"."+strings.Join(fields, ".")).appendBuildOpts(WithoutQuote)
- }
- return NewString("", r.fieldName).appendBuildOpts(WithoutQuote)
-}
-
-// AppendChildRelation append child relationship
-func (r *Relation) AppendChildRelation(relations ...Relation) {
- r.childRelations = append(r.childRelations, wrapPath(r.fieldPath, relations)...)
-}
-
-// On relation condition
-func (r Relation) On(conds ...Expr) RelationField {
- r.conds = append(r.conds, conds...)
- return &r
-}
-
-// Select relation select columns
-func (r Relation) Select(columns ...Expr) RelationField {
- r.selects = append(r.selects, columns...)
- return &r
-}
-
-// Order relation order columns
-func (r Relation) Order(columns ...Expr) RelationField {
- r.order = append(r.order, columns...)
- return &r
-}
-
-// Clauses set relation clauses
-func (r Relation) Clauses(hints ...clause.Expression) RelationField {
- r.clauses = append(r.clauses, hints...)
- return &r
-}
-
-// Scopes set scopes func
-func (r Relation) Scopes(funcs ...relationScope) RelationField {
- r.scopes = append(r.scopes, funcs...)
- return &r
-}
-
-// Offset set relation offset
-func (r Relation) Offset(offset int) RelationField {
- r.offset = offset
- return &r
-}
-
-// Limit set relation limit
-func (r Relation) Limit(limit int) RelationField {
- r.limit = limit
- return &r
-}
-
-// GetConds get query conditions
-func (r *Relation) GetConds() []Expr { return r.conds }
-
-// GetSelects get select columns
-func (r *Relation) GetSelects() []Expr { return r.selects }
-
-// GetOrderCol get order columns
-func (r *Relation) GetOrderCol() []Expr { return r.order }
-
-// GetClauses get clauses
-func (r *Relation) GetClauses() []clause.Expression { return r.clauses }
-
-// GetScopes get scope functions
-func (r *Relation) GetScopes() []relationScope { return r.scopes } // nolint
-
-// GetPage get offset and limit
-func (r *Relation) GetPage() (offset, limit int) { return r.offset, r.limit }
-
-// StructField return struct field code
-func (r *Relation) StructField() (fieldStr string) {
- for _, relation := range r.childRelations {
- fieldStr += relation.fieldName + " struct {\nfield.RelationField\n" + relation.StructField() + "}\n"
- }
- return fieldStr
-}
-
-// StructFieldInit return field initialize code
-func (r *Relation) StructFieldInit() string {
- initStr := fmt.Sprintf("RelationField: field.NewRelation(%q, %q),\n", r.fieldPath, r.fieldType)
- for _, relation := range r.childRelations {
- initStr += relation.fieldName + ": struct {\nfield.RelationField\n" + strings.TrimSpace(relation.StructField()) + "}"
- initStr += "{\n" + relation.StructFieldInit() + "},\n"
- }
- return initStr
-}
-
-func wrapPath(root string, rs []Relation) []Relation {
- result := make([]Relation, len(rs))
- for i, r := range rs {
- r.fieldPath = root + "." + r.fieldPath
- r.childRelations = wrapPath(root, r.childRelations)
- result[i] = r
- }
- return result
-}
-
-var defaultRelationshipPrefix = map[RelationshipType]string{
- // HasOne: "",
- // BelongsTo: "",
- HasMany: "[]",
- Many2Many: "[]",
-}
-
-// RelateConfig config for relationship
-type RelateConfig struct {
- RelatePointer bool
- RelateSlice bool
- RelateSlicePointer bool
-
- JSONTag string
- GORMTag GormTag
- Tag Tag
- OverwriteTag Tag
-}
-
-// RelateFieldPrefix return generated relation field's type
-func (c *RelateConfig) RelateFieldPrefix(relationshipType RelationshipType) string {
- switch {
- case c.RelatePointer:
- return "*"
- case c.RelateSlice:
- return "[]"
- case c.RelateSlicePointer:
- return "[]*"
- default:
- return defaultRelationshipPrefix[relationshipType]
- }
-}
-func (c *RelateConfig) GetTag(fieldName string) Tag {
- if c == nil {
- return Tag{}
- }
- if c.OverwriteTag != nil {
- return c.OverwriteTag
- }
- if c.Tag == nil {
- c.Tag = Tag{}
- }
- if c.JSONTag == "" {
- c.JSONTag = ns.ColumnName("", fieldName)
- }
- c.Tag.Set(TagKeyJson, c.JSONTag)
- return c.Tag
-}
diff --git a/toolkit/gormgen/field/asterisk.go b/toolkit/gormgen/field/asterisk.go
deleted file mode 100644
index f176e11a..00000000
--- a/toolkit/gormgen/field/asterisk.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package field
-
-import (
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/clause"
-)
-
-// Asterisk a type of xxx.*
-type Asterisk struct{ asteriskExpr }
-
-// Count count
-func (a Asterisk) Count() Asterisk {
- var expr *clause.Expr
- switch {
- case a.e != nil:
- expr = &clause.Expr{
- SQL: "COUNT(?)",
- Vars: []interface{}{a.e},
- }
- case a.col.Table == "":
- expr = &clause.Expr{SQL: "COUNT(*)"}
- default:
- expr = &clause.Expr{
- SQL: "COUNT(?.*)",
- Vars: []interface{}{clause.Table{Name: a.col.Table}},
- }
- }
- return Asterisk{asteriskExpr{expr: a.setE(expr)}}
-}
-
-// Distinct distinct
-func (a Asterisk) Distinct() Asterisk {
- var expr *clause.Expr
- if a.col.Table == "" {
- expr = &clause.Expr{SQL: "DISTINCT *"}
- } else {
- expr = &clause.Expr{
- SQL: "DISTINCT ?.*",
- Vars: []interface{}{clause.Table{Name: a.col.Table}},
- }
- }
- return Asterisk{asteriskExpr{expr: a.setE(expr)}}
-}
-
-type asteriskExpr struct{ expr }
-
-func (e asteriskExpr) BuildWithArgs(*gorm.Statement) (query sql, args []interface{}) {
- // if e.expr has no expression it must be directly calling for "*" or "xxx.*"
- if e.e != nil {
- return "?", []interface{}{e.e}
- }
- if e.col.Table == "" {
- return "*", nil
- }
- return "?.*", []interface{}{clause.Table{Name: e.col.Table}}
-}
diff --git a/toolkit/gormgen/field/bool.go b/toolkit/gormgen/field/bool.go
deleted file mode 100644
index 8fd1206b..00000000
--- a/toolkit/gormgen/field/bool.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package field
-
-// Bool boolean type field
-type Bool Field
-
-// Not ...
-func (field Bool) Not() Bool {
- return Bool{field.not()}
-}
-
-// Is ...
-func (field Bool) Is(value bool) Expr {
- return field.is(value)
-}
-
-// And boolean and
-func (field Bool) And(value bool) Expr {
- return Bool{field.and(value)}
-}
-
-// Or boolean or
-func (field Bool) Or(value bool) Expr {
- return Bool{field.or(value)}
-}
-
-// Xor ...
-func (field Bool) Xor(value bool) Expr {
- return Bool{field.xor(value)}
-}
-
-// BitXor ...
-func (field Bool) BitXor(value bool) Expr {
- return Bool{field.bitXor(value)}
-}
-
-// BitAnd ...
-func (field Bool) BitAnd(value bool) Expr {
- return Bool{field.bitAnd(value)}
-}
-
-// BitOr ...
-func (field Bool) BitOr(value bool) Expr {
- return Bool{field.bitOr(value)}
-}
-
-// Value ...
-func (field Bool) Value(value bool) AssignExpr {
- return field.value(value)
-}
-
-// Zero ...
-func (field Bool) Zero() AssignExpr {
- return field.value(false)
-}
diff --git a/toolkit/gormgen/field/doc.go b/toolkit/gormgen/field/doc.go
deleted file mode 100644
index ecaff1fb..00000000
--- a/toolkit/gormgen/field/doc.go
+++ /dev/null
@@ -1,2 +0,0 @@
-// Package field implement all type field and method
-package field
diff --git a/toolkit/gormgen/field/example_test.go b/toolkit/gormgen/field/example_test.go
deleted file mode 100644
index 75733ffa..00000000
--- a/toolkit/gormgen/field/example_test.go
+++ /dev/null
@@ -1,21 +0,0 @@
-package field_test
-
-import (
- "fmt"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
-)
-
-func ExampleFunc() {
- expr := field.Func.UnixTimestamp()
-
- sql, vars := field.BuildToString(expr)
- fmt.Println(sql, vars)
-
- sql, vars = field.BuildToString(expr.Mul(100))
- fmt.Println(sql, vars)
-
- // Output:
- // UNIX_TIMESTAMP() []
- // (UNIX_TIMESTAMP())*? [100]
-}
diff --git a/toolkit/gormgen/field/export.go b/toolkit/gormgen/field/export.go
deleted file mode 100644
index 541cf6c2..00000000
--- a/toolkit/gormgen/field/export.go
+++ /dev/null
@@ -1,333 +0,0 @@
-package field
-
-import (
- "fmt"
- "strings"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/clause"
-)
-
-var (
- // Star a symbol of "*"
- Star = NewAsterisk("")
- // ALL same with Star
- ALL = Star
-)
-
-// Option field option
-type Option func(clause.Column) clause.Column
-
-var (
- banColumnRaw Option = func(col clause.Column) clause.Column {
- col.Raw = false
- return col
- }
-)
-
-// ======================== generic field =======================
-
-// NewField create new field
-func NewField(table, column string, opts ...Option) Field {
- return Field{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewSerializer create new field2
-func NewSerializer(table, column string, opts ...Option) Serializer {
- return Serializer{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewAsterisk create new * field
-func NewAsterisk(table string, opts ...Option) Asterisk {
- return Asterisk{asteriskExpr: asteriskExpr{expr{col: toColumn(table, "*", opts...)}}}
-}
-
-// ======================== integer =======================
-
-// NewInt create new Int
-func NewInt(table, column string, opts ...Option) Int {
- return Int{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewInt8 create new Int8
-func NewInt8(table, column string, opts ...Option) Int8 {
- return Int8{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewInt16 ...
-func NewInt16(table, column string, opts ...Option) Int16 {
- return Int16{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewInt32 ...
-func NewInt32(table, column string, opts ...Option) Int32 {
- return Int32{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewInt64 ...
-func NewInt64(table, column string, opts ...Option) Int64 {
- return Int64{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewUint ...
-func NewUint(table, column string, opts ...Option) Uint {
- return Uint{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewUint8 ...
-func NewUint8(table, column string, opts ...Option) Uint8 {
- return Uint8{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewUint16 ...
-func NewUint16(table, column string, opts ...Option) Uint16 {
- return Uint16{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewUint32 ...
-func NewUint32(table, column string, opts ...Option) Uint32 {
- return Uint32{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewUint64 ...
-func NewUint64(table, column string, opts ...Option) Uint64 {
- return Uint64{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// ======================== float =======================
-
-// NewFloat32 ...
-func NewFloat32(table, column string, opts ...Option) Float32 {
- return Float32{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewFloat64 ...
-func NewFloat64(table, column string, opts ...Option) Float64 {
- return Float64{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// ======================== string =======================
-
-// NewString ...
-func NewString(table, column string, opts ...Option) String {
- return String{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// NewBytes ...
-func NewBytes(table, column string, opts ...Option) Bytes {
- return Bytes{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// ======================== bool =======================
-
-// NewBool ...
-func NewBool(table, column string, opts ...Option) Bool {
- return Bool{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-// ======================== time =======================
-
-// NewTime ...
-func NewTime(table, column string, opts ...Option) Time {
- return Time{expr: expr{col: toColumn(table, column, opts...)}}
-}
-
-func toColumn(table, column string, opts ...Option) clause.Column {
- col := clause.Column{Table: table, Name: column}
- for _, opt := range opts {
- col = opt(col)
- }
- return banColumnRaw(col)
-}
-
-// ======================== boolean operate ========================
-
-// Or return or condition
-func Or(exprs ...Expr) Expr {
- return &expr{e: clause.Or(toExpression(exprs...)...)}
-}
-
-// And return and condition
-func And(exprs ...Expr) Expr {
- return &expr{e: clause.And(toExpression(exprs...)...)}
-}
-
-// Not return not condition
-func Not(exprs ...Expr) Expr {
- return &expr{e: clause.Not(toExpression(exprs...)...)}
-}
-
-func toExpression(conds ...Expr) []clause.Expression {
- exprs := make([]clause.Expression, len(conds))
- for i, cond := range conds {
- exprs[i] = cond.expression()
- }
- return exprs
-}
-
-// ======================== subquery method ========================
-
-// ContainsSubQuery return contains subquery
-// when len(columns) == 1, equal to columns[0] IN (subquery)
-// when len(columns) > 1, equal to (columns[0], columns[1], ...) IN (subquery)
-func ContainsSubQuery(columns []Expr, subQuery *gorm.DB) Expr {
- switch len(columns) {
- case 0:
- return expr{e: clause.Expr{}}
- case 1:
- return expr{e: clause.Expr{
- SQL: "? IN (?)",
- Vars: []interface{}{columns[0].RawExpr(), subQuery},
- }}
- default: // len(columns) > 0
- placeholders := make([]string, len(columns))
- cols := make([]interface{}, len(columns))
- for i, c := range columns {
- placeholders[i], cols[i] = "?", c.RawExpr()
- }
- return expr{e: clause.Expr{
- SQL: fmt.Sprintf("(%s) IN (?)", strings.Join(placeholders, ",")),
- Vars: append(cols, subQuery),
- }}
- }
-}
-
-// AssignSubQuery assign with subquery
-func AssignSubQuery(columns []Expr, subQuery *gorm.DB) AssignExpr {
- cols := make([]string, len(columns))
- for i, c := range columns {
- cols[i] = string(c.BuildColumn(subQuery.Statement))
- }
-
- name := cols[0]
- if len(cols) > 1 {
- name = "(" + strings.Join(cols, ",") + ")"
- }
-
- return expr{e: clause.Set{{
- Column: clause.Column{Name: name, Raw: true},
- Value: gorm.Expr("(?)", subQuery),
- }}}
-}
-
-// CompareOperator compare operator
-type CompareOperator string
-
-const (
- // EqOp =
- EqOp CompareOperator = " = "
- // NeqOp <>
- NeqOp CompareOperator = " <> "
- // GtOp >
- GtOp CompareOperator = " > "
- // GteOp >=
- GteOp CompareOperator = " >= "
- // LtOp <
- LtOp CompareOperator = " < "
- // LteOp <=
- LteOp CompareOperator = " <= "
- // ExistsOp EXISTS
- ExistsOp CompareOperator = "EXISTS "
-)
-
-// CompareSubQuery compare with sub query
-func CompareSubQuery(op CompareOperator, column Expr, subQuery *gorm.DB) Expr {
- if op == ExistsOp {
- return expr{e: clause.Expr{
- SQL: fmt.Sprint(op, "(?)"),
- Vars: []interface{}{subQuery},
- }}
- }
- return expr{e: clause.Expr{
- SQL: fmt.Sprint("?", op, "(?)"),
- Vars: []interface{}{column.RawExpr(), subQuery},
- }}
-}
-
-// Value ...
-type Value interface {
- expr() clause.Expr
-
- // implement Condition
- BeCond() interface{}
- CondError() error
-}
-
-type val clause.Expr
-
-func (v val) expr() clause.Expr { return clause.Expr(v) }
-func (v val) BeCond() interface{} { return v }
-func (val) CondError() error { return nil }
-
-// Values convert value to expression which implement Value
-func Values(value interface{}) Value {
- return val(clause.Expr{
- SQL: "?",
- Vars: []interface{}{value},
- WithoutParentheses: true,
- })
-}
-
-// ContainsValue return expression which compare with value
-func ContainsValue(columns []Expr, value Value) Expr {
- switch len(columns) {
- case 0:
- return expr{e: clause.Expr{}}
- case 1:
- return expr{e: clause.Expr{
- SQL: "? IN (?)",
- Vars: []interface{}{columns[0].RawExpr(), value.expr()},
- }}
- default: // len(columns) > 0
- vars := make([]string, len(columns))
- queryCols := make([]interface{}, len(columns))
- for i, c := range columns {
- vars[i], queryCols[i] = "?", c.RawExpr()
- }
- return expr{e: clause.Expr{
- SQL: fmt.Sprintf("(%s) IN (?)", strings.Join(vars, ", ")),
- Vars: append(queryCols, value.expr()),
- }}
- }
-}
-
-// EmptyExpr return a empty expression
-func EmptyExpr() Expr { return expr{e: clause.Expr{}} }
-
-// AssociationFields all association
-var AssociationFields Expr = NewString("", clause.Associations).appendBuildOpts(WithoutQuote)
-
-// Associations ...
-var Associations RelationField = NewRelation(clause.Associations, "")
-
-// NewRelation return a new Relation for association
-func NewRelation(fieldName string, fieldType string, relations ...Relation) *Relation {
- return &Relation{
- fieldName: fieldName,
- fieldPath: fieldName,
- fieldType: fieldType,
- childRelations: wrapPath(fieldName, relations),
- }
-}
-
-// NewRelationWithType return a Relation with specified field type
-func NewRelationWithType(relationship RelationshipType, fieldName string, fieldType string, relations ...Relation) *Relation {
- return &Relation{
- relationship: relationship,
- fieldName: fieldName,
- fieldType: fieldType,
- fieldPath: fieldName,
- childRelations: wrapPath(fieldName, relations),
- }
-}
-
-// NewRelationWithModel return a Relation with specified model struct
-func NewRelationWithModel(relationship RelationshipType, fieldName string, fieldType string, fieldModel interface{}, relations ...Relation) *Relation {
- return &Relation{
- relationship: relationship,
- fieldName: fieldName,
- fieldType: fieldType,
- fieldPath: fieldName,
- fieldModel: fieldModel,
- }
-}
diff --git a/toolkit/gormgen/field/export_test.go b/toolkit/gormgen/field/export_test.go
deleted file mode 100644
index 06254a8d..00000000
--- a/toolkit/gormgen/field/export_test.go
+++ /dev/null
@@ -1,582 +0,0 @@
-package field_test
-
-import (
- "database/sql/driver"
- "fmt"
- "strings"
- "testing"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
-)
-
-var _ field.ScanValuer = new(password)
-
-type password string
-
-func (p *password) Scan(src interface{}) error {
- *p = password(fmt.Sprintf("this is password {%q}", src))
- return nil
-}
-func (p password) Value() (driver.Value, error) {
- return strings.TrimPrefix(strings.TrimSuffix(string(p), "}"), "this is password {"), nil
-}
-
-func TestExpr_Build(t *testing.T) {
- timeData, _ := time.Parse("2006-01-02 15:04:05", "2021-06-29 15:11:49")
- const p = password("i am password")
-
- testcases := []struct {
- Expr field.Expr
- ExpectedVars []interface{}
- Result string
- }{
- // ======================== generic ========================
- {
- Expr: field.NewField("user", "password").Eq(p),
- ExpectedVars: []interface{}{p},
- Result: "`user`.`password` = ?",
- },
- {
- Expr: field.NewField("", "id").EqCol(field.NewField("", "new_id")),
- Result: "`id` = `new_id`",
- },
- {
- Expr: field.NewField("", "id").NeqCol(field.NewField("", "new_id")),
- Result: "`id` <> `new_id`",
- },
- {
- Expr: field.NewField("", "id").LtCol(field.NewField("", "new_id")),
- Result: "`id` < `new_id`",
- },
- {
- Expr: field.NewField("", "id").LteCol(field.NewField("", "new_id")),
- Result: "`id` <= `new_id`",
- },
- {
- Expr: field.NewField("", "id").GtCol(field.NewField("", "new_id")),
- Result: "`id` > `new_id`",
- },
- {
- Expr: field.NewField("", "id").GteCol(field.NewField("", "new_id")),
- Result: "`id` >= `new_id`",
- },
- {
- Expr: field.NewField("", "id").EqCol(field.NewField("", "new_id").Avg()),
- Result: "`id` = AVG(`new_id`)",
- },
- {
- Expr: field.NewField("", "id").EqCol(field.NewField("", "new_id").WithTable("tableB")),
- Result: "`id` = `tableB`.`new_id`",
- },
- {
- Expr: field.NewField("", "id").EqCol(field.NewField("", "new_id").WithTable("tableB")),
- Result: "`id` = `tableB`.`new_id`",
- },
- {
- Expr: field.NewField("", "id").NeqCol(field.NewField("", "new_id").WithTable("tableB")),
- Result: "`id` <> `tableB`.`new_id`",
- },
- {
- Expr: field.NewField("", "id").IsNull(),
- Result: "`id` IS NULL",
- },
- {
- Expr: field.NewField("", "id").IsNotNull(),
- Result: "`id` IS NOT NULL",
- },
- {
- Expr: field.NewField("", "id").GroupConcat(),
- Result: "GROUP_CONCAT(`id`)",
- },
- {
- Expr: field.Func.UnixTimestamp(),
- Result: "UNIX_TIMESTAMP()",
- },
- {
- Expr: field.Func.UnixTimestamp("2005-03-27 03:00:00").Mul(99),
- Result: "(UNIX_TIMESTAMP(?))*?",
- ExpectedVars: []interface{}{"2005-03-27 03:00:00", uint64(99)},
- },
- {
- Expr: field.NewInt("t1", "id").AddCol(field.NewInt("t2", "num")),
- Result: "`t1`.`id` + `t2`.`num`",
- },
- {
- Expr: field.NewInt("t1", "id").AddCol(field.NewInt("t1", "num")).SubCol(field.NewInt("t1", "age")),
- Result: "`t1`.`id` + `t1`.`num` - `t1`.`age`",
- },
- {
- Expr: field.NewInt("t1", "id").AddCol(field.NewInt("t1", "num")).SubCol(field.NewInt("t1", "age")).MulCol(field.NewInt("t1", "age")).DivCol(field.NewInt("t1", "base")),
- Result: "((`t1`.`id` + `t1`.`num` - `t1`.`age`) * (`t1`.`age`)) / (`t1`.`base`)",
- },
- {
- Expr: field.NewInt("t1", "id").AddCol(field.NewInt("t2", "num").Add(1)),
- Result: "`t1`.`id` + `t2`.`num`+?",
- ExpectedVars: []interface{}{int(1)},
- },
- {
- Expr: field.NewInt("t1", "id").EqCol(field.NewInt("t1", "id").AddCol(field.NewInt("t2", "num").Add(1))),
- Result: "`t1`.`id` = `t1`.`id` + `t2`.`num`+?",
- ExpectedVars: []interface{}{int(1)},
- },
- {
- Expr: field.NewInt("t1", "a").AddCol(field.NewInt("t2", "b").Add(1)).(field.Field).GtCol(field.NewInt("t", "c")),
- Result: "`t1`.`a` + `t2`.`b`+? > `t`.`c`",
- ExpectedVars: []interface{}{int(1)},
- },
- {
- Expr: field.ALL.Count(),
- Result: "COUNT(*)",
- },
- {
- Expr: field.ALL.Distinct().Count(),
- Result: "COUNT(DISTINCT *)",
- },
- {
- Expr: field.NewAsterisk("user").Count(),
- Result: "COUNT(`user`.*)",
- },
- {
- Expr: field.NewAsterisk("user").Distinct().Count(),
- Result: "COUNT(DISTINCT `user`.*)",
- },
- // ======================== integer ========================
- {
- Expr: field.NewUint("", "id"),
- Result: "`id`",
- },
- {
- Expr: field.NewUint("user", "id").Sum().Gt(100),
- ExpectedVars: []interface{}{uint(100)},
- Result: "SUM(`user`.`id`) > ?",
- },
- {
- Expr: field.NewUint("", "i`d"),
- Result: "`i``d`",
- },
- {
- Expr: field.NewUint("", "id").Avg(),
- Result: "AVG(`id`)",
- },
- {
- Expr: field.NewUint("", "id").Desc(),
- Result: "`id` DESC",
- },
- {
- Expr: field.NewUint("", "id").As("number"),
- Result: "`id` AS `number`",
- },
- {
- Expr: field.NewUint("", "id").Avg().As("number"),
- Result: "AVG(`id`) AS `number`",
- },
- {
- Expr: field.NewUint("", "id").Eq(10),
- ExpectedVars: []interface{}{uint(10)},
- Result: "`id` = ?",
- },
- {
- Expr: field.NewUint("", "id").Neq(10),
- ExpectedVars: []interface{}{uint(10)},
- Result: "`id` <> ?",
- },
- {
- Expr: field.NewUint("", "id").Gt(1),
- ExpectedVars: []interface{}{uint(1)},
- Result: "`id` > ?",
- },
- {
- Expr: field.NewUint("", "id").Gte(1),
- ExpectedVars: []interface{}{uint(1)},
- Result: "`id` >= ?",
- },
- {
- Expr: field.NewUint("", "id").Lt(1),
- ExpectedVars: []interface{}{uint(1)},
- Result: "`id` < ?",
- },
- {
- Expr: field.NewUint("", "id").Lte(1),
- ExpectedVars: []interface{}{uint(1)},
- Result: "`id` <= ?",
- },
- {
- Expr: field.NewUint("", "id").Mod(7),
- ExpectedVars: []interface{}{uint(7)},
- Result: "`id`%?",
- },
- {
- Expr: field.And(field.NewUint("", "id").Gt(1), field.NewUint("", "id").Lt(10)),
- ExpectedVars: []interface{}{uint(1), uint(10)},
- Result: "(`id` > ? AND `id` < ?)",
- },
- {
- Expr: field.Or(field.NewUint("", "id").Lt(4), field.NewUint("", "id").Gt(6)),
- ExpectedVars: []interface{}{uint(4), uint(6)},
- Result: "(`id` < ? OR `id` > ?)",
- },
- {
- Expr: field.NewUint("", "id").In(1, 2, 3),
- ExpectedVars: []interface{}{uint(1), uint(2), uint(3)},
- Result: "`id` IN (?,?,?)",
- },
- {
- Expr: field.NewUint("", "id").NotIn(1, 2, 3),
- ExpectedVars: []interface{}{uint(1), uint(2), uint(3)},
- Result: "`id` NOT IN (?,?,?)",
- },
- {
- Expr: field.NewUint("", "id").Between(1, 10),
- ExpectedVars: []interface{}{uint(1), uint(10)},
- Result: "`id` BETWEEN ? AND ?",
- },
- {
- Expr: field.NewUint("", "id").Count(),
- Result: "COUNT(`id`)",
- },
- {
- Expr: field.NewUint("", "id").Count().As("UserID"),
- Result: "COUNT(`id`) AS `UserID`",
- },
- {
- Expr: field.NewUint("", "id").Distinct(),
- Result: "DISTINCT `id`",
- },
- {
- Expr: field.NewUint("", "id").Distinct().Count(),
- Result: "COUNT(DISTINCT `id`)",
- },
- {
- Expr: field.NewUint("", "id").Distinct().Count().As("UserID"),
- Result: "COUNT(DISTINCT `id`) AS `UserID`",
- },
- {
- Expr: field.NewInt("", "age").RightShift(3),
- ExpectedVars: []interface{}{3},
- Result: "`age`>>?",
- },
- {
- Expr: field.NewInt("", "age").LeftShift(3),
- ExpectedVars: []interface{}{3},
- Result: "`age`<",
- },
- {
- Expr: field.NewInt("", "age").Add(1).Mul(2).Div(3),
- ExpectedVars: []interface{}{1, 2, 3},
- Result: "((`age`+?)*?)/?",
- },
- // ======================== float ========================
- {
- Expr: field.NewFloat64("", "score").Add(3.0),
- ExpectedVars: []interface{}{float64(3.0)},
- Result: "`score`+?",
- },
- {
- Expr: field.NewFloat64("", "score").Sub(3.0),
- ExpectedVars: []interface{}{float64(3.0)},
- Result: "`score`-?",
- },
- {
- Expr: field.NewFloat64("", "score").Mul(3.0),
- ExpectedVars: []interface{}{float64(3.0)},
- Result: "`score`*?",
- },
- {
- Expr: field.NewFloat64("", "score").Div(3.0),
- ExpectedVars: []interface{}{float64(3.0)},
- Result: "`score`/?",
- },
- {
- Expr: field.NewFloat64("", "score").FloorDiv(3.0),
- ExpectedVars: []interface{}{float64(3.0)},
- Result: "`score` DIV ?",
- },
- // ======================== string ========================
- {
- Expr: field.NewString("", "name").Eq("tom"),
- ExpectedVars: []interface{}{"tom"},
- Result: "`name` = ?",
- },
- {
- Expr: field.NewString("", "name").Neq("tom"),
- ExpectedVars: []interface{}{"tom"},
- Result: "`name` <> ?",
- },
- {
- Expr: field.NewString("", "name").Like("%%tom%%"),
- ExpectedVars: []interface{}{"%%tom%%"},
- Result: "`name` LIKE ?",
- },
- {
- Expr: field.NewString("", "name").NotLike("%%tom%%"),
- ExpectedVars: []interface{}{"%%tom%%"},
- Result: "`name` NOT LIKE ?",
- },
- {
- Expr: field.NewString("", "name").Regexp(".*"),
- ExpectedVars: []interface{}{".*"},
- Result: "`name` REGEXP ?",
- },
- {
- Expr: field.NewString("", "name").NotRegxp(".*"),
- ExpectedVars: []interface{}{".*"},
- Result: "NOT `name` REGEXP ?",
- },
- {
- Expr: field.NewString("", "address").FindInSetWith("sh"),
- ExpectedVars: []interface{}{"sh"},
- Result: "FIND_IN_SET(?,`address`)",
- },
- {
- Expr: field.NewString("", "address").FindInSet("sh"),
- ExpectedVars: []interface{}{"sh"},
- Result: "FIND_IN_SET(`address`,?)",
- },
- {
- Expr: field.NewString("", "address").Replace("address", "path"),
- ExpectedVars: []interface{}{"address", "path"},
- Result: "REPLACE(`address`,?,?)",
- },
- {
- Expr: field.NewString("", "address").Concat("[", "]"),
- ExpectedVars: []interface{}{"[", "]"},
- Result: "CONCAT(?,`address`,?)",
- },
- {
- Expr: field.NewString("", "address").Concat("", "_"),
- ExpectedVars: []interface{}{"_"},
- Result: "CONCAT(`address`,?)",
- },
- {
- Expr: field.NewString("", "address").Replace("address", "path").Concat("[", "]"),
- ExpectedVars: []interface{}{"[", "address", "path", "]"},
- Result: "CONCAT(?,REPLACE(`address`,?,?),?)",
- },
- // ======================== time ========================
- {
- Expr: field.NewTime("", "creatAt").Eq(timeData),
- ExpectedVars: []interface{}{timeData},
- Result: "`creatAt` = ?",
- },
- {
- Expr: field.NewTime("", "creatAt").Gt(timeData),
- ExpectedVars: []interface{}{timeData},
- Result: "`creatAt` > ?",
- },
- {
- Expr: field.NewTime("", "creatAt").Gte(timeData),
- ExpectedVars: []interface{}{timeData},
- Result: "`creatAt` >= ?",
- },
- {
- Expr: field.NewTime("", "creatAt").Lt(timeData),
- ExpectedVars: []interface{}{timeData},
- Result: "`creatAt` < ?",
- },
- {
- Expr: field.NewTime("", "creatAt").Lte(timeData),
- ExpectedVars: []interface{}{timeData},
- Result: "`creatAt` <= ?",
- },
- {
- Expr: field.NewTime("", "creatAt").Between(timeData, timeData.Add(24*time.Hour)),
- ExpectedVars: []interface{}{timeData, timeData.Add(24 * time.Hour)},
- Result: "`creatAt` BETWEEN ? AND ?",
- },
- {
- Expr: field.NewTime("", "creatAt").Add(24 * time.Hour),
- ExpectedVars: []interface{}{time.Duration(24 * time.Hour).Microseconds()},
- Result: "DATE_ADD(`creatAt`, INTERVAL ? MICROSECOND)",
- },
- {
- Expr: field.NewTime("", "creatAt").Sub(24 * time.Hour),
- ExpectedVars: []interface{}{time.Duration(24 * time.Hour).Microseconds()},
- Result: "DATE_SUB(`creatAt`, INTERVAL ? MICROSECOND)",
- },
- {
- Expr: field.NewTime("", "updateAt").DateFormat("%W %M %Y"),
- ExpectedVars: []interface{}{"%W %M %Y"},
- Result: "DATE_FORMAT(`updateAt`,?)",
- },
- // ======================== bool ========================
- {
- Expr: field.NewBool("", "male").Not(),
- Result: "NOT `male`",
- },
- {
- Expr: field.NewBool("", "male").Is(true),
- ExpectedVars: []interface{}{true},
- Result: "`male` = ?",
- },
- {
- Expr: field.NewBool("", "male").And(true),
- ExpectedVars: []interface{}{true},
- Result: "`male` AND ?",
- },
- {
- Expr: field.NewBool("", "male").Or(true),
- ExpectedVars: []interface{}{true},
- Result: "`male` OR ?",
- },
- }
-
- for _, testcase := range testcases {
- field.CheckBuildExpr(t, testcase.Expr, testcase.Result, testcase.ExpectedVars)
- }
-}
-
-func TestExpr_BuildColumn(t *testing.T) {
- stmt := field.GetStatement()
- id := field.NewUint("user", "id")
- expectColumnStr := "`id`"
- expectColumnStrWithTable := "`user`.`id`"
- expectColumnStrWithoutQuote := "id"
- expectColumnStrWithTableWithoutQuote := "user.id"
-
- if colStr := id.BuildColumn(stmt).String(); colStr != expectColumnStr {
- t.Errorf("id.BuildColumn(stmt).String() got: %q, except: %q", colStr, expectColumnStr)
- }
- if colStr := id.BuildColumn(stmt, field.WithTable).String(); colStr != expectColumnStrWithTable {
- t.Errorf("id.BuildColumn(stmt, field.WithTable).String() got: %q, except: %q", colStr, expectColumnStrWithTable)
- }
- if colStr := id.BuildColumn(stmt, field.WithoutQuote).String(); colStr != expectColumnStrWithoutQuote {
- t.Errorf("id.BuildColumn(stmt, field.WithoutQuote).String() got: %q, except: %q", colStr, expectColumnStrWithoutQuote)
- }
- if colStr := id.BuildColumn(stmt, field.WithTable, field.WithoutQuote).String(); colStr != expectColumnStrWithTableWithoutQuote {
- t.Errorf("id.BuildColumn(stmt, field.WithTable, field.WithoutQuote).String() got: %q, except: %q", colStr, expectColumnStrWithTableWithoutQuote)
- }
-
- expectStarColumnStr := "*"
- if colStr := field.Star.BuildColumn(stmt).String(); colStr != expectStarColumnStr {
- t.Errorf("field.Star.BuildColumn(stmt).String() got: %q, except: %q", colStr, expectStarColumnStr)
- }
-
- allField := field.NewString("user", "*")
- expectStarColumnStrWithTable := "`user`.*"
- if colStr := allField.BuildColumn(stmt, field.WithTable).String(); colStr != expectStarColumnStrWithTable {
- t.Errorf("allField.BuildColumn(stmt, field.WithTable).String() got: %q, except: %q", colStr, expectStarColumnStrWithTable)
- }
-}
-
-func BenchmarkExpr_Count(b *testing.B) {
- id := field.NewUint("", "id")
- for i := 0; i < b.N; i++ {
- n := id.Count()
- _ = n
- }
-}
-
-func TestRelation_StructField(t *testing.T) {
- var testdatas = []struct {
- relation *field.Relation
- expectedValue string
- }{
- {
- relation: field.NewRelation(
- "CreditCards", "model.CreditCard",
- *field.NewRelation("Owner", "model.Owner"),
- *field.NewRelation("Bank", "model.Bank",
- *field.NewRelation("Manager", "model.Bank"),
- *field.NewRelation("City", "model.City",
- *field.NewRelation("State", "model.Bank"),
- ),
- ),
- ),
- expectedValue: "Owner struct {\nfield.RelationField\n}\nBank struct {\nfield.RelationField\nManager struct {\nfield.RelationField\n}\nCity struct {\nfield.RelationField\nState struct {\nfield.RelationField\n}\n}\n}\n",
- },
- }
-
- for _, testdata := range testdatas {
- if result := testdata.relation.StructField(); result != testdata.expectedValue {
- t.Errorf("StructField fail: except %q, got %q", testdata.expectedValue, result)
- }
- }
-}
-
-func TestRelation_StructFieldInit(t *testing.T) {
- var testdatas = []struct {
- relation *field.Relation
- expectedValue string
- }{
- {
- relation: field.NewRelation(
- "CreditCards", "model.CreditCard",
- *field.NewRelation("Owner", "model.Owner"),
- *field.NewRelation("Bank", "model.Bank",
- *field.NewRelation("Manager", "model.Manager"),
- *field.NewRelation("City", "model.City",
- *field.NewRelation("State", "model.State"),
- ),
- ),
- ),
- expectedValue: "RelationField: field.NewRelation(\"CreditCards\", \"model.CreditCard\"),\nOwner: struct {\nfield.RelationField\n}{\nRelationField: field.NewRelation(\"CreditCards.Owner\", \"model.Owner\"),\n},\nBank: struct {\nfield.RelationField\nManager struct {\nfield.RelationField\n}\nCity struct {\nfield.RelationField\nState struct {\nfield.RelationField\n}\n}}{\nRelationField: field.NewRelation(\"CreditCards.Bank\", \"model.Bank\"),\nManager: struct {\nfield.RelationField\n}{\nRelationField: field.NewRelation(\"CreditCards.Bank.Manager\", \"model.Manager\"),\n},\nCity: struct {\nfield.RelationField\nState struct {\nfield.RelationField\n}}{\nRelationField: field.NewRelation(\"CreditCards.Bank.City\", \"model.City\"),\nState: struct {\nfield.RelationField\n}{\nRelationField: field.NewRelation(\"CreditCards.Bank.City.State\", \"model.State\"),\n},\n},\n},\n",
- },
- }
-
- for _, testdata := range testdatas {
- if result := testdata.relation.StructFieldInit(); result != testdata.expectedValue {
- t.Errorf("StructField fail: except %q, got %q", testdata.expectedValue, result)
- }
- }
-}
-
-func expectedStruct() { // nolint
- _ = struct {
- field.RelationField
- Owner struct {
- field.RelationField
- }
- Bank struct {
- field.RelationField
- Manager struct {
- field.RelationField
- }
- City struct {
- field.RelationField
- State struct {
- field.RelationField
- }
- }
- }
- }{
- RelationField: field.NewRelation("CreditCards", "model.CreditCard"),
- Owner: struct {
- field.RelationField
- }{
- RelationField: field.NewRelation("CreditCards.Owner", "model.Owner"),
- },
- Bank: struct {
- field.RelationField
- Manager struct {
- field.RelationField
- }
- City struct {
- field.RelationField
- State struct {
- field.RelationField
- }
- }
- }{
- RelationField: field.NewRelation("CreditCards.Bank", "model.Bank"),
- Manager: struct {
- field.RelationField
- }{
- RelationField: field.NewRelation("CreditCards.Bank.Manager", "model.Manager"),
- },
- City: struct {
- field.RelationField
- State struct {
- field.RelationField
- }
- }{
- RelationField: field.NewRelation("CreditCards.Bank.City", "model.City"),
- State: struct {
- field.RelationField
- }{
- RelationField: field.NewRelation("CreditCards.Bank.City.State", "model.State"),
- },
- },
- },
- }
-}
diff --git a/toolkit/gormgen/field/expr.go b/toolkit/gormgen/field/expr.go
deleted file mode 100644
index c0d63bef..00000000
--- a/toolkit/gormgen/field/expr.go
+++ /dev/null
@@ -1,408 +0,0 @@
-package field
-
-import (
- "fmt"
- "strings"
- "time"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/clause"
-)
-
-var _ Expr = new(Field)
-
-// AssignExpr assign expression
-type AssignExpr interface {
- Expr
-
- AssignExpr() expression
-}
-
-// Expr a query expression about field
-type Expr interface {
- // Clause Expression interface
- Build(clause.Builder)
-
- As(alias string) Expr
- ColumnName() sql
- BuildColumn(*gorm.Statement, ...BuildOpt) sql
- BuildWithArgs(*gorm.Statement) (query sql, args []interface{})
- RawExpr() expression
-
- // col operate expression
- AddCol(col Expr) Expr
- SubCol(col Expr) Expr
- MulCol(col Expr) Expr
- DivCol(col Expr) Expr
- ConcatCol(cols ...Expr) Expr
-
- // implement Condition
- BeCond() interface{}
- CondError() error
-
- expression() clause.Expression
-}
-
-// OrderExpr order expression
-// used in Order()
-type OrderExpr interface {
- Expr
- Desc() Expr
-}
-
-type expression interface{}
-
-type sql string
-
-func (e sql) String() string { return string(e) }
-
-type expr struct {
- col clause.Column
-
- e clause.Expression
- buildOpts []BuildOpt
-}
-
-func (e expr) BeCond() interface{} { return e.expression() }
-func (expr) CondError() error { return nil }
-
-func (e expr) AssignExpr() expression {
- return e.expression()
-}
-
-func (e expr) expression() clause.Expression {
- if e.e == nil {
- return clause.NamedExpr{SQL: "?", Vars: []interface{}{e.col}}
- }
- return e.e
-}
-
-func (e expr) ColumnName() sql { return sql(e.col.Name) }
-
-// BuildOpt build option
-type BuildOpt uint
-
-const (
- // WithTable build column with table
- WithTable BuildOpt = iota
-
- // WithAll build column with table and alias
- WithAll
-
- // WithoutQuote build column without quote
- WithoutQuote
-)
-
-func (e expr) BuildColumn(stmt *gorm.Statement, opts ...BuildOpt) sql {
- col := clause.Column{Name: e.col.Name}
- for _, opt := range append(e.buildOpts, opts...) {
- switch opt {
- case WithTable:
- col.Table = e.col.Table
- case WithAll:
- col.Table = e.col.Table
- col.Alias = e.col.Alias
- case WithoutQuote:
- col.Raw = true
- }
- }
- if col.Name == "*" {
- if col.Table != "" {
- return sql(stmt.Quote(col.Table)) + ".*"
- }
- return "*"
- }
- return sql(stmt.Quote(col))
-}
-
-func (e expr) Build(builder clause.Builder) {
- if e.e == nil {
- if stmt, ok := builder.(*gorm.Statement); ok {
- builder.WriteString(string(e.BuildColumn(stmt, WithAll)))
- return
- }
- }
-
- e.e.Build(builder)
-}
-
-func (e expr) BuildWithArgs(stmt *gorm.Statement) (sql, []interface{}) {
- if e.e == nil {
- return sql(e.BuildColumn(stmt, WithAll)), nil
- }
- newStmt := &gorm.Statement{DB: stmt.DB, Table: stmt.Table, Schema: stmt.Schema}
- e.e.Build(newStmt)
- return sql(newStmt.SQL.String()), newStmt.Vars
-}
-
-func (e expr) RawExpr() expression {
- if e.e == nil {
- return e.col
- }
- return e.e
-}
-
-func (e expr) setE(expression clause.Expression) expr {
- e.e = expression
- return e
-}
-
-func (e expr) appendBuildOpts(opts ...BuildOpt) expr {
- e.buildOpts = append(e.buildOpts, opts...)
- return e
-}
-
-// ======================== basic function ========================
-func (e expr) WithTable(table string) Expr {
- e.col.Table = table
- return e
-}
-
-func (e expr) IsNull() Expr {
- return e.setE(clause.Expr{SQL: "? IS NULL", Vars: []interface{}{e.RawExpr()}})
-}
-
-func (e expr) IsNotNull() Expr {
- return e.setE(clause.Expr{SQL: "? IS NOT NULL", Vars: []interface{}{e.RawExpr()}})
-}
-
-func (e expr) Count() Int {
- return Int{e.setE(clause.Expr{SQL: "COUNT(?)", Vars: []interface{}{e.RawExpr()}})}
-}
-
-func (e expr) Distinct() Int {
- return Int{e.setE(clause.Expr{SQL: "DISTINCT ?", Vars: []interface{}{e.RawExpr()}})}
-}
-
-func (e expr) Length() Int {
- return Int{e.setE(clause.Expr{SQL: "LENGTH(?)", Vars: []interface{}{e.RawExpr()}})}
-}
-
-func (e expr) Max() Float64 {
- return Float64{e.setE(clause.Expr{SQL: "MAX(?)", Vars: []interface{}{e.RawExpr()}})}
-}
-
-func (e expr) Min() Float64 {
- return Float64{e.setE(clause.Expr{SQL: "MIN(?)", Vars: []interface{}{e.RawExpr()}})}
-}
-
-func (e expr) Avg() Float64 {
- return Float64{e.setE(clause.Expr{SQL: "AVG(?)", Vars: []interface{}{e.RawExpr()}})}
-}
-
-func (e expr) Null() AssignExpr {
- return e.setE(clause.Eq{Column: e.col.Name, Value: nil})
-}
-
-func (e expr) GroupConcat() Expr {
- return e.setE(clause.Expr{SQL: "GROUP_CONCAT(?)", Vars: []interface{}{e.RawExpr()}})
-}
-
-// ======================== comparison between columns ========================
-func (e expr) EqCol(col Expr) Expr {
- return e.setE(clause.Expr{SQL: "? = ?", Vars: []interface{}{e.RawExpr(), col.RawExpr()}})
-}
-
-func (e expr) NeqCol(col Expr) Expr {
- return e.setE(clause.Expr{SQL: "? <> ?", Vars: []interface{}{e.RawExpr(), col.RawExpr()}})
-}
-
-func (e expr) GtCol(col Expr) Expr {
- return e.setE(clause.Expr{SQL: "? > ?", Vars: []interface{}{e.RawExpr(), col.RawExpr()}})
-}
-
-func (e expr) GteCol(col Expr) Expr {
- return e.setE(clause.Expr{SQL: "? >= ?", Vars: []interface{}{e.RawExpr(), col.RawExpr()}})
-}
-
-func (e expr) LtCol(col Expr) Expr {
- return e.setE(clause.Expr{SQL: "? < ?", Vars: []interface{}{e.RawExpr(), col.RawExpr()}})
-}
-
-func (e expr) LteCol(col Expr) Expr {
- return e.setE(clause.Expr{SQL: "? <= ?", Vars: []interface{}{e.RawExpr(), col.RawExpr()}})
-}
-
-func (e expr) SetCol(col Expr) AssignExpr {
- return e.setE(clause.Eq{Column: e.col.Name, Value: col.RawExpr()})
-}
-
-// ======================== operate columns ========================
-func (e expr) AddCol(col Expr) Expr {
- return Field{e.setE(clause.Expr{SQL: "? + ?", Vars: []interface{}{e.RawExpr(), col.RawExpr()}})}
-}
-
-func (e expr) SubCol(col Expr) Expr {
- return Field{e.setE(clause.Expr{SQL: "? - ?", Vars: []interface{}{e.RawExpr(), col.RawExpr()}})}
-}
-
-func (e expr) MulCol(col Expr) Expr {
- return Field{e.setE(clause.Expr{SQL: "(?) * (?)", Vars: []interface{}{e.RawExpr(), col.RawExpr()}})}
-}
-
-func (e expr) DivCol(col Expr) Expr {
- return Field{e.setE(clause.Expr{SQL: "(?) / (?)", Vars: []interface{}{e.RawExpr(), col.RawExpr()}})}
-}
-
-func (e expr) ConcatCol(cols ...Expr) Expr {
- placeholders := []string{"?"}
- vars := []interface{}{e.RawExpr()}
- for _, col := range cols {
- placeholders = append(placeholders, "?")
- vars = append(vars, col.RawExpr())
- }
- return Field{e.setE(clause.Expr{
- SQL: fmt.Sprintf("Concat(%s)", strings.Join(placeholders, ",")),
- Vars: vars,
- })}
-}
-
-// ======================== keyword ========================
-func (e expr) As(alias string) Expr {
- if e.e != nil {
- return e.setE(clause.Expr{SQL: "? AS ?", Vars: []interface{}{e.e, clause.Column{Name: alias}}})
- }
- e.col.Alias = alias
- return e
-}
-
-func (e expr) Desc() Expr {
- return e.setE(clause.Expr{SQL: "? DESC", Vars: []interface{}{e.RawExpr()}})
-}
-
-// ======================== general experssion ========================
-func (e expr) value(value interface{}) AssignExpr {
- return e.setE(clause.Eq{Column: e.col.Name, Value: value})
-}
-
-func (e expr) between(values []interface{}) expr {
- return e.setE(clause.Expr{SQL: "? BETWEEN ? AND ?", Vars: append([]interface{}{e.RawExpr()}, values...)})
-}
-
-func (e expr) add(value interface{}) expr {
- switch v := value.(type) {
- case time.Duration:
- return e.setE(clause.Expr{SQL: "DATE_ADD(?, INTERVAL ? MICROSECOND)", Vars: []interface{}{e.RawExpr(), v.Microseconds()}})
- default:
- return e.setE(clause.Expr{SQL: "?+?", Vars: []interface{}{e.RawExpr(), value}})
- }
-}
-
-func (e expr) sub(value interface{}) expr {
- switch v := value.(type) {
- case time.Duration:
- return e.setE(clause.Expr{SQL: "DATE_SUB(?, INTERVAL ? MICROSECOND)", Vars: []interface{}{e.RawExpr(), v.Microseconds()}})
- default:
- return e.setE(clause.Expr{SQL: "?-?", Vars: []interface{}{e.RawExpr(), value}})
- }
-}
-
-func (e expr) mul(value interface{}) expr {
- if e.isPure() {
- return e.setE(clause.Expr{SQL: "?*?", Vars: []interface{}{e.col, value}})
- }
- return e.setE(clause.Expr{SQL: "(?)*?", Vars: []interface{}{e.e, value}})
-}
-
-func (e expr) div(value interface{}) expr {
- if e.isPure() {
- return e.setE(clause.Expr{SQL: "?/?", Vars: []interface{}{e.col, value}})
- }
- return e.setE(clause.Expr{SQL: "(?)/?", Vars: []interface{}{e.e, value}})
-}
-
-func (e expr) mod(value interface{}) expr {
- if e.isPure() {
- return e.setE(clause.Expr{SQL: "?%?", Vars: []interface{}{e.col, value}})
- }
- return e.setE(clause.Expr{SQL: "(?)%?", Vars: []interface{}{e.e, value}})
-}
-
-func (e expr) floorDiv(value interface{}) expr {
- if e.isPure() {
- return e.setE(clause.Expr{SQL: "? DIV ?", Vars: []interface{}{e.col, value}})
- }
- return e.setE(clause.Expr{SQL: "(?) DIV ?", Vars: []interface{}{e.e, value}})
-}
-
-func (e expr) floor() expr {
- return e.setE(clause.Expr{SQL: "FLOOR(?)", Vars: []interface{}{e.RawExpr()}})
-}
-
-func (e expr) rightShift(value interface{}) expr {
- if e.isPure() {
- return e.setE(clause.Expr{SQL: "?>>?", Vars: []interface{}{e.col, value}})
- }
- return e.setE(clause.Expr{SQL: "(?)>>?", Vars: []interface{}{e.e, value}})
-}
-
-func (e expr) leftShift(value interface{}) expr {
- if e.isPure() {
- return e.setE(clause.Expr{SQL: "?<", Vars: []interface{}{e.col, value}})
- }
- return e.setE(clause.Expr{SQL: "(?)<", Vars: []interface{}{e.e, value}})
-}
-
-func (e expr) bitXor(value interface{}) expr {
- if e.isPure() {
- return e.setE(clause.Expr{SQL: "?^?", Vars: []interface{}{e.col, value}})
- }
- return e.setE(clause.Expr{SQL: "(?)^?", Vars: []interface{}{e.e, value}})
-}
-
-func (e expr) bitAnd(value interface{}) expr {
- if e.isPure() {
- return e.setE(clause.Expr{SQL: "?&?", Vars: []interface{}{e.col, value}})
- }
- return e.setE(clause.Expr{SQL: "(?)&?", Vars: []interface{}{e.e, value}})
-}
-
-func (e expr) bitOr(value interface{}) expr {
- if e.isPure() {
- return e.setE(clause.Expr{SQL: "?|?", Vars: []interface{}{e.col, value}})
- }
- return e.setE(clause.Expr{SQL: "(?)|?", Vars: []interface{}{e.e, value}})
-}
-
-func (e expr) bitFlip() expr {
- if e.isPure() {
- return e.setE(clause.Expr{SQL: "~?", Vars: []interface{}{e.col}})
- }
- return e.setE(clause.Expr{SQL: "~(?)", Vars: []interface{}{e.RawExpr()}})
-}
-
-func (e expr) regexp(value interface{}) expr {
- return e.setE(clause.Expr{SQL: "? REGEXP ?", Vars: []interface{}{e.RawExpr(), value}})
-}
-
-func (e expr) not() expr {
- return e.setE(clause.Expr{SQL: "NOT ?", Vars: []interface{}{e.RawExpr()}})
-}
-
-func (e expr) is(value interface{}) expr {
- return e.setE(clause.Eq{Column: e.RawExpr(), Value: value})
-}
-
-func (e expr) and(value interface{}) expr {
- return e.setE(clause.Expr{SQL: "? AND ?", Vars: []interface{}{e.RawExpr(), value}})
-}
-
-func (e expr) or(value interface{}) expr {
- return e.setE(clause.Expr{SQL: "? OR ?", Vars: []interface{}{e.RawExpr(), value}})
-}
-
-func (e expr) xor(value interface{}) expr {
- return e.setE(clause.Expr{SQL: "? XOR ?", Vars: []interface{}{e.RawExpr(), value}})
-}
-
-func (e expr) isPure() bool {
- return e.e == nil
-}
-
-func (e expr) ifNull(value interface{}) expr {
- return e.setE(clause.Expr{SQL: "IFNULL(?,?)", Vars: []interface{}{e.RawExpr(), value}})
-}
-
-func (e expr) sum() expr {
- return e.setE(clause.Expr{SQL: "SUM(?)", Vars: []interface{}{e.RawExpr()}})
-}
diff --git a/toolkit/gormgen/field/field.go b/toolkit/gormgen/field/field.go
deleted file mode 100644
index 786d2702..00000000
--- a/toolkit/gormgen/field/field.go
+++ /dev/null
@@ -1,79 +0,0 @@
-package field
-
-import (
- "database/sql/driver"
-
- "github.com/wubin1989/gorm/clause"
-)
-
-// ScanValuer interface for Field
-type ScanValuer interface {
- Scan(src interface{}) error // sql.Scanner
- Value() (driver.Value, error) // driver.Valuer
-}
-
-// Field a standard field struct
-type Field struct{ expr }
-
-// Eq judge equal
-func (field Field) Eq(value driver.Valuer) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq judge not equal
-func (field Field) Neq(value driver.Valuer) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Field) In(values ...driver.Valuer) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// Gt ...
-func (field Field) Gt(value driver.Valuer) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte ...
-func (field Field) Gte(value driver.Valuer) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt ...
-func (field Field) Lt(value driver.Valuer) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte ...
-func (field Field) Lte(value driver.Valuer) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// Like ...
-func (field Field) Like(value driver.Valuer) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// Value ...
-func (field Field) Value(value driver.Valuer) AssignExpr {
- return field.value(value)
-}
-
-// Sum ...
-func (field Field) Sum() Field {
- return Field{field.sum()}
-}
-
-// IfNull ...
-func (field Field) IfNull(value driver.Valuer) Expr {
- return field.ifNull(value)
-}
-
-func (field Field) toSlice(values ...driver.Valuer) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
diff --git a/toolkit/gormgen/field/field_test.go b/toolkit/gormgen/field/field_test.go
deleted file mode 100644
index b21aaa80..00000000
--- a/toolkit/gormgen/field/field_test.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package field
-
-import (
- "reflect"
- "strings"
- "sync"
- "testing"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/clause"
- "github.com/wubin1989/gorm/schema"
- "github.com/wubin1989/gorm/utils/tests"
-)
-
-var db, _ = gorm.Open(tests.DummyDialector{}, nil)
-
-func GetStatement() *gorm.Statement {
- user, _ := schema.Parse(&User{}, &sync.Map{}, db.NamingStrategy)
- return &gorm.Statement{DB: db, Table: user.Table, Schema: user, Clauses: map[string]clause.Clause{}}
-}
-
-func CheckBuildExpr(t *testing.T, e Expr, result string, vars []interface{}) {
- stmt := GetStatement()
-
- e.expression().Build(stmt)
-
- sql := strings.TrimSpace(stmt.SQL.String())
- if sql != result {
- t.Errorf("SQL expects %v got %v", result, sql)
- }
-
- if !reflect.DeepEqual(stmt.Vars, vars) {
- t.Errorf("Vars expects %+v got %v", stmt.Vars, vars)
- }
-}
-
-func BuildToString(e Expr) (string, []interface{}) {
- stmt := GetStatement()
-
- e.expression().Build(stmt)
-
- return stmt.SQL.String(), stmt.Vars
-}
-
-type User struct {
- gorm.Model
- Name string
- Age uint
- // Birthday *time.Time
- // Account Account
- // Pets []*Pet
- // Toys []Toy `gorm:"polymorphic:Owner"`
- // CompanyID *int
- // Company Company
- // ManagerID *uint
- // Manager *User
- // Team []User `gorm:"foreignkey:ManagerID"`
- // Languages []Language `gorm:"many2many:UserSpeak;"`
- // Friends []*User `gorm:"many2many:user_friends;"`
- // Active bool
-}
diff --git a/toolkit/gormgen/field/float.go b/toolkit/gormgen/field/float.go
deleted file mode 100644
index f7929e9a..00000000
--- a/toolkit/gormgen/field/float.go
+++ /dev/null
@@ -1,245 +0,0 @@
-package field
-
-import "github.com/wubin1989/gorm/clause"
-
-// Float64 float64 type field
-type Float64 Field
-
-// Eq equal to
-func (field Float64) Eq(value float64) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Float64) Neq(value float64) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Float64) Gt(value float64) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Float64) Gte(value float64) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Float64) Lt(value float64) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Float64) Lte(value float64) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Float64) In(values ...float64) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Float64) NotIn(values ...float64) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Between ...
-func (field Float64) Between(left float64, right float64) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Float64) NotBetween(left float64, right float64) Expr {
- return Not(field.Between(left, right))
-}
-
-// Like ...
-func (field Float64) Like(value float64) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Float64) NotLike(value float64) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Add ...
-func (field Float64) Add(value float64) Float64 {
- return Float64{field.add(value)}
-}
-
-// Sub ...
-func (field Float64) Sub(value float64) Float64 {
- return Float64{field.sub(value)}
-}
-
-// Mul ...
-func (field Float64) Mul(value float64) Float64 {
- return Float64{field.mul(value)}
-}
-
-// Div ...
-func (field Float64) Div(value float64) Float64 {
- return Float64{field.div(value)}
-}
-
-// FloorDiv ...
-func (field Float64) FloorDiv(value float64) Int {
- return Int{field.floorDiv(value)}
-}
-
-// Floor ...
-func (field Float64) Floor() Int {
- return Int{field.floor()}
-}
-
-// Value set value
-func (field Float64) Value(value float64) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Float64) Zero() AssignExpr {
- return field.value(0)
-}
-
-// Sum ...
-func (field Float64) Sum() Float64 {
- return Float64{field.sum()}
-}
-
-// IfNull ...
-func (field Float64) IfNull(value float64) Expr {
- return field.ifNull(value)
-}
-
-func (field Float64) toSlice(values ...float64) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
-
-// Float32 float32 type field
-type Float32 Float64
-
-// Eq equal to
-func (field Float32) Eq(value float32) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Float32) Neq(value float32) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Float32) Gt(value float32) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Float32) Gte(value float32) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Float32) Lt(value float32) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Float32) Lte(value float32) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Float32) In(values ...float32) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Float32) NotIn(values ...float32) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Between ...
-func (field Float32) Between(left float32, right float32) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Float32) NotBetween(left float32, right float32) Expr {
- return Not(field.Between(left, right))
-}
-
-// Like ...
-func (field Float32) Like(value float32) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Float32) NotLike(value float32) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Add ...
-func (field Float32) Add(value float32) Float32 {
- return Float32{field.add(value)}
-}
-
-// Sub ...
-func (field Float32) Sub(value float32) Float32 {
- return Float32{field.sub(value)}
-}
-
-// Mul ...
-func (field Float32) Mul(value float32) Float32 {
- return Float32{field.mul(value)}
-}
-
-// Div ...
-func (field Float32) Div(value float32) Float32 {
- return Float32{field.div(value)}
-}
-
-// FloorDiv ...
-func (field Float32) FloorDiv(value float32) Int {
- return Int{field.floorDiv(value)}
-}
-
-// Floor ...
-func (field Float32) Floor() Int {
- return Int{field.floor()}
-}
-
-// Value set value
-func (field Float32) Value(value float32) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Float32) Zero() AssignExpr {
- return field.value(0)
-}
-
-// Sum ...
-func (field Float32) Sum() Float32 {
- return Float32{field.sum()}
-}
-
-// IfNull ...
-func (field Float32) IfNull(value float32) Expr {
- return field.ifNull(value)
-}
-
-func (field Float32) toSlice(values ...float32) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
diff --git a/toolkit/gormgen/field/function.go b/toolkit/gormgen/field/function.go
deleted file mode 100644
index 241f96ed..00000000
--- a/toolkit/gormgen/field/function.go
+++ /dev/null
@@ -1,28 +0,0 @@
-package field
-
-import (
- "strings"
-
- "github.com/wubin1989/gorm/clause"
-)
-
-// Func sql functions
-var Func = new(function)
-
-type function struct{}
-
-// UnixTimestamp same as UNIX_TIMESTAMP([date])
-func (f *function) UnixTimestamp(date ...string) Uint64 {
- if len(date) > 0 {
- return Uint64{expr{e: clause.Expr{SQL: "UNIX_TIMESTAMP(?)", Vars: []interface{}{date[0]}}}}
- }
- return Uint64{expr{e: clause.Expr{SQL: "UNIX_TIMESTAMP()"}}}
-}
-
-// FromUnixTime FROM_UNIXTIME(unix_timestamp[,format])
-func (f *function) FromUnixTime(date uint64, format string) String {
- if strings.TrimSpace(format) != "" {
- return String{expr{e: clause.Expr{SQL: "FROM_UNIXTIME(?, ?)", Vars: []interface{}{date, format}}}}
- }
- return String{expr{e: clause.Expr{SQL: "FROM_UNIXTIME(?)", Vars: []interface{}{date}}}}
-}
diff --git a/toolkit/gormgen/field/int.go b/toolkit/gormgen/field/int.go
deleted file mode 100644
index 1beb76f9..00000000
--- a/toolkit/gormgen/field/int.go
+++ /dev/null
@@ -1,1515 +0,0 @@
-package field
-
-import (
- "github.com/wubin1989/gorm/clause"
-)
-
-// Int int type field
-type Int Field
-
-// Eq equal to
-func (field Int) Eq(value int) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Int) Neq(value int) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Int) Gt(value int) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Int) Gte(value int) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Int) Lt(value int) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Int) Lte(value int) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Int) In(values ...int) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Int) NotIn(values ...int) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Between ...
-func (field Int) Between(left int, right int) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Int) NotBetween(left int, right int) Expr {
- return Not(field.Between(left, right))
-}
-
-// Like ...
-func (field Int) Like(value int) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Int) NotLike(value int) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Add ...
-func (field Int) Add(value int) Int {
- return Int{field.add(value)}
-}
-
-// Sub ...
-func (field Int) Sub(value int) Int {
- return Int{field.sub(value)}
-}
-
-// Mul ...
-func (field Int) Mul(value int) Int {
- return Int{field.mul(value)}
-}
-
-// Div ...
-func (field Int) Div(value int) Int {
- return Int{field.div(value)}
-}
-
-// Mod ...
-func (field Int) Mod(value int) Int {
- return Int{field.mod(value)}
-}
-
-// FloorDiv ...
-func (field Int) FloorDiv(value int) Int {
- return Int{field.floorDiv(value)}
-}
-
-// RightShift ...
-func (field Int) RightShift(value int) Int {
- return Int{field.rightShift(value)}
-}
-
-// LeftShift ...
-func (field Int) LeftShift(value int) Int {
- return Int{field.leftShift(value)}
-}
-
-// BitXor ...
-func (field Int) BitXor(value int) Int {
- return Int{field.bitXor(value)}
-}
-
-// BitAnd ...
-func (field Int) BitAnd(value int) Int {
- return Int{field.bitAnd(value)}
-}
-
-// BitOr ...
-func (field Int) BitOr(value int) Int {
- return Int{field.bitOr(value)}
-}
-
-// BitFlip ...
-func (field Int) BitFlip() Int {
- return Int{field.bitFlip()}
-}
-
-// Value set value
-func (field Int) Value(value int) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Int) Zero() AssignExpr {
- return field.value(0)
-}
-
-// Sum ...
-func (field Int) Sum() Int {
- return Int{field.sum()}
-}
-
-// IfNull ...
-func (field Int) IfNull(value int) Expr {
- return field.ifNull(value)
-}
-
-func (field Int) toSlice(values ...int) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
-
-// Int8 int8 type field
-type Int8 Int
-
-// Eq equal to
-func (field Int8) Eq(value int8) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Int8) Neq(value int8) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Int8) Gt(value int8) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Int8) Gte(value int8) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Int8) Lt(value int8) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Int8) Lte(value int8) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Int8) In(values ...int8) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Int8) NotIn(values ...int8) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Between ...
-func (field Int8) Between(left int8, right int8) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Int8) NotBetween(left int8, right int8) Expr {
- return Not(field.Between(left, right))
-}
-
-// Like ...
-func (field Int8) Like(value int8) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Int8) NotLike(value int8) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Add ...
-func (field Int8) Add(value int8) Int8 {
- return Int8{field.add(value)}
-}
-
-// Sub ...
-func (field Int8) Sub(value int8) Int8 {
- return Int8{field.sub(value)}
-}
-
-// Mul ...
-func (field Int8) Mul(value int8) Int8 {
- return Int8{field.mul(value)}
-}
-
-// Div ...
-func (field Int8) Div(value int8) Int8 {
- return Int8{field.div(value)}
-}
-
-// Mod ...
-func (field Int8) Mod(value int8) Int8 {
- return Int8{field.mod(value)}
-}
-
-// FloorDiv ...
-func (field Int8) FloorDiv(value int8) Int8 {
- return Int8{field.floorDiv(value)}
-}
-
-// RightShift ...
-func (field Int8) RightShift(value int8) Int8 {
- return Int8{field.rightShift(value)}
-}
-
-// LeftShift ...
-func (field Int8) LeftShift(value int8) Int8 {
- return Int8{field.leftShift(value)}
-}
-
-// BitXor ...
-func (field Int8) BitXor(value int8) Int8 {
- return Int8{field.bitXor(value)}
-}
-
-// BitAnd ...
-func (field Int8) BitAnd(value int8) Int8 {
- return Int8{field.bitAnd(value)}
-}
-
-// BitOr ...
-func (field Int8) BitOr(value int8) Int8 {
- return Int8{field.bitOr(value)}
-}
-
-// BitFlip ...
-func (field Int8) BitFlip() Int8 {
- return Int8{field.bitFlip()}
-}
-
-// Value set value
-func (field Int8) Value(value int8) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Int8) Zero() AssignExpr {
- return field.value(0)
-}
-
-// Sum ...
-func (field Int8) Sum() Int8 {
- return Int8{field.sum()}
-}
-
-// IfNull ...
-func (field Int8) IfNull(value int8) Expr {
- return field.ifNull(value)
-}
-
-func (field Int8) toSlice(values ...int8) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
-
-// Int16 int16 type field
-type Int16 Int
-
-// Eq equal to
-func (field Int16) Eq(value int16) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Int16) Neq(value int16) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Int16) Gt(value int16) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Int16) Gte(value int16) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Int16) Lt(value int16) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Int16) Lte(value int16) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Int16) In(values ...int16) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Int16) NotIn(values ...int16) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Between ...
-func (field Int16) Between(left int16, right int16) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Int16) NotBetween(left int16, right int16) Expr {
- return Not(field.Between(left, right))
-}
-
-// Like ...
-func (field Int16) Like(value int16) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Int16) NotLike(value int16) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Add ...
-func (field Int16) Add(value int16) Int16 {
- return Int16{field.add(value)}
-}
-
-// Sub ...
-func (field Int16) Sub(value int16) Int16 {
- return Int16{field.sub(value)}
-}
-
-// Mul ...
-func (field Int16) Mul(value int16) Int16 {
- return Int16{field.mul(value)}
-}
-
-// Div ...
-func (field Int16) Div(value int16) Int16 {
- return Int16{field.div(value)}
-}
-
-// Mod ...
-func (field Int16) Mod(value int16) Int16 {
- return Int16{field.mod(value)}
-}
-
-// FloorDiv ...
-func (field Int16) FloorDiv(value int16) Int16 {
- return Int16{field.floorDiv(value)}
-}
-
-// RightShift ...
-func (field Int16) RightShift(value int16) Int16 {
- return Int16{field.rightShift(value)}
-}
-
-// LeftShift ...
-func (field Int16) LeftShift(value int16) Int16 {
- return Int16{field.leftShift(value)}
-}
-
-// BitXor ...
-func (field Int16) BitXor(value int16) Int16 {
- return Int16{field.bitXor(value)}
-}
-
-// BitAnd ...
-func (field Int16) BitAnd(value int16) Int16 {
- return Int16{field.bitAnd(value)}
-}
-
-// BitOr ...
-func (field Int16) BitOr(value int16) Int16 {
- return Int16{field.bitOr(value)}
-}
-
-// BitFlip ...
-func (field Int16) BitFlip() Int16 {
- return Int16{field.bitFlip()}
-}
-
-// Value set value
-func (field Int16) Value(value int16) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Int16) Zero() AssignExpr {
- return field.value(0)
-}
-
-// Sum ...
-func (field Int16) Sum() Int16 {
- return Int16{field.sum()}
-}
-
-// IfNull ...
-func (field Int16) IfNull(value int16) Expr {
- return field.ifNull(value)
-}
-
-func (field Int16) toSlice(values ...int16) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
-
-// Int32 int32 type field
-type Int32 Int
-
-// Eq equal to
-func (field Int32) Eq(value int32) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Int32) Neq(value int32) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Int32) Gt(value int32) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Int32) Gte(value int32) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Int32) Lt(value int32) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Int32) Lte(value int32) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Int32) In(values ...int32) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Int32) NotIn(values ...int32) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Between ...
-func (field Int32) Between(left int32, right int32) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Int32) NotBetween(left int32, right int32) Expr {
- return Not(field.Between(left, right))
-}
-
-// Like ...
-func (field Int32) Like(value int32) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Int32) NotLike(value int32) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Add ...
-func (field Int32) Add(value int32) Int32 {
- return Int32{field.add(value)}
-}
-
-// Sub ...
-func (field Int32) Sub(value int32) Int32 {
- return Int32{field.sub(value)}
-}
-
-// Mul ...
-func (field Int32) Mul(value int32) Int32 {
- return Int32{field.mul(value)}
-}
-
-// Div ...
-func (field Int32) Div(value int32) Int32 {
- return Int32{field.div(value)}
-}
-
-// Mod ...
-func (field Int32) Mod(value int32) Int32 {
- return Int32{field.mod(value)}
-}
-
-// FloorDiv ...
-func (field Int32) FloorDiv(value int32) Int32 {
- return Int32{field.floorDiv(value)}
-}
-
-// RightShift ...
-func (field Int32) RightShift(value int32) Int32 {
- return Int32{field.rightShift(value)}
-}
-
-// LeftShift ...
-func (field Int32) LeftShift(value int32) Int32 {
- return Int32{field.leftShift(value)}
-}
-
-// BitXor ...
-func (field Int32) BitXor(value int32) Int32 {
- return Int32{field.bitXor(value)}
-}
-
-// BitAnd ...
-func (field Int32) BitAnd(value int32) Int32 {
- return Int32{field.bitAnd(value)}
-}
-
-// BitOr ...
-func (field Int32) BitOr(value int32) Int32 {
- return Int32{field.bitOr(value)}
-}
-
-// BitFlip ...
-func (field Int32) BitFlip() Int32 {
- return Int32{field.bitFlip()}
-}
-
-// Value set value
-func (field Int32) Value(value int32) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Int32) Zero() AssignExpr {
- return field.value(0)
-}
-
-// Sum ...
-func (field Int32) Sum() Int32 {
- return Int32{field.sum()}
-}
-
-// IfNull ...
-func (field Int32) IfNull(value int32) Expr {
- return field.ifNull(value)
-}
-
-func (field Int32) toSlice(values ...int32) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
-
-// Int64 int64 type field
-type Int64 Int
-
-// Eq equal to
-func (field Int64) Eq(value int64) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Int64) Neq(value int64) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Int64) Gt(value int64) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Int64) Gte(value int64) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Int64) Lt(value int64) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Int64) Lte(value int64) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Int64) In(values ...int64) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Int64) NotIn(values ...int64) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Between ...
-func (field Int64) Between(left int64, right int64) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Int64) NotBetween(left int64, right int64) Expr {
- return Not(field.Between(left, right))
-}
-
-// Like ...
-func (field Int64) Like(value int64) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Int64) NotLike(value int64) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Add ...
-func (field Int64) Add(value int64) Int64 {
- return Int64{field.add(value)}
-}
-
-// Sub ...
-func (field Int64) Sub(value int64) Int64 {
- return Int64{field.sub(value)}
-}
-
-// Mul ...
-func (field Int64) Mul(value int64) Int64 {
- return Int64{field.mul(value)}
-}
-
-// Div ...
-func (field Int64) Div(value int64) Int64 {
- return Int64{field.div(value)}
-}
-
-// Mod ...
-func (field Int64) Mod(value int64) Int64 {
- return Int64{field.mod(value)}
-}
-
-// FloorDiv ...
-func (field Int64) FloorDiv(value int64) Int64 {
- return Int64{field.floorDiv(value)}
-}
-
-// RightShift ...
-func (field Int64) RightShift(value int64) Int64 {
- return Int64{field.rightShift(value)}
-}
-
-// LeftShift ...
-func (field Int64) LeftShift(value int64) Int64 {
- return Int64{field.leftShift(value)}
-}
-
-// BitXor ...
-func (field Int64) BitXor(value int64) Int64 {
- return Int64{field.bitXor(value)}
-}
-
-// BitAnd ...
-func (field Int64) BitAnd(value int64) Int64 {
- return Int64{field.bitAnd(value)}
-}
-
-// BitOr ...
-func (field Int64) BitOr(value int64) Int64 {
- return Int64{field.bitOr(value)}
-}
-
-// BitFlip ...
-func (field Int64) BitFlip() Int64 {
- return Int64{field.bitFlip()}
-}
-
-// Value set value
-func (field Int64) Value(value int64) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Int64) Zero() AssignExpr {
- return field.value(0)
-}
-
-// Sum ...
-func (field Int64) Sum() Int64 {
- return Int64{field.sum()}
-}
-
-// IfNull ...
-func (field Int64) IfNull(value int64) Expr {
- return field.ifNull(value)
-}
-
-func (field Int64) toSlice(values ...int64) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
-
-// Uint uint type field
-type Uint Int
-
-// Eq equal to
-func (field Uint) Eq(value uint) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Uint) Neq(value uint) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Uint) Gt(value uint) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Uint) Gte(value uint) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Uint) Lt(value uint) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Uint) Lte(value uint) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Uint) In(values ...uint) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Uint) NotIn(values ...uint) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Between ...
-func (field Uint) Between(left uint, right uint) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Uint) NotBetween(left uint, right uint) Expr {
- return Not(field.Between(left, right))
-}
-
-// Like ...
-func (field Uint) Like(value uint) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Uint) NotLike(value uint) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Add ...
-func (field Uint) Add(value uint) Uint {
- return Uint{field.add(value)}
-}
-
-// Sub ...
-func (field Uint) Sub(value uint) Uint {
- return Uint{field.sub(value)}
-}
-
-// Mul ...
-func (field Uint) Mul(value uint) Uint {
- return Uint{field.mul(value)}
-}
-
-// Div ...
-func (field Uint) Div(value uint) Uint {
- return Uint{field.mul(value)}
-}
-
-// Mod ...
-func (field Uint) Mod(value uint) Uint {
- return Uint{field.mod(value)}
-}
-
-// FloorDiv ...
-func (field Uint) FloorDiv(value uint) Uint {
- return Uint{field.floorDiv(value)}
-}
-
-// RightShift ...
-func (field Uint) RightShift(value uint) Uint {
- return Uint{field.rightShift(value)}
-}
-
-// LeftShift ...
-func (field Uint) LeftShift(value uint) Uint {
- return Uint{field.leftShift(value)}
-}
-
-// BitXor ...
-func (field Uint) BitXor(value uint) Uint {
- return Uint{field.bitXor(value)}
-}
-
-// BitAnd ...
-func (field Uint) BitAnd(value uint) Uint {
- return Uint{field.bitAnd(value)}
-}
-
-// BitOr ...
-func (field Uint) BitOr(value uint) Uint {
- return Uint{field.bitOr(value)}
-}
-
-// BitFlip ...
-func (field Uint) BitFlip() Uint {
- return Uint{field.bitFlip()}
-}
-
-// Value set value
-func (field Uint) Value(value uint) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Uint) Zero() AssignExpr {
- return field.value(0)
-}
-
-// Sum ...
-func (field Uint) Sum() Uint {
- return Uint{field.sum()}
-}
-
-// IfNull ...
-func (field Uint) IfNull(value uint) Expr {
- return field.ifNull(value)
-}
-
-func (field Uint) toSlice(values ...uint) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
-
-// Uint8 uint8 type field
-type Uint8 Int
-
-// Eq equal to
-func (field Uint8) Eq(value uint8) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Uint8) Neq(value uint8) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Uint8) Gt(value uint8) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Uint8) Gte(value uint8) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Uint8) Lt(value uint8) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Uint8) Lte(value uint8) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Uint8) In(values ...uint8) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Uint8) NotIn(values ...uint8) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Between ...
-func (field Uint8) Between(left uint8, right uint8) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Uint8) NotBetween(left uint8, right uint8) Expr {
- return Not(field.Between(left, right))
-}
-
-// Like ...
-func (field Uint8) Like(value uint8) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Uint8) NotLike(value uint8) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Add ...
-func (field Uint8) Add(value uint8) Uint8 {
- return Uint8{field.add(value)}
-}
-
-// Sub ...
-func (field Uint8) Sub(value uint8) Uint8 {
- return Uint8{field.sub(value)}
-}
-
-// Mul ...
-func (field Uint8) Mul(value uint8) Uint8 {
- return Uint8{field.mul(value)}
-}
-
-// Div ...
-func (field Uint8) Div(value uint8) Uint8 {
- return Uint8{field.mul(value)}
-}
-
-// Mod ...
-func (field Uint8) Mod(value uint8) Uint8 {
- return Uint8{field.mod(value)}
-}
-
-// FloorDiv ...
-func (field Uint8) FloorDiv(value uint8) Uint8 {
- return Uint8{field.floorDiv(value)}
-}
-
-// RightShift ...
-func (field Uint8) RightShift(value uint8) Uint8 {
- return Uint8{field.rightShift(value)}
-}
-
-// LeftShift ...
-func (field Uint8) LeftShift(value uint8) Uint8 {
- return Uint8{field.leftShift(value)}
-}
-
-// BitXor ...
-func (field Uint8) BitXor(value uint8) Uint8 {
- return Uint8{field.bitXor(value)}
-}
-
-// BitAnd ...
-func (field Uint8) BitAnd(value uint8) Uint8 {
- return Uint8{field.bitAnd(value)}
-}
-
-// BitOr ...
-func (field Uint8) BitOr(value uint8) Uint8 {
- return Uint8{field.bitOr(value)}
-}
-
-// BitFlip ...
-func (field Uint8) BitFlip() Uint8 {
- return Uint8{field.bitFlip()}
-}
-
-// Value set value
-func (field Uint8) Value(value uint8) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Uint8) Zero() AssignExpr {
- return field.value(0)
-}
-
-// Sum ...
-func (field Uint8) Sum() Uint8 {
- return Uint8{field.sum()}
-}
-
-// IfNull ...
-func (field Uint8) IfNull(value uint8) Expr {
- return field.ifNull(value)
-}
-
-func (field Uint8) toSlice(values ...uint8) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
-
-// Uint16 uint16 type field
-type Uint16 Int
-
-// Eq equal to
-func (field Uint16) Eq(value uint16) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Uint16) Neq(value uint16) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Uint16) Gt(value uint16) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Uint16) Gte(value uint16) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Uint16) Lt(value uint16) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Uint16) Lte(value uint16) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Uint16) In(values ...uint16) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Uint16) NotIn(values ...uint16) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Between ...
-func (field Uint16) Between(left uint16, right uint16) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Uint16) NotBetween(left uint16, right uint16) Expr {
- return Not(field.Between(left, right))
-}
-
-// Like ...
-func (field Uint16) Like(value uint16) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Uint16) NotLike(value uint16) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Add ...
-func (field Uint16) Add(value uint16) Uint16 {
- return Uint16{field.add(value)}
-}
-
-// Sub ...
-func (field Uint16) Sub(value uint16) Uint16 {
- return Uint16{field.sub(value)}
-}
-
-// Mul ...
-func (field Uint16) Mul(value uint16) Uint16 {
- return Uint16{field.mul(value)}
-}
-
-// Div ...
-func (field Uint16) Div(value uint16) Uint16 {
- return Uint16{field.mul(value)}
-}
-
-// Mod ...
-func (field Uint16) Mod(value uint16) Uint16 {
- return Uint16{field.mod(value)}
-}
-
-// FloorDiv ...
-func (field Uint16) FloorDiv(value uint16) Uint16 {
- return Uint16{field.floorDiv(value)}
-}
-
-// RightShift ...
-func (field Uint16) RightShift(value uint16) Uint16 {
- return Uint16{field.rightShift(value)}
-}
-
-// LeftShift ...
-func (field Uint16) LeftShift(value uint16) Uint16 {
- return Uint16{field.leftShift(value)}
-}
-
-// BitXor ...
-func (field Uint16) BitXor(value uint16) Uint16 {
- return Uint16{field.bitXor(value)}
-}
-
-// BitAnd ...
-func (field Uint16) BitAnd(value uint16) Uint16 {
- return Uint16{field.bitAnd(value)}
-}
-
-// BitOr ...
-func (field Uint16) BitOr(value uint16) Uint16 {
- return Uint16{field.bitOr(value)}
-}
-
-// BitFlip ...
-func (field Uint16) BitFlip() Uint16 {
- return Uint16{field.bitFlip()}
-}
-
-// Value set value
-func (field Uint16) Value(value uint16) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Uint16) Zero() AssignExpr {
- return field.value(0)
-}
-
-// Sum ...
-func (field Uint16) Sum() Uint16 {
- return Uint16{field.sum()}
-}
-
-// IfNull ...
-func (field Uint16) IfNull(value uint16) Expr {
- return field.ifNull(value)
-}
-
-func (field Uint16) toSlice(values ...uint16) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
-
-// Uint32 uint32 type field
-type Uint32 Int
-
-// Eq equal to
-func (field Uint32) Eq(value uint32) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Uint32) Neq(value uint32) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Uint32) Gt(value uint32) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Uint32) Gte(value uint32) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Uint32) Lt(value uint32) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Uint32) Lte(value uint32) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Uint32) In(values ...uint32) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Uint32) NotIn(values ...uint32) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Between ...
-func (field Uint32) Between(left uint32, right uint32) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Uint32) NotBetween(left uint32, right uint32) Expr {
- return Not(field.Between(left, right))
-}
-
-// Like ...
-func (field Uint32) Like(value uint32) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Uint32) NotLike(value uint32) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Add ...
-func (field Uint32) Add(value uint32) Uint32 {
- return Uint32{field.add(value)}
-}
-
-// Sub ...
-func (field Uint32) Sub(value uint32) Uint32 {
- return Uint32{field.sub(value)}
-}
-
-// Mul ...
-func (field Uint32) Mul(value uint32) Uint32 {
- return Uint32{field.mul(value)}
-}
-
-// Div ...
-func (field Uint32) Div(value uint32) Uint32 {
- return Uint32{field.mul(value)}
-}
-
-// Mod ...
-func (field Uint32) Mod(value uint32) Uint32 {
- return Uint32{field.mod(value)}
-}
-
-// FloorDiv ...
-func (field Uint32) FloorDiv(value uint32) Uint32 {
- return Uint32{field.floorDiv(value)}
-}
-
-// RightShift ...
-func (field Uint32) RightShift(value uint32) Uint32 {
- return Uint32{field.rightShift(value)}
-}
-
-// LeftShift ...
-func (field Uint32) LeftShift(value uint32) Uint32 {
- return Uint32{field.leftShift(value)}
-}
-
-// BitXor ...
-func (field Uint32) BitXor(value uint32) Uint32 {
- return Uint32{field.bitXor(value)}
-}
-
-// BitAnd ...
-func (field Uint32) BitAnd(value uint32) Uint32 {
- return Uint32{field.bitAnd(value)}
-}
-
-// BitOr ...
-func (field Uint32) BitOr(value uint32) Uint32 {
- return Uint32{field.bitOr(value)}
-}
-
-// BitFlip ...
-func (field Uint32) BitFlip() Uint32 {
- return Uint32{field.bitFlip()}
-}
-
-// Value set value
-func (field Uint32) Value(value uint32) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Uint32) Zero() AssignExpr {
- return field.value(0)
-}
-
-// Sum ...
-func (field Uint32) Sum() Uint32 {
- return Uint32{field.sum()}
-}
-
-// IfNull ...
-func (field Uint32) IfNull(value uint32) Expr {
- return field.ifNull(value)
-}
-
-func (field Uint32) toSlice(values ...uint32) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
-
-// Uint64 uint64 type field
-type Uint64 Int
-
-// Eq equal to
-func (field Uint64) Eq(value uint64) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Uint64) Neq(value uint64) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Uint64) Gt(value uint64) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Uint64) Gte(value uint64) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Uint64) Lt(value uint64) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Uint64) Lte(value uint64) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// In ...
-func (field Uint64) In(values ...uint64) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Uint64) NotIn(values ...uint64) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Between ...
-func (field Uint64) Between(left uint64, right uint64) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Uint64) NotBetween(left uint64, right uint64) Expr {
- return Not(field.Between(left, right))
-}
-
-// Like ...
-func (field Uint64) Like(value uint64) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Uint64) NotLike(value uint64) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Add ...
-func (field Uint64) Add(value uint64) Uint64 {
- return Uint64{field.add(value)}
-}
-
-// Sub ...
-func (field Uint64) Sub(value uint64) Uint64 {
- return Uint64{field.sub(value)}
-}
-
-// Mul ...
-func (field Uint64) Mul(value uint64) Uint64 {
- return Uint64{field.mul(value)}
-}
-
-// Div ...
-func (field Uint64) Div(value uint64) Uint64 {
- return Uint64{field.mul(value)}
-}
-
-// Mod ...
-func (field Uint64) Mod(value uint64) Uint64 {
- return Uint64{field.mod(value)}
-}
-
-// FloorDiv ...
-func (field Uint64) FloorDiv(value uint64) Uint64 {
- return Uint64{field.floorDiv(value)}
-}
-
-// RightShift ...
-func (field Uint64) RightShift(value uint64) Uint64 {
- return Uint64{field.rightShift(value)}
-}
-
-// LeftShift ...
-func (field Uint64) LeftShift(value uint64) Uint64 {
- return Uint64{field.leftShift(value)}
-}
-
-// BitXor ...
-func (field Uint64) BitXor(value uint64) Uint64 {
- return Uint64{field.bitXor(value)}
-}
-
-// BitAnd ...
-func (field Uint64) BitAnd(value uint64) Uint64 {
- return Uint64{field.bitAnd(value)}
-}
-
-// BitOr ...
-func (field Uint64) BitOr(value uint64) Uint64 {
- return Uint64{field.bitOr(value)}
-}
-
-// BitFlip ...
-func (field Uint64) BitFlip() Uint64 {
- return Uint64{field.bitFlip()}
-}
-
-// Value set value
-func (field Uint64) Value(value uint64) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Uint64) Zero() AssignExpr {
- return field.value(0)
-}
-
-// Sum ...
-func (field Uint64) Sum() Uint64 {
- return Uint64{field.sum()}
-}
-
-// IfNull ...
-func (field Uint64) IfNull(value uint64) Expr {
- return field.ifNull(value)
-}
-
-func (field Uint64) toSlice(values ...uint64) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
diff --git a/toolkit/gormgen/field/serializer.go b/toolkit/gormgen/field/serializer.go
deleted file mode 100644
index f0900003..00000000
--- a/toolkit/gormgen/field/serializer.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package field
-
-import (
- "context"
- "reflect"
-
- "github.com/wubin1989/gorm/clause"
- "github.com/wubin1989/gorm/schema"
-
- "github.com/wubin1989/gorm"
-)
-
-type ValuerType struct {
- Column string
- Value schema.SerializerValuerInterface
- FucName string
-}
-
-func (v ValuerType) GormValue(ctx context.Context, db *gorm.DB) (expr clause.Expr) {
- stmt := db.Statement.Schema
- field := stmt.LookUpField(v.Column)
- newValue, err := v.Value.Value(context.WithValue(ctx, "func_name", v.FucName), field, reflect.ValueOf(v.Value), v.Value)
- db.AddError(err)
- return clause.Expr{SQL: "?", Vars: []interface{}{newValue}}
-}
-
-// Field2 a standard field struct
-type Serializer struct{ expr }
-
-// Eq judge equal
-func (field Serializer) Eq(value schema.SerializerValuerInterface) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: ValuerType{Column: field.ColumnName().String(), Value: value, FucName: "Eq"}}}
-}
-
-// Neq judge not equal
-func (field Serializer) Neq(value schema.SerializerValuerInterface) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: ValuerType{Column: field.ColumnName().String(), Value: value, FucName: "Neq"}}}
-}
-
-// In ...
-func (field Serializer) In(values ...schema.SerializerValuerInterface) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// Gt ...
-func (field Serializer) Gt(value schema.SerializerValuerInterface) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: ValuerType{Column: field.ColumnName().String(), Value: value, FucName: "Gt"}}}
-}
-
-// Gte ...
-func (field Serializer) Gte(value schema.SerializerValuerInterface) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: ValuerType{Column: field.ColumnName().String(), Value: value, FucName: "Gte"}}}
-}
-
-// Lt ...
-func (field Serializer) Lt(value schema.SerializerValuerInterface) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: ValuerType{Column: field.ColumnName().String(), Value: value, FucName: "Lt"}}}
-}
-
-// Lte ...
-func (field Serializer) Lte(value schema.SerializerValuerInterface) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: ValuerType{Column: field.ColumnName().String(), Value: value, FucName: "Lte"}}}
-}
-
-// Like ...
-func (field Serializer) Like(value schema.SerializerValuerInterface) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: ValuerType{Column: field.ColumnName().String(), Value: value, FucName: "Like"}}}
-}
-
-// Value ...
-func (field Serializer) Value(value schema.SerializerValuerInterface) AssignExpr {
- return field.value(ValuerType{Column: field.ColumnName().String(), Value: value, FucName: "Value"})
-}
-
-// Sum ...
-func (field Serializer) Sum() Field {
- return Field{field.sum()}
-}
-
-// IfNull ...
-func (field Serializer) IfNull(value schema.SerializerValuerInterface) Expr {
- return field.ifNull(ValuerType{Column: field.ColumnName().String(), Value: value, FucName: "IfNull"})
-}
-
-func (field Serializer) toSlice(values ...schema.SerializerValuerInterface) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = ValuerType{Column: field.ColumnName().String(), Value: v, FucName: "In"}
- }
- return slice
-}
diff --git a/toolkit/gormgen/field/string.go b/toolkit/gormgen/field/string.go
deleted file mode 100644
index 2cfe5d9e..00000000
--- a/toolkit/gormgen/field/string.go
+++ /dev/null
@@ -1,256 +0,0 @@
-package field
-
-import (
- "fmt"
-
- "github.com/wubin1989/gorm/clause"
-)
-
-// String string type field
-type String Field
-
-// Eq equal to
-func (field String) Eq(value string) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field String) Neq(value string) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field String) Gt(value string) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field String) Gte(value string) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field String) Lt(value string) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field String) Lte(value string) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// Between ...
-func (field String) Between(left string, right string) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field String) NotBetween(left string, right string) Expr {
- return Not(field.Between(left, right))
-}
-
-// In ...
-func (field String) In(values ...string) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values)}}
-}
-
-// NotIn ...
-func (field String) NotIn(values ...string) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Like ...
-func (field String) Like(value string) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field String) NotLike(value string) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Regexp ...
-func (field String) Regexp(value string) Expr {
- return field.regexp(value)
-}
-
-// NotRegxp ...
-func (field String) NotRegxp(value string) Expr {
- return expr{e: clause.Not(field.Regexp(value).expression())}
-}
-
-// Value ...
-func (field String) Value(value string) AssignExpr {
- return field.value(value)
-}
-
-// Zero ...
-func (field String) Zero() AssignExpr {
- return field.value("")
-}
-
-// IfNull ...
-func (field String) IfNull(value string) Expr {
- return field.ifNull(value)
-}
-
-// FindInSet equal to FIND_IN_SET(field_name, input_string_list)
-func (field String) FindInSet(targetList string) Expr {
- return expr{e: clause.Expr{SQL: "FIND_IN_SET(?,?)", Vars: []interface{}{field.RawExpr(), targetList}}}
-}
-
-// FindInSetWith equal to FIND_IN_SET(input_string, field_name)
-func (field String) FindInSetWith(target string) Expr {
- return expr{e: clause.Expr{SQL: "FIND_IN_SET(?,?)", Vars: []interface{}{target, field.RawExpr()}}}
-}
-
-// Replace ...
-func (field String) Replace(from, to string) String {
- return String{expr{e: clause.Expr{SQL: "REPLACE(?,?,?)", Vars: []interface{}{field.RawExpr(), from, to}}}}
-}
-
-// Concat ...
-func (field String) Concat(before, after string) String {
- switch {
- case before != "" && after != "":
- return String{expr{e: clause.Expr{SQL: "CONCAT(?,?,?)", Vars: []interface{}{before, field.RawExpr(), after}}}}
- case before != "":
- return String{expr{e: clause.Expr{SQL: "CONCAT(?,?)", Vars: []interface{}{before, field.RawExpr()}}}}
- case after != "":
- return String{expr{e: clause.Expr{SQL: "CONCAT(?,?)", Vars: []interface{}{field.RawExpr(), after}}}}
- default:
- return field
- }
-}
-
-// SubstringIndex SUBSTRING_INDEX
-// https://dev.mysql.com/doc/refman/8.0/en/functions.html#function_substring-index
-func (field String) SubstringIndex(delim string, count int) String {
- return String{expr{e: clause.Expr{
- SQL: fmt.Sprintf("SUBSTRING_INDEX(?,%q,%d)", delim, count),
- Vars: []interface{}{field.RawExpr()},
- }}}
-}
-
-func (field String) toSlice(values []string) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
-
-// Bytes []byte type field
-type Bytes String
-
-// Eq equal to
-func (field Bytes) Eq(value []byte) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Bytes) Neq(value []byte) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Bytes) Gt(value []byte) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Bytes) Gte(value []byte) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Bytes) Lt(value []byte) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Bytes) Lte(value []byte) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// Between ...
-func (field Bytes) Between(left []byte, right []byte) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Bytes) NotBetween(left []byte, right []byte) Expr {
- return Not(field.Between(left, right))
-}
-
-// In ...
-func (field Bytes) In(values ...[]byte) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values)}}
-}
-
-// NotIn ...
-func (field Bytes) NotIn(values ...[]byte) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Like ...
-func (field Bytes) Like(value string) Expr {
- return expr{e: clause.Like{Column: field.RawExpr(), Value: value}}
-}
-
-// NotLike ...
-func (field Bytes) NotLike(value string) Expr {
- return expr{e: clause.Not(field.Like(value).expression())}
-}
-
-// Regexp ...
-func (field Bytes) Regexp(value string) Expr {
- return field.regexp(value)
-}
-
-// NotRegxp ...
-func (field Bytes) NotRegxp(value string) Expr {
- return Not(field.Regexp(value))
-}
-
-// Value ...
-func (field Bytes) Value(value []byte) AssignExpr {
- return field.value(value)
-}
-
-// Zero ...
-func (field Bytes) Zero() AssignExpr {
- return field.value([]byte{})
-}
-
-// IfNull ...
-func (field Bytes) IfNull(value []byte) Expr {
- return field.ifNull(value)
-}
-
-// FindInSet FIND_IN_SET(field_name, input_string_list)
-func (field Bytes) FindInSet(targetList string) Expr {
- return expr{e: clause.Expr{SQL: "FIND_IN_SET(?,?)", Vars: []interface{}{field.RawExpr(), targetList}}}
-}
-
-// FindInSetWith FIND_IN_SET(input_string, field_name)
-func (field Bytes) FindInSetWith(target string) Expr {
- return expr{e: clause.Expr{SQL: "FIND_IN_SET(?,?)", Vars: []interface{}{target, field.RawExpr()}}}
-}
-
-// SubstringIndex SUBSTRING_INDEX
-// https://dev.mysql.com/doc/refman/8.0/en/functions.html#function_substring-index
-func (field Bytes) SubstringIndex(delim string, count int) Bytes {
- return Bytes{expr{e: clause.Expr{
- SQL: fmt.Sprintf("SUBSTRING_INDEX(?,%q,%d)", delim, count),
- Vars: []interface{}{field.RawExpr()},
- }}}
-}
-
-func (field Bytes) toSlice(values [][]byte) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
diff --git a/toolkit/gormgen/field/tag.go b/toolkit/gormgen/field/tag.go
deleted file mode 100644
index 0a1a3e45..00000000
--- a/toolkit/gormgen/field/tag.go
+++ /dev/null
@@ -1,159 +0,0 @@
-package field
-
-import (
- "sort"
- "strings"
-)
-
-const (
- TagKeyGorm = "gorm"
- TagKeyJson = "json"
-
- //gorm tag
- TagKeyGormColumn = "column"
- TagKeyGormType = "type"
- TagKeyGormPrimaryKey = "primaryKey"
- TagKeyGormAutoIncrement = "autoIncrement"
- TagKeyGormNotNull = "not null"
- TagKeyGormUniqueIndex = "uniqueIndex"
- TagKeyGormIndex = "index"
- TagKeyGormDefault = "default"
- TagKeyGormComment = "comment"
-)
-
-var (
- tagKeyPriorities = map[string]int16{
- TagKeyGorm: 100,
- TagKeyJson: 99,
-
- TagKeyGormColumn: 10,
- TagKeyGormType: 9,
- TagKeyGormPrimaryKey: 8,
- TagKeyGormAutoIncrement: 7,
- TagKeyGormNotNull: 6,
- TagKeyGormUniqueIndex: 5,
- TagKeyGormIndex: 4,
- TagKeyGormDefault: 3,
- TagKeyGormComment: 0,
- }
-)
-
-type TagBuilder interface {
- Build() string
-}
-
-type Tag map[string]string
-
-func (tag Tag) Set(key, value string) Tag {
- tag[key] = value
- return tag
-}
-
-func (tag Tag) Remove(key string) Tag {
- delete(tag, key)
- return tag
-}
-
-func (tag Tag) Build() string {
- if tag == nil || len(tag) == 0 {
- return ""
- }
-
- tags := make([]string, 0, len(tag))
- for _, k := range tagKeys(tag) {
- v := tag[k]
- if k == "" || v == "" {
- continue
- }
- tags = append(tags, k+":\""+v+"\"")
- }
- return strings.Join(tags, " ")
-}
-
-type GormTag map[string][]string
-
-func (tag GormTag) Append(key string, values ...string) GormTag {
- if _, ok := tag[key]; ok {
- tag[key] = append(tag[key], values...)
- } else {
- tag[key] = values
- }
- return tag
-}
-
-func (tag GormTag) Set(key string, values ...string) GormTag {
- tag[key] = values
- return tag
-}
-
-func (tag GormTag) Remove(key string) GormTag {
- delete(tag, key)
- return tag
-}
-
-func (tag GormTag) Build() string {
- if tag == nil || len(tag) == 0 {
- return ""
- }
- tags := make([]string, 0, len(tag))
- for _, k := range gormKeys(tag) {
- vs := tag[k]
- if len(vs) == 0 && k == "" {
- continue
- }
- if len(vs) == 0 {
- tags = append(tags, k)
- continue
- }
- for _, v := range vs {
- if k == "" && v == "" {
- continue
- }
- tv := make([]string, 0, 2)
- if k != "" {
- tv = append(tv, k)
- }
- if v != "" {
- tv = append(tv, v)
- }
- tags = append(tags, strings.Join(tv, ":"))
- }
- }
-
- return strings.Join(tags, ";")
-}
-
-func tagKeys(tag Tag) []string {
- keys := make([]string, 0, len(tag))
- if len(tag) == 0 {
- return keys
- }
- for k, _ := range tag {
- keys = append(keys, k)
- }
- return keySort(keys)
-}
-
-func gormKeys(tag GormTag) []string {
- keys := make([]string, 0, len(tag))
- if len(tag) == 0 {
- return keys
- }
- for k, _ := range tag {
- keys = append(keys, k)
- }
- return keySort(keys)
-}
-
-func keySort(keys []string) []string {
- if len(keys) == 0 {
- return keys
- }
- sort.Slice(keys, func(i, j int) bool {
- if tagKeyPriorities[keys[i]] == tagKeyPriorities[keys[j]] {
- return keys[i] <= keys[j]
- }
- return tagKeyPriorities[keys[i]] > tagKeyPriorities[keys[j]]
- })
- return keys
-}
diff --git a/toolkit/gormgen/field/time.go b/toolkit/gormgen/field/time.go
deleted file mode 100644
index 06062cc0..00000000
--- a/toolkit/gormgen/field/time.go
+++ /dev/null
@@ -1,198 +0,0 @@
-package field
-
-import (
- "fmt"
- "time"
-
- "github.com/wubin1989/gorm/clause"
-)
-
-// Time time type field
-type Time Field
-
-// Eq equal to
-func (field Time) Eq(value time.Time) Expr {
- return expr{e: clause.Eq{Column: field.RawExpr(), Value: value}}
-}
-
-// Neq not equal to
-func (field Time) Neq(value time.Time) Expr {
- return expr{e: clause.Neq{Column: field.RawExpr(), Value: value}}
-}
-
-// Gt greater than
-func (field Time) Gt(value time.Time) Expr {
- return expr{e: clause.Gt{Column: field.RawExpr(), Value: value}}
-}
-
-// Gte greater or equal to
-func (field Time) Gte(value time.Time) Expr {
- return expr{e: clause.Gte{Column: field.RawExpr(), Value: value}}
-}
-
-// Lt less than
-func (field Time) Lt(value time.Time) Expr {
- return expr{e: clause.Lt{Column: field.RawExpr(), Value: value}}
-}
-
-// Lte less or equal to
-func (field Time) Lte(value time.Time) Expr {
- return expr{e: clause.Lte{Column: field.RawExpr(), Value: value}}
-}
-
-// Between ...
-func (field Time) Between(left time.Time, right time.Time) Expr {
- return field.between([]interface{}{left, right})
-}
-
-// NotBetween ...
-func (field Time) NotBetween(left time.Time, right time.Time) Expr {
- return Not(field.Between(left, right))
-}
-
-// In ...
-func (field Time) In(values ...time.Time) Expr {
- return expr{e: clause.IN{Column: field.RawExpr(), Values: field.toSlice(values...)}}
-}
-
-// NotIn ...
-func (field Time) NotIn(values ...time.Time) Expr {
- return expr{e: clause.Not(field.In(values...).expression())}
-}
-
-// Add ...
-func (field Time) Add(value time.Duration) Time {
- return Time{field.add(value)}
-}
-
-// Sub ...
-func (field Time) Sub(value time.Duration) Time {
- return Time{field.sub(value)}
-}
-
-// Date convert to data, equal to "DATE(time_expr)"
-func (field Time) Date() Time {
- return Time{expr{e: clause.Expr{SQL: "DATE(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-// DateDiff equal to DATADIFF(self, value)
-func (field Time) DateDiff(value time.Time) Int {
- return Int{expr{e: clause.Expr{SQL: "DATEDIFF(?,?)", Vars: []interface{}{field.RawExpr(), value}}}}
-}
-
-// DateFormat equal to DATE_FORMAT(self, value)
-func (field Time) DateFormat(value string) String {
- return String{expr{e: clause.Expr{SQL: "DATE_FORMAT(?,?)", Vars: []interface{}{field.RawExpr(), value}}}}
-}
-
-// Now return result of NOW()
-func (field Time) Now() Time {
- return Time{expr{e: clause.Expr{SQL: "NOW()"}}}
-}
-
-// CurDate return result of CURDATE()
-func (field Time) CurDate() Time {
- return Time{expr{e: clause.Expr{SQL: "CURDATE()"}}}
-}
-
-// CurTime return result of CURTIME()
-func (field Time) CurTime() Time {
- return Time{expr{e: clause.Expr{SQL: "CURTIME()"}}}
-}
-
-// DayName equal to DAYNAME(self)
-func (field Time) DayName() String {
- return String{expr{e: clause.Expr{SQL: "DAYNAME(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-// MonthName equal to MONTHNAME(self)
-func (field Time) MonthName() String {
- return String{expr{e: clause.Expr{SQL: "MONTHNAME(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-func (field Time) Year() Int {
- return Int{expr{e: clause.Expr{SQL: "YEAR(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-// Month equal to MONTH(self)
-func (field Time) Month() Int {
- return Int{expr{e: clause.Expr{SQL: "MONTH(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-// Day equal to DAY(self)
-func (field Time) Day() Int {
- return Int{expr{e: clause.Expr{SQL: "DAY(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-// Hour equal to HOUR(self)
-func (field Time) Hour() Int {
- return Int{expr{e: clause.Expr{SQL: "HOUR(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-// Minute equal to MINUTE(self)
-func (field Time) Minute() Int {
- return Int{expr{e: clause.Expr{SQL: "MINUTE(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-// Second equal to SECOND(self)
-func (field Time) Second() Int {
- return Int{expr{e: clause.Expr{SQL: "SECOND(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-// MicroSecond equal to MICROSECOND(self)
-func (field Time) MicroSecond() Int {
- return Int{expr{e: clause.Expr{SQL: "MICROSECOND(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-// DayOfWeek equal to DAYOFWEEK(self)
-func (field Time) DayOfWeek() Int {
- return Int{expr{e: clause.Expr{SQL: "DAYOFWEEK(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-// DayOfMonth equal to DAYOFMONTH(self)
-func (field Time) DayOfMonth() Int {
- return Int{expr{e: clause.Expr{SQL: "DAYOFMONTH(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-// DayOfYear equal to DAYOFYEAR(self)
-func (field Time) DayOfYear() Int {
- return Int{expr{e: clause.Expr{SQL: "DAYOFYEAR(?)", Vars: []interface{}{field.RawExpr()}}}}
-}
-
-// FromDays equal to FROM_DAYS(self)
-func (field Time) FromDays(value int) Time {
- return Time{expr{e: clause.Expr{SQL: fmt.Sprintf("FROM_DAYS(%d)", value)}}}
-}
-
-// FromUnixtime equal to FROM_UNIXTIME(self)
-func (field Time) FromUnixtime(value int) Time {
- return Time{expr{e: clause.Expr{SQL: fmt.Sprintf("FROM_UNIXTIME(%d)", value)}}}
-}
-
-// Value set value
-func (field Time) Value(value time.Time) AssignExpr {
- return field.value(value)
-}
-
-// Zero set zero value
-func (field Time) Zero() AssignExpr {
- return field.value(time.Time{})
-}
-
-// Sum calc sum
-func (field Time) Sum() Time {
- return Time{field.sum()}
-}
-
-// IfNull ...
-func (field Time) IfNull(value time.Time) Expr {
- return field.ifNull(value)
-}
-
-func (field Time) toSlice(values ...time.Time) []interface{} {
- slice := make([]interface{}, len(values))
- for i, v := range values {
- slice[i] = v
- }
- return slice
-}
diff --git a/toolkit/gormgen/field_options.go b/toolkit/gormgen/field_options.go
deleted file mode 100644
index c4454090..00000000
--- a/toolkit/gormgen/field_options.go
+++ /dev/null
@@ -1,272 +0,0 @@
-package gormgen
-
-import (
- "reflect"
- "regexp"
- "strings"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/generate"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/model"
- "github.com/wubin1989/gorm/schema"
-)
-
-// ModelOpt field option
-type ModelOpt = model.Option
-
-var ns = schema.NamingStrategy{}
-
-var (
- // FieldNew add new field (any type your want)
- FieldNew = func(fieldName, fieldType string, fieldTag field.Tag) model.CreateFieldOpt {
- return func(*model.Field) *model.Field {
- return &model.Field{
- Name: fieldName,
- Type: fieldType,
- Tag: fieldTag,
- }
- }
- }
- // FieldIgnore ignore some columns by name
- FieldIgnore = func(columnNames ...string) model.FilterFieldOpt {
- return func(m *model.Field) *model.Field {
- for _, name := range columnNames {
- if m.ColumnName == name {
- return nil
- }
- }
- return m
- }
- }
- // FieldIgnoreReg ignore some columns by RegExp
- FieldIgnoreReg = func(columnNameRegs ...string) model.FilterFieldOpt {
- regs := make([]regexp.Regexp, len(columnNameRegs))
- for i, reg := range columnNameRegs {
- regs[i] = *regexp.MustCompile(reg)
- }
- return func(m *model.Field) *model.Field {
- for _, reg := range regs {
- if reg.MatchString(m.ColumnName) {
- return nil
- }
- }
- return m
- }
- }
- // FieldRename specify field name in generated struct
- FieldRename = func(columnName string, newName string) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- if m.ColumnName == columnName {
- m.Name = newName
- }
- return m
- }
- }
- // FieldComment specify field comment in generated struct
- FieldComment = func(columnName string, comment string) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- if m.ColumnName == columnName {
- m.ColumnComment = comment
- m.MultilineComment = strings.Contains(comment, "\n")
- }
- return m
- }
- }
- // FieldType specify field type in generated struct
- FieldType = func(columnName string, newType string) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- if m.ColumnName == columnName {
- m.Type = newType
- }
- return m
- }
- }
- // FieldTypeReg specify field type in generated struct by RegExp
- FieldTypeReg = func(columnNameReg string, newType string) model.ModifyFieldOpt {
- reg := regexp.MustCompile(columnNameReg)
- return func(m *model.Field) *model.Field {
- if reg.MatchString(m.ColumnName) {
- m.Type = newType
- }
- return m
- }
- }
- // FieldGenType specify field gen type in generated dao
- FieldGenType = func(columnName string, newType string) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- if m.ColumnName == columnName {
- m.CustomGenType = newType
- }
- return m
- }
- }
- // FieldGenTypeReg specify field gen type in generated dao by RegExp
- FieldGenTypeReg = func(columnNameReg string, newType string) model.ModifyFieldOpt {
- reg := regexp.MustCompile(columnNameReg)
- return func(m *model.Field) *model.Field {
- if reg.MatchString(m.ColumnName) {
- m.CustomGenType = newType
- }
- return m
- }
- }
- // FieldTag specify GORM tag and JSON tag
- FieldTag = func(columnName string, tagFunc func(tag field.Tag) field.Tag) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- if m.ColumnName == columnName {
- m.Tag = tagFunc(m.Tag)
- }
- return m
- }
- }
- // FieldJSONTag specify JSON tag
- FieldJSONTag = func(columnName string, jsonTag string) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- if m.ColumnName == columnName {
- m.Tag.Set(field.TagKeyJson, jsonTag)
- }
- return m
- }
- }
- // FieldJSONTagWithNS specify JSON tag with name strategy
- FieldJSONTagWithNS = func(schemaName func(columnName string) (tagContent string)) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- if schemaName != nil {
- m.Tag.Set(field.TagKeyJson, schemaName(m.ColumnName))
-
- }
- return m
- }
- }
- // FieldGORMTag specify GORM tag
- FieldGORMTag = func(columnName string, gormTag func(tag field.GormTag) field.GormTag) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- if columnName == "" {
- m.GORMTag = gormTag(m.GORMTag)
- return m
- }
- if m.ColumnName == columnName {
- m.GORMTag = gormTag(m.GORMTag)
- }
- return m
- }
- }
- // FieldGORMTagReg specify GORM tag by RegExp
- FieldGORMTagReg = func(columnNameReg string, gormTag func(tag field.GormTag) field.GormTag) model.ModifyFieldOpt {
- reg := regexp.MustCompile(columnNameReg)
- return func(m *model.Field) *model.Field {
- if reg.MatchString(m.ColumnName) {
- m.GORMTag = gormTag(m.GORMTag)
- }
- return m
- }
- }
- // FieldNewTag add new tag
- FieldNewTag = func(columnName string, newTag field.Tag) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- if m.ColumnName == columnName {
- for k, v := range newTag {
- m.Tag.Set(k, v)
- }
- }
- return m
- }
- }
- // FieldNewTagWithNS add new tag with name strategy
- FieldNewTagWithNS = func(tagName string, schemaName func(columnName string) string) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- if schemaName == nil {
- schemaName = func(name string) string { return name }
- }
- m.Tag.Set(tagName, schemaName(m.ColumnName))
- return m
- }
- }
- // FieldTrimPrefix trim column name's prefix
- FieldTrimPrefix = func(prefix string) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- m.Name = strings.TrimPrefix(m.Name, prefix)
- return m
- }
- }
- // FieldTrimSuffix trim column name's suffix
- FieldTrimSuffix = func(suffix string) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- m.Name = strings.TrimSuffix(m.Name, suffix)
- return m
- }
- }
- // FieldAddPrefix add prefix to struct's memeber name
- FieldAddPrefix = func(prefix string) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- m.Name = prefix + m.Name
- return m
- }
- }
- // FieldAddSuffix add suffix to struct's memeber name
- FieldAddSuffix = func(suffix string) model.ModifyFieldOpt {
- return func(m *model.Field) *model.Field {
- m.Name += suffix
- return m
- }
- }
- // FieldRelate relate to table in database
- FieldRelate = func(relationship field.RelationshipType, fieldName string, table *generate.QueryStructMeta, config *field.RelateConfig) model.CreateFieldOpt {
- if config == nil {
- config = &field.RelateConfig{}
- }
-
- return func(*model.Field) *model.Field {
- return &model.Field{
- Name: fieldName,
- Type: config.RelateFieldPrefix(relationship) + table.StructInfo.Type,
- Tag: config.GetTag(fieldName),
- GORMTag: config.GORMTag,
- Relation: field.NewRelationWithType(
- relationship, fieldName, table.StructInfo.Package+"."+table.StructInfo.Type,
- table.Relations()...),
- }
- }
- }
- // FieldRelateModel relate to exist table model
- FieldRelateModel = func(relationship field.RelationshipType, fieldName string, relModel interface{}, config *field.RelateConfig) model.CreateFieldOpt {
- st := reflect.TypeOf(relModel)
- if st.Kind() == reflect.Ptr {
- st = st.Elem()
- }
- fieldType := st.String()
-
- if config == nil {
- config = &field.RelateConfig{}
- }
-
- return func(*model.Field) *model.Field {
- return &model.Field{
- Name: fieldName,
- Type: config.RelateFieldPrefix(relationship) + fieldType,
- GORMTag: config.GORMTag,
- Tag: config.GetTag(fieldName),
- Relation: field.NewRelationWithModel(relationship, fieldName, fieldType, relModel),
- }
- }
- }
-
- // WithMethod add custom method for table model
- WithMethod = func(methods ...interface{}) model.AddMethodOpt {
- return func() []interface{} { return methods }
- }
-)
-
-var (
- DefaultMethodTableWithNamer = (&defaultModel{}).TableName
-)
-
-type defaultModel struct {
-}
-
-func (*defaultModel) TableName(namer schema.Namer) string {
- if namer == nil {
- return "@@table"
- }
- return namer.TableName("@@table")
-}
diff --git a/toolkit/gormgen/generator.go b/toolkit/gormgen/generator.go
deleted file mode 100644
index 4dd440f5..00000000
--- a/toolkit/gormgen/generator.go
+++ /dev/null
@@ -1,961 +0,0 @@
-package gormgen
-
-import (
- "bytes"
- "context"
- "database/sql"
- "fmt"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/fileutils"
- "io"
- "io/ioutil"
- "log"
- "os"
- "path/filepath"
- "runtime"
- "strconv"
- "strings"
- "text/template"
-
- "github.com/iancoleman/strcase"
- "github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- v3 "github.com/unionj-cloud/go-doudou/v2/toolkit/protobuf/v3"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/schema"
- "golang.org/x/tools/go/packages"
- "golang.org/x/tools/imports"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/helper"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/generate"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/model"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/parser"
- tmpl "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/template"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/utils/pools"
-)
-
-// T generic type
-type T interface{}
-
-// M map[string]interface{}
-type M map[string]interface{}
-
-// SQLResult sql.result
-type SQLResult sql.Result
-
-// SQLRow sql.Row
-type SQLRow sql.Row
-
-// SQLRows sql.Rows
-type SQLRows sql.Rows
-
-// RowsAffected execute affected raws
-type RowsAffected int64
-
-var concurrent = runtime.NumCPU()
-
-func init() { runtime.GOMAXPROCS(runtime.NumCPU()) }
-
-// NewGenerator create a new generator
-func NewGenerator(cfg Config) *Generator {
- if err := cfg.Revise(); err != nil {
- panic(fmt.Errorf("create generator fail: %w", err))
- }
-
- return &Generator{
- Config: cfg,
- Data: make(map[string]*genInfo),
- models: make(map[string]*generate.QueryStructMeta),
- }
-}
-
-// genInfo info about generated code
-type genInfo struct {
- *generate.QueryStructMeta
- Interfaces []*generate.InterfaceMethod
-}
-
-func (i *genInfo) appendMethods(methods []*generate.InterfaceMethod) {
- for _, newMethod := range methods {
- if i.methodInGenInfo(newMethod) {
- continue
- }
- i.Interfaces = append(i.Interfaces, newMethod)
- }
-}
-
-func (i *genInfo) methodInGenInfo(m *generate.InterfaceMethod) bool {
- for _, method := range i.Interfaces {
- if method.IsRepeatFromSameInterface(m) {
- return true
- }
- }
- return false
-}
-
-// Generator code generator
-type Generator struct {
- Config
-
- Data map[string]*genInfo //gen query data
- models map[string]*generate.QueryStructMeta //gen model data
-}
-
-// UseDB set db connection
-func (g *Generator) UseDB(db *gorm.DB) {
- if db != nil {
- g.db = db
- }
-}
-
-/*
-** The feature of mapping table from database server to Golang struct
-** Provided by @qqxhb
- */
-
-// GenerateModel catch table info from db, return a BaseStruct
-func (g *Generator) GenerateModel(tableName string, opts ...ModelOpt) *generate.QueryStructMeta {
- return g.GenerateModelAs(tableName, g.db.Config.NamingStrategy.SchemaName(tableName), opts...)
-}
-
-// GenerateModelAs catch table info from db, return a BaseStruct
-func (g *Generator) GenerateModelAs(tableName string, modelName string, opts ...ModelOpt) *generate.QueryStructMeta {
- meta, err := generate.GetQueryStructMeta(g.db, g.genModelConfig(tableName, modelName, opts))
- if err != nil {
- g.db.Logger.Error(context.Background(), "generate struct from table fail: %s", err)
- panic("generate struct fail")
- }
- if meta == nil {
- g.info(fmt.Sprintf("ignore table <%s>", tableName))
- return nil
- }
- g.models[meta.ModelStructName] = meta
-
- g.info(fmt.Sprintf("got %d columns from table <%s>", len(meta.Fields), meta.TableName))
- return meta
-}
-
-// GenerateAllTable generate all tables in db
-func (g *Generator) GenerateAllTable(opts ...ModelOpt) (tableModels []interface{}) {
- tableList, err := g.db.Migrator().GetTables()
- if err != nil {
- panic(fmt.Errorf("get all tables fail: %w", err))
- }
-
- g.info(fmt.Sprintf("find %d table from db: %s", len(tableList), tableList))
-
- tableModels = make([]interface{}, len(tableList))
- for i, tableName := range tableList {
- tableModels[i] = g.GenerateModel(tableName, opts...)
- }
- return tableModels
-}
-
-// GenerateFilteredTables generate all tables filterd by glob syntax in db
-func (g *Generator) GenerateFilteredTables(opts ...ModelOpt) (tableModels []interface{}) {
- tableList, err := g.db.Migrator().GetTables()
- if err != nil {
- panic(fmt.Errorf("get all tables fail: %w", err))
- }
- filteredTables := make([]string, 0)
- for _, item := range tableList {
- match := false
- if g.FilterTableGlob != nil && g.FilterTableGlob.Match(item) {
- match = true
- }
- if g.ExcludeTableGlob != nil && g.ExcludeTableGlob.Match(item) {
- match = false
- }
- if match {
- filteredTables = append(filteredTables, item)
- }
- }
- tableList = filteredTables
- g.info(fmt.Sprintf("find %d table from db: %s", len(tableList), tableList))
-
- tableModels = make([]interface{}, len(tableList))
- for i, tableName := range tableList {
- tableModels[i] = g.GenerateModel(tableName, opts...)
- }
- return tableModels
-}
-
-// GenerateModelFrom generate model from object
-func (g *Generator) GenerateModelFrom(obj helper.Object) *generate.QueryStructMeta {
- s, err := generate.GetQueryStructMetaFromObject(obj, g.genModelObjConfig())
- if err != nil {
- panic(fmt.Errorf("generate struct from object fail: %w", err))
- }
- g.models[s.ModelStructName] = s
-
- g.info(fmt.Sprintf("parse object %s", obj.StructName()))
- return s
-}
-
-func (g *Generator) genModelConfig(tableName string, modelName string, modelOpts []ModelOpt) *model.Config {
- if modelOpts == nil {
- modelOpts = g.modelOpts
- } else {
- modelOpts = append(modelOpts, g.modelOpts...)
- }
- return &model.Config{
- ModelPkg: g.Config.ModelPkgPath,
- TablePrefix: g.getTablePrefix(),
- TableName: tableName,
- ModelName: modelName,
- ImportPkgPaths: g.importPkgPaths,
- ModelOpts: modelOpts,
- NameStrategy: model.NameStrategy{
- SchemaNameOpts: g.dbNameOpts,
- TableNameNS: g.tableNameNS,
- ModelNameNS: g.modelNameNS,
- FileNameNS: g.fileNameNS,
- },
- FieldConfig: model.FieldConfig{
- DataTypeMap: g.dataTypeMap,
-
- FieldSignable: g.FieldSignable,
- FieldNullable: g.FieldNullable,
- FieldCoverable: g.FieldCoverable,
- FieldWithIndexTag: g.FieldWithIndexTag,
- FieldWithTypeTag: g.FieldWithTypeTag,
-
- FieldJSONTagNS: g.fieldJSONTagNS,
- },
- }
-}
-
-func (g *Generator) getTablePrefix() string {
- if ns, ok := g.db.NamingStrategy.(schema.NamingStrategy); ok {
- return ns.TablePrefix
- }
- return ""
-}
-
-func (g *Generator) genModelObjConfig() *model.Config {
- return &model.Config{
- ModelPkg: g.Config.ModelPkgPath,
- ImportPkgPaths: g.importPkgPaths,
- NameStrategy: model.NameStrategy{
- TableNameNS: g.tableNameNS,
- ModelNameNS: g.modelNameNS,
- FileNameNS: g.fileNameNS,
- },
- }
-}
-
-// ApplyBasic specify models which will implement basic .diy_method
-func (g *Generator) ApplyBasic(models ...interface{}) {
- g.ApplyInterface(func() {}, models...)
-}
-
-// ApplyInterface specifies .diy_method interfaces on structures, implment codes will be generated after calling g.Execute()
-// eg: g.ApplyInterface(func(model.Method){}, model.User{}, model.Company{})
-func (g *Generator) ApplyInterface(fc interface{}, models ...interface{}) {
- structs, err := generate.ConvertStructs(g.db, models...)
- if err != nil {
- g.db.Logger.Error(context.Background(), "check struct fail: %v", err)
- panic("check struct fail")
- }
- g.apply(fc, structs)
-}
-
-func (g *Generator) apply(fc interface{}, structs []*generate.QueryStructMeta) {
- interfacePaths, err := parser.GetInterfacePath(fc)
- if err != nil {
- g.db.Logger.Error(context.Background(), "get interface name or file fail: %s", err)
- panic("check interface fail")
- }
-
- readInterface := new(parser.InterfaceSet)
- err = readInterface.ParseFile(interfacePaths, generate.GetStructNames(structs))
- if err != nil {
- g.db.Logger.Error(context.Background(), "parser interface file fail: %s", err)
- panic("parser interface file fail")
- }
-
- for _, interfaceStructMeta := range structs {
- if g.judgeMode(WithoutContext) {
- interfaceStructMeta.ReviseFieldNameFor(model.GormKeywords)
- }
- interfaceStructMeta.ReviseFieldNameFor(model.DOKeywords)
-
- genInfo, err := g.pushQueryStructMeta(interfaceStructMeta)
- if err != nil {
- g.db.Logger.Error(context.Background(), "gen struct fail: %v", err)
- panic("gen struct fail")
- }
-
- functions, err := generate.BuildDIYMethod(readInterface, interfaceStructMeta, genInfo.Interfaces)
- if err != nil {
- g.db.Logger.Error(context.Background(), "check interface fail: %v", err)
- panic("check interface fail")
- }
- genInfo.appendMethods(functions)
- }
-}
-
-// Execute generate code to output path
-func (g *Generator) Execute() {
- g.info("Start generating orm related code.")
-
- if err := g.generateModelFile(); err != nil {
- g.db.Logger.Error(context.Background(), "generate model struct fail: %s", err)
- panic("generate model struct fail")
- }
-
- if err := g.generateQueryFile(); err != nil {
- g.db.Logger.Error(context.Background(), "generate query code fail: %s", err)
- panic("generate query code fail")
- }
-
- g.info("Generate orm related code done.")
-}
-
-// GenerateSvcGo ...
-func (g *Generator) GenerateSvcGo() {
- g.info("Start generating svc.go.")
-
- if err := g.generateSvcGo(); err != nil {
- g.db.Logger.Error(context.Background(), "generate svc.go fail: %s", err)
- return
- }
-
- g.info("Generate code svc.go done.")
-}
-
-// GenerateSvcImplGo ...
-func (g *Generator) GenerateSvcImplGo() {
- g.info("Start generating svcimpl.go.")
-
- if err := g.generateSvcImplGo(); err != nil {
- g.db.Logger.Error(context.Background(), "generate svcimpl.go fail: %s", err)
- panic("generate svcimpl.go fail")
- }
-
- g.info("Generate code svcimpl.go done.")
-}
-
-// GenerateSvcImplGrpc ...
-func (g *Generator) GenerateSvcImplGrpc(grpcService v3.Service) {
- g.info("Start generating grpc implementation code in svcimpl.go.")
-
- if err := g.generateSvcImplGrpc(grpcService); err != nil {
- g.db.Logger.Error(context.Background(), "generate grpc implementation code in svcimpl.go fail: %s", err)
- panic("generate grpc implementation code in svcimpl.go fail")
- }
-
- g.info("Generate grpc implementation code in svcimpl.go done.")
-}
-
-func (g *Generator) filterNewModels(meta astutils.InterfaceMeta) []*generate.QueryStructMeta {
- models := make([]*generate.QueryStructMeta, 0, len(g.models))
- for _, data := range g.models {
- if data == nil || !data.Generated {
- continue
- }
- targets := []string{
- fmt.Sprintf("PostGen%s", data.ModelStructName),
- fmt.Sprintf("GetGen%s", data.ModelStructName),
- fmt.Sprintf("PutGen%s", data.ModelStructName),
- fmt.Sprintf("DeleteGen%s", data.ModelStructName),
- fmt.Sprintf("GetGen%ss", data.ModelStructName),
- }
- count := 0
- for _, method := range meta.Methods {
- if sliceutils.StringContains(targets, method.Name) {
- count++
- }
- }
- if count == 0 {
- models = append(models, data)
- }
- }
- return models
-}
-
-func (g *Generator) filterNewModelsGrpc(meta astutils.InterfaceMeta) []*generate.QueryStructMeta {
- models := make([]*generate.QueryStructMeta, 0, len(g.models))
- for _, data := range g.models {
- if data == nil || !data.Generated {
- continue
- }
- targets := []string{
- fmt.Sprintf("PostGen%sRpc", data.ModelStructName),
- fmt.Sprintf("GetGen%sRpc", data.ModelStructName),
- fmt.Sprintf("PutGen%sRpc", data.ModelStructName),
- fmt.Sprintf("DeleteGen%sRpc", data.ModelStructName),
- fmt.Sprintf("GetGen%ssRpc", data.ModelStructName),
- }
- count := 0
- for _, method := range meta.Methods {
- if sliceutils.StringContains(targets, method.Name) {
- count++
- }
- }
- if count == 0 {
- models = append(models, data)
- }
- }
- return models
-}
-
-// generateSvcGo generate svc.go code and save to file
-func (g *Generator) generateSvcGo() error {
- if len(g.models) == 0 {
- return nil
- }
- svcFile := filepath.Join(g.RootDir, "svc.go")
- var err error
- if _, err = os.Stat(svcFile); err != nil {
- return errors.New("svc.go file is not found, maybe the project has not been initialized yet")
- }
- var interfaceMeta astutils.InterfaceMeta
- ic := astutils.BuildInterfaceCollector(svcFile, astutils.ExprString)
- if len(ic.Interfaces) > 0 {
- interfaceMeta = ic.Interfaces[0]
- }
- g.info("New content will be append to svc.go file")
- var f *os.File
- if f, err = os.OpenFile(svcFile, os.O_APPEND, os.ModePerm); err != nil {
- return errors.WithStack(err)
- }
- defer f.Close()
- models := g.filterNewModels(interfaceMeta)
- var buf bytes.Buffer
- for _, model := range models {
- if err = render(tmpl.AppendSvc, &buf, model); err != nil {
- return errors.WithStack(err)
- }
- }
- var original []byte
- if original, err = ioutil.ReadAll(f); err != nil {
- return errors.WithStack(err)
- }
- original = bytes.TrimSpace(original)
- last := bytes.LastIndexByte(original, '}')
- original = append(original[:last], buf.Bytes()...)
- original = append(original, '}')
- if err = g.outputWithOpt(svcFile, original, &imports.Options{
- TabWidth: 8,
- TabIndent: true,
- Comments: true,
- Fragment: true,
- }); err != nil {
- return errors.WithStack(err)
- }
- return nil
-}
-
-// generateSvcImplGo generate svcimpl.go code and save to file
-func (g *Generator) generateSvcImplGo() error {
- if len(g.models) == 0 {
- return nil
- }
- svcImplFile := filepath.Join(g.RootDir, "svcimpl.go")
- var err error
- if _, err = os.Stat(svcImplFile); err != nil {
- return errors.New("svcimpl.go file is not found, maybe the project has not been initialized yet")
- }
- var interfaceMeta astutils.InterfaceMeta
- interfaceMeta.Name = strcase.ToCamel(strcase.ToCamel(filepath.Base(g.RootDir)))
- sc := astutils.NewStructCollector(astutils.ExprString)
- astutils.CollectStructsInFolder(g.RootDir, sc)
- interfaceMeta.Methods = sc.Methods[interfaceMeta.Name+"Impl"]
- g.info("New content will be append to svcimpl.go file")
- var f *os.File
- if f, err = os.OpenFile(svcImplFile, os.O_APPEND, os.ModePerm); err != nil {
- return errors.WithStack(err)
- }
- defer f.Close()
- models := g.filterNewModelsGrpc(interfaceMeta)
- var buf bytes.Buffer
- for _, model := range models {
- if err = render(tmpl.AppendSvcImpl, &buf, struct {
- *generate.QueryStructMeta
- InterfaceName string
- }{
- QueryStructMeta: model,
- InterfaceName: interfaceMeta.Name,
- }); err != nil {
- return errors.WithStack(err)
- }
- }
- var original []byte
- if original, err = ioutil.ReadAll(f); err != nil {
- return errors.WithStack(err)
- }
- original = append(original, buf.Bytes()...)
-
- confPkg := g.GetPkgPath(filepath.Join(g.RootDir, "config"))
- dtoPkg := g.GetPkgPath(filepath.Join(g.RootDir, "dto"))
- queryPkg := g.GetPkgPath(g.OutPath)
- fileutils.CreateDirectory(filepath.Join(g.RootDir, "transport", "grpc"))
- transGrpcPkg := g.GetPkgPath(filepath.Join(g.RootDir, "transport", "grpc"))
-
- // fix import block
- var importBuf bytes.Buffer
- if err = render(tmpl.SvcImplImport, &importBuf, struct {
- ConfigPackage string
- DtoPackage string
- ModelPackage string
- QueryPackage string
- TransportGrpcPackage string
- }{
- ConfigPackage: confPkg,
- DtoPackage: dtoPkg,
- ModelPackage: g.modelPkgPath,
- QueryPackage: queryPkg,
- TransportGrpcPackage: transGrpcPkg,
- }); err != nil {
- return errors.WithStack(err)
- }
- original = astutils.AppendImportStatements(original, importBuf.Bytes())
-
- if err = g.outputWithOpt(svcImplFile, original, &imports.Options{
- TabWidth: 8,
- TabIndent: true,
- Comments: true,
- Fragment: true,
- }); err != nil {
- return errors.WithStack(err)
- }
- return nil
-}
-
-// info logger
-func (g *Generator) info(logInfos ...string) {
- for _, l := range logInfos {
- g.db.Logger.Info(context.Background(), l)
- log.Println(l)
- }
-}
-
-// generateQueryFile generate query code and save to file
-func (g *Generator) generateQueryFile() (err error) {
- if len(g.Data) == 0 {
- return nil
- }
-
- if err = os.MkdirAll(g.OutPath, os.ModePerm); err != nil {
- return fmt.Errorf("make dir outpath(%s) fail: %s", g.OutPath, err)
- }
-
- errChan := make(chan error)
- pool := pools.NewPool(concurrent)
- // generate query code for all struct
- for _, info := range g.Data {
- pool.Wait()
- go func(info *genInfo) {
- defer pool.Done()
- err := g.generateSingleQueryFile(info)
- if err != nil {
- errChan <- err
- }
-
- if g.WithUnitTest {
- err = g.generateQueryUnitTestFile(info)
- if err != nil { // do not panic
- g.db.Logger.Error(context.Background(), "generate unit test fail: %s", err)
- }
- }
- }(info)
- }
- select {
- case err = <-errChan:
- return errors.WithStack(err)
- case <-pool.AsyncWaitAll():
- }
-
- if !g.GenGenGo {
- return nil
- }
-
- importPkgs := importList.Add(g.importPkgPaths...).Paths()
-
- // generate query file
- var buf bytes.Buffer
- err = render(tmpl.Header, &buf, map[string]interface{}{
- "Package": g.queryPkgName,
- "ImportPkgPaths": importPkgs,
- })
- if err != nil {
- return errors.WithStack(err)
- }
-
- if g.judgeMode(WithDefaultQuery) {
- err = render(tmpl.DefaultQuery, &buf, g)
- if err != nil {
- return errors.WithStack(err)
- }
- }
- err = render(tmpl.QueryMethod, &buf, g)
- if err != nil {
- return errors.WithStack(err)
- }
-
- err = g.output(g.OutFile, buf.Bytes())
- if err != nil {
- return errors.WithStack(err)
- }
- g.info("generate query file: " + g.OutFile)
-
- // generate query unit test file
- if g.WithUnitTest {
- buf.Reset()
-
- err = render(tmpl.Header, &buf, map[string]interface{}{
- "Package": g.queryPkgName,
- "ImportPkgPaths": unitTestImportList.Add(g.importPkgPaths...).Paths(),
- })
- if err != nil {
- g.db.Logger.Error(context.Background(), "generate query unit test fail: %s", err)
- return nil
- }
- err = render(tmpl.DIYMethodTestBasic, &buf, nil)
- if err != nil {
- return errors.WithStack(err)
- }
- err = render(tmpl.QueryMethodTest, &buf, g)
- if err != nil {
- g.db.Logger.Error(context.Background(), "generate query unit test fail: %s", err)
- return nil
- }
- fileName := strings.TrimSuffix(g.OutFile, ".go") + "_test.go"
- err = g.output(fileName, buf.Bytes())
- if err != nil {
- g.db.Logger.Error(context.Background(), "generate query unit test fail: %s", err)
- return nil
- }
- g.info("generate unit test file: " + fileName)
- }
-
- return nil
-}
-
-// generateSingleQueryFile generate query code and save to file
-func (g *Generator) generateSingleQueryFile(data *genInfo) (err error) {
- var buf bytes.Buffer
-
- structPkgPath := data.StructInfo.PkgPath
- if structPkgPath == "" {
- structPkgPath = g.modelPkgPath
- }
-
- importPkgs := importList.Add(structPkgPath).Add(getImportPkgPaths(data)...).Paths()
- err = render(tmpl.Header, &buf, map[string]interface{}{
- "Package": g.queryPkgName,
- "ImportPkgPaths": importPkgs,
- })
- if err != nil {
- return errors.WithStack(err)
- }
- data.QueryStructMeta = data.QueryStructMeta.IfaceMode(g.judgeMode(WithQueryInterface))
-
- structTmpl := tmpl.TableQueryStructWithContext
- if g.judgeMode(WithoutContext) {
- structTmpl = tmpl.TableQueryStruct
- }
- err = render(structTmpl, &buf, data.QueryStructMeta)
- if err != nil {
- return errors.WithStack(err)
- }
-
- if g.judgeMode(WithQueryInterface) {
- err = render(tmpl.TableQueryIface, &buf, data)
- if err != nil {
- return errors.WithStack(err)
- }
- }
-
- for _, method := range data.Interfaces {
- err = render(tmpl.DIYMethod, &buf, method)
- if err != nil {
- return errors.WithStack(err)
- }
- }
-
- err = render(tmpl.CRUDMethod, &buf, data.QueryStructMeta)
- if err != nil {
- return errors.WithStack(err)
- }
-
- defer g.info(fmt.Sprintf("generate query file: %s%s%s.gen.go", g.OutPath, string(os.PathSeparator), data.FileName))
- return g.output(fmt.Sprintf("%s%s%s.gen.go", g.OutPath, string(os.PathSeparator), data.FileName), buf.Bytes())
-}
-
-// generateQueryUnitTestFile generate unit test file for query
-func (g *Generator) generateQueryUnitTestFile(data *genInfo) (err error) {
- var buf bytes.Buffer
-
- structPkgPath := data.StructInfo.PkgPath
- if structPkgPath == "" {
- structPkgPath = g.modelPkgPath
- }
- err = render(tmpl.Header, &buf, map[string]interface{}{
- "Package": g.queryPkgName,
- "ImportPkgPaths": unitTestImportList.Add(structPkgPath).Add(data.ImportPkgPaths...).Paths(),
- })
- if err != nil {
- return errors.WithStack(err)
- }
-
- err = render(tmpl.CRUDMethodTest, &buf, data.QueryStructMeta)
- if err != nil {
- return errors.WithStack(err)
- }
-
- for _, method := range data.Interfaces {
- err = render(tmpl.DIYMethodTest, &buf, method)
- if err != nil {
- return errors.WithStack(err)
- }
- }
-
- defer g.info(fmt.Sprintf("generate unit test file: %s%s%s.gen_test.go", g.OutPath, string(os.PathSeparator), data.FileName))
- return g.output(fmt.Sprintf("%s%s%s.gen_test.go", g.OutPath, string(os.PathSeparator), data.FileName), buf.Bytes())
-}
-
-// generateModelFile generate model structures and save to file
-func (g *Generator) generateModelFile() error {
- if len(g.models) == 0 {
- return nil
- }
-
- modelOutPath, err := g.getModelOutputPath()
- if err != nil {
- return errors.WithStack(err)
- }
-
- if err = os.MkdirAll(modelOutPath, os.ModePerm); err != nil {
- return fmt.Errorf("create model pkg path(%s) fail: %s", modelOutPath, err)
- }
-
- errChan := make(chan error)
- pool := pools.NewPool(concurrent)
- for _, data := range g.models {
- if data == nil || !data.Generated {
- continue
- }
- pool.Wait()
- go func(data *generate.QueryStructMeta) {
- defer pool.Done()
-
- var buf bytes.Buffer
- funcMap := make(map[string]interface{})
- funcMap["convert"] = func(s string) string {
- return s
- }
- funcMap["contains"] = strings.Contains
- t, err := template.New(tmpl.Model).Funcs(funcMap).Parse(tmpl.Model)
- if err != nil {
- errChan <- err
- return
- }
- err = t.Execute(&buf, struct {
- *generate.QueryStructMeta
- ConfigPackage string
- }{
- QueryStructMeta: data,
- ConfigPackage: g.ConfigPackage,
- })
- if err != nil {
- errChan <- err
- return
- }
-
- for _, method := range data.ModelMethods {
- err = render(tmpl.ModelMethod, &buf, method)
- if err != nil {
- errChan <- err
- return
- }
- }
-
- modelFile := modelOutPath + data.FileName + ".gen.go"
- err = g.output(modelFile, buf.Bytes())
- if err != nil {
- errChan <- err
- return
- }
-
- g.info(fmt.Sprintf("generate model file(table <%s> -> {%s.%s}): %s", data.TableName, data.StructInfo.Package, data.StructInfo.Type, modelFile))
- }(data)
- }
- select {
- case err = <-errChan:
- return errors.WithStack(err)
- case <-pool.AsyncWaitAll():
- g.fillModelPkgPath(modelOutPath)
- }
- return nil
-}
-
-func (g *Generator) getModelOutputPath() (outPath string, err error) {
- if strings.Contains(g.ModelPkgPath, string(os.PathSeparator)) {
- outPath, err = filepath.Abs(g.ModelPkgPath)
- if err != nil {
- return "", fmt.Errorf("cannot parse model pkg path: %w", err)
- }
- } else {
- outPath = filepath.Join(filepath.Dir(g.OutPath), g.ModelPkgPath)
- }
- return outPath + string(os.PathSeparator), nil
-}
-
-func (g *Generator) fillModelPkgPath(filePath string) {
- pkgs, err := packages.Load(&packages.Config{
- Mode: packages.NeedName,
- Dir: filePath,
- })
- if err != nil {
- g.db.Logger.Warn(context.Background(), "parse model pkg path fail: %s", err)
- return
- }
- if len(pkgs) == 0 {
- g.db.Logger.Warn(context.Background(), "parse model pkg path fail: got 0 packages")
- return
- }
- g.Config.modelPkgPath = pkgs[0].PkgPath
-}
-
-// output format and output
-func (g *Generator) output(fileName string, content []byte) error {
- return g.outputWithOpt(fileName, content, nil)
-}
-
-// output format and output
-func (g *Generator) outputWithOpt(fileName string, content []byte, opt *imports.Options) error {
- result, err := imports.Process(fileName, content, opt)
- if err != nil {
- lines := strings.Split(string(content), "\n")
- errLine, _ := strconv.Atoi(strings.Split(err.Error(), ":")[1])
- startLine, endLine := errLine-5, errLine+5
- fmt.Println("Format fail:", errLine, err)
- if startLine < 0 {
- startLine = 0
- }
- if endLine > len(lines)-1 {
- endLine = len(lines) - 1
- }
- for i := startLine; i <= endLine; i++ {
- fmt.Println(i, lines[i])
- }
- return fmt.Errorf("cannot format file: %w", err)
- }
- return ioutil.WriteFile(fileName, result, 0640)
-}
-
-func (g *Generator) pushQueryStructMeta(meta *generate.QueryStructMeta) (*genInfo, error) {
- structName := meta.ModelStructName
- if g.Data[structName] == nil {
- g.Data[structName] = &genInfo{QueryStructMeta: meta}
- }
- if g.Data[structName].Source != meta.Source {
- return nil, fmt.Errorf("cannot generate struct with the same name from different source:%s.%s and %s.%s",
- meta.StructInfo.Package, meta.ModelStructName, g.Data[structName].StructInfo.Package, g.Data[structName].ModelStructName)
- }
- return g.Data[structName], nil
-}
-
-func render(tmpl string, wr io.Writer, data interface{}) error {
- t, err := template.New(tmpl).Parse(tmpl)
- if err != nil {
- return errors.WithStack(err)
- }
- return t.Execute(wr, data)
-}
-
-func getImportPkgPaths(data *genInfo) []string {
- importPathMap := make(map[string]struct{})
- for _, path := range data.ImportPkgPaths {
- importPathMap[path] = struct{}{}
- }
- // imports.Process (called in Generator.output) will guess missing imports, and will be
- // much faster if import path is already specified. So add all imports from DIY interface package.
- for _, method := range data.Interfaces {
- for _, param := range method.Params {
- importPathMap[param.PkgPath] = struct{}{}
- }
- }
- importPkgPaths := make([]string, 0, len(importPathMap))
- for importPath := range importPathMap {
- importPkgPaths = append(importPkgPaths, importPath)
- }
- return importPkgPaths
-}
-
-func (g *Generator) GetPkgPath(filePath string) string {
- pkgs, err := packages.Load(&packages.Config{
- Mode: packages.NeedName,
- Dir: filePath,
- })
- if err != nil {
- g.db.Logger.Warn(context.Background(), "parse pkg path fail: %s", err)
- return ""
- }
- if len(pkgs) == 0 {
- g.db.Logger.Warn(context.Background(), "parse pkg path fail: got 0 packages")
- return ""
- }
- return pkgs[0].PkgPath
-}
-
-func (g *Generator) generateSvcImplGrpc(grpcService v3.Service) error {
- if len(g.models) == 0 {
- return nil
- }
- svcImplFile := filepath.Join(g.RootDir, "svcimpl.go")
- _, err := os.Stat(svcImplFile)
- if err != nil {
- return errors.WithStack(err)
- }
- var interfaceMeta astutils.InterfaceMeta
- interfaceMeta.Name = strcase.ToCamel(strcase.ToCamel(filepath.Base(g.RootDir)))
- sc := astutils.NewStructCollector(astutils.ExprString)
- astutils.CollectStructsInFolder(g.RootDir, sc)
- interfaceMeta.Methods = sc.Methods[interfaceMeta.Name+"Impl"]
- g.info("New content will be append to svcimpl.go file")
- models := g.filterNewModelsGrpc(interfaceMeta)
- var buf bytes.Buffer
- for _, model := range models {
- if err = render(tmpl.AppendSvcImplGrpc, &buf, struct {
- *generate.QueryStructMeta
- InterfaceName string
- }{
- QueryStructMeta: model,
- InterfaceName: interfaceMeta.Name,
- }); err != nil {
- return errors.WithStack(err)
- }
- }
- var original []byte
- if original, err = ioutil.ReadFile(svcImplFile); err != nil {
- return errors.WithStack(err)
- }
- original = append(original, buf.Bytes()...)
-
- transGrpcPkg := g.GetPkgPath(filepath.Join(g.RootDir, "transport", "grpc"))
-
- // fix import block
- var importBuf bytes.Buffer
- if err = render(tmpl.SvcImplImportGrpc, &importBuf, struct {
- TransportGrpcPackage string
- }{
- TransportGrpcPackage: transGrpcPkg,
- }); err != nil {
- return errors.WithStack(err)
- }
- original = astutils.AppendImportStatements(original, importBuf.Bytes())
- original = astutils.GrpcRelatedModify(original, interfaceMeta.Name, grpcService.Name)
- if err = g.outputWithOpt(svcImplFile, original, &imports.Options{
- TabWidth: 8,
- TabIndent: true,
- Comments: true,
- Fragment: true,
- }); err != nil {
- return errors.WithStack(err)
- }
- return nil
-}
diff --git a/toolkit/gormgen/generator_test.go b/toolkit/gormgen/generator_test.go
deleted file mode 100644
index a86a94e4..00000000
--- a/toolkit/gormgen/generator_test.go
+++ /dev/null
@@ -1,312 +0,0 @@
-package gormgen
-
-import (
- "context"
- "testing"
- "time"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/callbacks"
- "github.com/wubin1989/gorm/clause"
- "github.com/wubin1989/gorm/schema"
- "github.com/wubin1989/gorm/utils/tests"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
-)
-
-func TestConfig(t *testing.T) {
- _ = &Config{
- db: nil,
-
- OutPath: "path",
- OutFile: "",
-
- ModelPkgPath: "models",
-
- queryPkgName: "query",
- }
-}
-
-// test data
-type mysqlDialectors struct{ tests.DummyDialector }
-
-func (mysqlDialectors) Name() string {
- return "mysql"
-}
-
-var db, _ = gorm.Open(mysqlDialectors{}, nil)
-
-func init() {
- db = db.Debug()
-
- callbacks.RegisterDefaultCallbacks(db, &callbacks.Config{
- UpdateClauses: []string{"UPDATE", "SET", "WHERE", "ORDER BY", "LIMIT"},
- DeleteClauses: []string{"DELETE", "FROM", "WHERE", "ORDER BY", "LIMIT"},
- })
-}
-
-// User user data struct
-type User struct {
- ID uint `gorm:"primary_key"`
- Name string
- Age int
- Score float64
- Address string
- Famous bool
- RegisterAt time.Time
-}
-
-func (User) TableName() string {
- return "users_info"
-}
-
-// StudentRaw student data struct
-type StudentRaw struct {
- ID int64 `gorm:"primary_key"`
- Name string
- Age int
- Instructor int64 //导师
-}
-
-func (StudentRaw) TableName() string {
- return "student"
-}
-
-// Teacher teacher data struct
-type TeacherRaw struct {
- ID int64 `gorm:"primary_key"`
- Name string
-}
-
-func (TeacherRaw) TableName() string {
- return "teacher"
-}
-
-type user struct {
- userDo
-
- ALL field.Asterisk
- ID field.Uint
- Name field.String
- Age field.Int
- Score field.Float64
- Address field.String
- Famous field.Bool
- RegisterAt field.Time
-}
-
-type userDo struct{ DO }
-
-func (u userDo) Debug() *userDo {
- u.DO = *u.DO.Debug().(*DO)
- return &u
-}
-
-func (u userDo) WithContext(ctx context.Context) *userDo {
- u.DO = *u.DO.WithContext(ctx).(*DO)
- return &u
-}
-
-func (u userDo) Clauses(conds ...clause.Expression) *userDo {
- u.DO = *u.DO.Clauses(conds...).(*DO)
- return &u
-}
-
-func (u userDo) Not(conds ...Condition) *userDo {
- u.DO = *u.DO.Not(conds...).(*DO)
- return &u
-}
-
-func (u userDo) Or(conds ...Condition) *userDo {
- u.DO = *u.DO.Or(conds...).(*DO)
- return &u
-}
-
-func (u userDo) Select(conds ...field.Expr) *userDo {
- u.DO = *u.DO.Select(conds...).(*DO)
- return &u
-}
-
-func (u userDo) Where(conds ...Condition) *userDo {
- u.DO = *u.DO.Where(conds...).(*DO)
- return &u
-}
-
-func (u userDo) Order(conds ...field.Expr) *userDo {
- u.DO = *u.DO.Order(conds...).(*DO)
- return &u
-}
-
-func (u userDo) Distinct(cols ...field.Expr) *userDo {
- u.DO = *u.DO.Distinct(cols...).(*DO)
- return &u
-}
-
-func (u userDo) Omit(cols ...field.Expr) *userDo {
- u.DO = *u.DO.Omit(cols...).(*DO)
- return &u
-}
-
-func (u userDo) Join(table schema.Tabler, on ...field.Expr) *userDo {
- u.DO = *u.DO.Join(table, on...).(*DO)
- return &u
-}
-
-func (u userDo) LeftJoin(table schema.Tabler, on ...field.Expr) *userDo {
- u.DO = *u.DO.LeftJoin(table, on...).(*DO)
- return &u
-}
-
-func (u userDo) RightJoin(table schema.Tabler, on ...field.Expr) *userDo {
- u.DO = *u.DO.RightJoin(table, on...).(*DO)
- return &u
-}
-
-func (u userDo) Group(col field.Expr) *userDo {
- u.DO = *u.DO.Group(col).(*DO)
- return &u
-}
-
-func (u userDo) Having(conds ...Condition) *userDo {
- u.DO = *u.DO.Having(conds...).(*DO)
- return &u
-}
-
-func (u userDo) Limit(limit int) *userDo {
- u.DO = *u.DO.Limit(limit).(*DO)
- return &u
-}
-
-func (u userDo) Offset(offset int) *userDo {
- u.DO = *u.DO.Offset(offset).(*DO)
- return &u
-}
-
-func (u userDo) Scopes(funcs ...func(Dao) Dao) *userDo {
- u.DO = *u.DO.Scopes(funcs...).(*DO)
- return &u
-}
-
-func (u userDo) Unscoped() *userDo {
- u.DO = *u.DO.Unscoped().(*DO)
- return &u
-}
-
-func (u userDo) Create(values ...*user) error {
- if len(values) == 0 {
- return nil
- }
- return u.DO.Create(values)
-}
-
-func (u userDo) CreateInBatches(values []*user, batchSize int) error {
- return u.DO.CreateInBatches(values, batchSize)
-}
-
-func (u userDo) Save(values ...*user) error {
- if len(values) == 0 {
- return nil
- }
- return u.DO.Save(values)
-}
-
-func (u userDo) First() (*user, error) {
- if result, err := u.DO.First(); err != nil {
- return nil, err
- } else {
- return result.(*user), nil
- }
-}
-
-func (u userDo) Take() (*user, error) {
- if result, err := u.DO.Take(); err != nil {
- return nil, err
- } else {
- return result.(*user), nil
- }
-}
-
-func (u userDo) Last() (*user, error) {
- if result, err := u.DO.Last(); err != nil {
- return nil, err
- } else {
- return result.(*user), nil
- }
-}
-
-func (u userDo) Find() ([]*user, error) {
- result, err := u.DO.Find()
- return result.([]*user), err
-}
-
-func (u userDo) FindInBatches(result []*user, batchSize int, fc func(tx Dao, batch int) error) error {
- return u.DO.FindInBatches(&result, batchSize, fc)
-}
-
-func (u userDo) FindByPage(offset int, limit int) (result []*user, count int64, err error) {
- count, err = u.Count()
- if err != nil {
- return
- }
-
- result, err = u.Offset(offset).Limit(limit).Find()
- return
-}
-
-var u = func() *user {
- u := user{
- ALL: field.NewAsterisk(""),
- ID: field.NewUint("", "id"),
- Name: field.NewString("", "name"),
- Age: field.NewInt("", "age"),
- Score: field.NewFloat64("", "score"),
- Address: field.NewString("", "address"),
- Famous: field.NewBool("", "famous"),
- RegisterAt: field.NewTime("", "register_at"),
- }
- u.UseDB(db.Session(&gorm.Session{Context: context.Background(), DryRun: true}))
- u.UseModel(User{})
- return &u
-}()
-
-type Student struct {
- DO
-
- ALL field.Asterisk
- ID field.Int64
- Name field.String
- Age field.Int
- Instructor field.Int64
-}
-
-var student = func() *Student {
- s := Student{
- ALL: field.NewAsterisk("student"),
- ID: field.NewInt64("student", "id"),
- Name: field.NewString("student", "name"),
- Age: field.NewInt("student", "age"),
- Instructor: field.NewInt64("student", "instructor"),
- }
- s.UseDB(db.Session(&gorm.Session{Context: context.Background(), DryRun: true}))
- s.UseModel(StudentRaw{})
- return &s
-}()
-
-type Teacher struct {
- DO
-
- ALL field.Asterisk
- ID field.Int64
- Name field.String
-}
-
-var teacher = func() Teacher {
- t := Teacher{
- ALL: field.NewAsterisk("teacher"),
- ID: field.NewInt64("teacher", "id"),
- Name: field.NewString("teacher", "name"),
- }
- t.UseDB(db.Session(&gorm.Session{Context: context.Background(), DryRun: true}))
- t.UseModel(TeacherRaw{})
- return t
-}()
diff --git a/toolkit/gormgen/helper/clause.go b/toolkit/gormgen/helper/clause.go
deleted file mode 100644
index 2a603158..00000000
--- a/toolkit/gormgen/helper/clause.go
+++ /dev/null
@@ -1,177 +0,0 @@
-package helper
-
-import (
- "strings"
-
- "github.com/wubin1989/gorm/clause"
-)
-
-// Cond ...
-type Cond struct {
- Cond bool
- Result string
-}
-
-// IfClause if clause
-func IfClause(conds []Cond) string {
- judge := func(c Cond) string {
- if c.Cond {
- return c.Result
- }
- return ""
- }
-
- clauses := make([]string, len(conds))
- for i, cond := range conds {
- clauses[i] = strings.Trim(judge(cond), " ")
- }
- return " " + strings.Join(clauses, " ")
-}
-
-// WhereClause where clause
-func WhereClause(conds []string) string {
- return joinClause(conds, "WHERE", whereValue, " ")
-}
-
-// SetClause set clause
-func SetClause(conds []string) string {
- return joinClause(conds, "SET", setValue, ",")
-}
-
-func joinClause(conds []string, keyword string, deal func(string) string, sep string) string {
- clauses := make([]string, len(conds))
- for i, clause := range conds {
- clauses[i] = deal(clause)
- }
-
- sql := trimAll(strings.Join(clauses, sep))
- if sql != "" {
- sql = " " + keyword + " " + sql
- }
- return sql
-}
-
-func trimAll(input string) string {
- return trimRight(trimLeft(input))
-}
-
-func trimLeft(input string) string {
- input = strings.TrimSpace(input)
- lowercase := strings.ToLower(input)
- switch {
- case strings.HasPrefix(lowercase, "and "):
- return input[4:]
- case strings.HasPrefix(lowercase, "or "):
- return input[3:]
- case strings.HasPrefix(lowercase, "xor "):
- return input[4:]
- case strings.HasPrefix(lowercase, ","):
- return input[1:]
- default:
- return input
- }
-}
-func trimRight(input string) string {
- input = strings.TrimSpace(input)
- lowercase := strings.ToLower(input)
- switch {
- case strings.HasSuffix(lowercase, " and"):
- return input[:len(input)-3]
- case strings.HasSuffix(lowercase, " or"):
- return input[:len(input)-2]
- case strings.HasSuffix(lowercase, " xor"):
- return input[:len(input)-3]
- case strings.HasSuffix(lowercase, ","):
- return input[:len(input)-1]
- default:
- return input
- }
-}
-
-// whereValue append a new condition with prefix "AND"
-func whereValue(value string) string {
- value = strings.Trim(value, " ")
- lowercase := strings.ToLower(value)
- switch {
- case lowercase == "":
- return ""
- case strings.HasPrefix(lowercase, "and "):
- return value
- case strings.HasPrefix(lowercase, "or "):
- return value
- case strings.HasPrefix(lowercase, "xor "):
- return value
- default:
- return "AND " + value
- }
-}
-
-func setValue(value string) string {
- return strings.Trim(value, ", ")
-}
-
-// JoinWhereBuilder join where builder
-func JoinWhereBuilder(src *strings.Builder, whereValue strings.Builder) {
- value := trimAll(whereValue.String())
- if value != "" {
- src.WriteString("WHERE ")
- src.WriteString(value)
- src.WriteString(" ")
- }
-}
-
-// JoinSetBuilder join set builder
-func JoinSetBuilder(src *strings.Builder, setValue strings.Builder) {
- value := trimAll(setValue.String())
- if value != "" {
- src.WriteString("SET ")
- src.WriteString(value)
- src.WriteString(" ")
- }
-}
-
-// JoinTrimAllBuilder join trim builder, will trim and,or,xor, `,`
-func JoinTrimAllBuilder(src *strings.Builder, setValue strings.Builder) {
- src.WriteString(trimAll(setValue.String()))
- src.WriteString(" ")
-}
-
-// JoinTblExpr join clause with table expression(sub query...)
-type JoinTblExpr struct {
- clause.Join
- TableExpr clause.Expression
-}
-
-// NewJoinTblExpr create new join table expr
-func NewJoinTblExpr(join clause.Join, tbExpr clause.Expression) JoinTblExpr {
- return JoinTblExpr{Join: join, TableExpr: tbExpr}
-}
-
-// Build ...
-func (join JoinTblExpr) Build(builder clause.Builder) {
- if builder == nil {
- return
- }
- if join.Type != "" {
- _, _ = builder.WriteString(string(join.Type))
- _ = builder.WriteByte(' ')
- }
- _, _ = builder.WriteString("JOIN ")
- if join.TableExpr != nil {
- join.TableExpr.Build(builder)
- }
-
- if len(join.ON.Exprs) > 0 {
- _, _ = builder.WriteString(" ON ")
- join.ON.Build(builder)
- } else if len(join.Using) > 0 {
- _, _ = builder.WriteString(" USING (")
- for idx, c := range join.Using {
- if idx > 0 {
- _ = builder.WriteByte(',')
- }
- builder.WriteQuoted(c)
- }
- _ = builder.WriteByte(')')
- }
-}
diff --git a/toolkit/gormgen/helper/object.go b/toolkit/gormgen/helper/object.go
deleted file mode 100644
index dbe0d703..00000000
--- a/toolkit/gormgen/helper/object.go
+++ /dev/null
@@ -1,60 +0,0 @@
-package helper
-
-import (
- "errors"
- "fmt"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
-)
-
-// Object an object interface
-type Object interface {
- // TableName return table name
- TableName() string
- // StructName return struct name
- StructName() string
- // FileName return field name
- FileName() string
- // ImportPkgPaths return need import package path
- ImportPkgPaths() []string
-
- // Fields return field array
- Fields() []Field
-}
-
-// Field a field interface
-type Field interface {
- // Name return field name
- Name() string
- // Type return field type
- Type() string
-
- // ColumnName return column name
- ColumnName() string
- // GORMTag return gorm tag
- GORMTag() string
- // JSONTag return json tag
- JSONTag() string
- // Tag return field tag
- Tag() field.Tag
-
- // Comment return comment
- Comment() string
-}
-
-// CheckObject check ojbect
-func CheckObject(obj Object) error {
- if obj.StructName() == "" {
- return errors.New("Object's StructName() cannot be empty")
- }
-
- for _, field := range obj.Fields() {
- switch "" {
- case field.Name():
- return fmt.Errorf("Object %s's Field.Name() cannot be empty", obj.StructName())
- case field.Type():
- return fmt.Errorf("Object %s's Field.Type() cannot be empty", obj.StructName())
- }
- }
- return nil
-}
diff --git a/toolkit/gormgen/import.go b/toolkit/gormgen/import.go
deleted file mode 100644
index cf6ac7a4..00000000
--- a/toolkit/gormgen/import.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package gormgen
-
-import "strings"
-
-var (
- importList = new(importPkgS).Add(
- "context",
- "database/sql",
- "strings",
- "",
- "github.com/wubin1989/gorm",
- "github.com/wubin1989/gorm/schema",
- "github.com/wubin1989/gorm/clause",
- "",
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen",
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field",
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/helper",
- "",
- "github.com/wubin1989/dbresolver",
- )
- unitTestImportList = new(importPkgS).Add(
- "context",
- "fmt",
- "strconv",
- "testing",
- "",
- "github.com/wubin1989/sqlite",
- "github.com/wubin1989/gorm",
- )
-)
-
-type importPkgS struct {
- paths []string
-}
-
-func (ip importPkgS) Add(paths ...string) *importPkgS {
- purePaths := make([]string, 0, len(paths)+1)
- for _, p := range paths {
- p = strings.TrimSpace(p)
- if p == "" {
- purePaths = append(purePaths, p)
- continue
- }
-
- if p[len(p)-1] != '"' {
- p = `"` + p + `"`
- }
-
- var exists bool
- for _, existsP := range ip.paths {
- if p == existsP {
- exists = true
- break
- }
- }
- if !exists {
- purePaths = append(purePaths, p)
- }
- }
- purePaths = append(purePaths, "")
-
- ip.paths = append(ip.paths, purePaths...)
-
- return &ip
-}
-
-func (ip importPkgS) Paths() []string { return ip.paths }
diff --git a/toolkit/gormgen/interface.go b/toolkit/gormgen/interface.go
deleted file mode 100644
index 9dff6d1b..00000000
--- a/toolkit/gormgen/interface.go
+++ /dev/null
@@ -1,90 +0,0 @@
-package gormgen
-
-import (
- "database/sql"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/clause"
- "github.com/wubin1989/gorm/schema"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
-)
-
-type (
- // Condition query condition
- // field.Expr and subquery are expect value
- Condition interface {
- BeCond() interface{}
- CondError() error
- }
-)
-
-var (
- _ Condition = (field.Expr)(nil)
- _ Condition = (field.Value)(nil)
- _ Condition = (SubQuery)(nil)
- _ Condition = (Dao)(nil)
-)
-
-// SubQuery sub query interface
-type SubQuery interface {
- underlyingDB() *gorm.DB
- underlyingDO() *DO
-
- Condition
-}
-
-// Dao CRUD methods
-type Dao interface {
- SubQuery
- schema.Tabler
- As(alias string) Dao
-
- Not(conds ...Condition) Dao
- Or(conds ...Condition) Dao
-
- Select(columns ...field.Expr) Dao
- Where(conds ...Condition) Dao
- Order(columns ...field.Expr) Dao
- Distinct(columns ...field.Expr) Dao
- Omit(columns ...field.Expr) Dao
- Join(table schema.Tabler, conds ...field.Expr) Dao
- LeftJoin(table schema.Tabler, conds ...field.Expr) Dao
- RightJoin(table schema.Tabler, conds ...field.Expr) Dao
- Group(columns ...field.Expr) Dao
- Having(conds ...Condition) Dao
- Limit(limit int) Dao
- Offset(offset int) Dao
- Scopes(funcs ...func(Dao) Dao) Dao
- Unscoped() Dao
- Attrs(attrs ...field.AssignExpr) Dao
- Assign(attrs ...field.AssignExpr) Dao
- Joins(field field.RelationField) Dao
- Preload(field field.RelationField) Dao
- Clauses(conds ...clause.Expression) Dao
-
- Create(value interface{}) error
- CreateInBatches(value interface{}, batchSize int) error
- Save(value interface{}) error
- First() (result interface{}, err error)
- Take() (result interface{}, err error)
- Last() (result interface{}, err error)
- Find() (results interface{}, err error)
- FindInBatches(dest interface{}, batchSize int, fc func(tx Dao, batch int) error) error
- FirstOrInit() (result interface{}, err error)
- FirstOrCreate() (result interface{}, err error)
- Update(column field.Expr, value interface{}) (info ResultInfo, err error)
- UpdateSimple(columns ...field.AssignExpr) (info ResultInfo, err error)
- Updates(values interface{}) (info ResultInfo, err error)
- UpdateColumn(column field.Expr, value interface{}) (info ResultInfo, err error)
- UpdateColumns(values interface{}) (info ResultInfo, err error)
- UpdateColumnSimple(columns ...field.AssignExpr) (info ResultInfo, err error)
- Delete(...interface{}) (info ResultInfo, err error)
- Count() (int64, error)
- Row() *sql.Row
- Rows() (*sql.Rows, error)
- Scan(dest interface{}) error
- Fetch(dest interface{}) error
- Pluck(column field.Expr, dest interface{}) error
- ScanRows(rows *sql.Rows, dest interface{}) error
-}
diff --git a/toolkit/gormgen/internal/generate/clause.go b/toolkit/gormgen/internal/generate/clause.go
deleted file mode 100644
index fae615c4..00000000
--- a/toolkit/gormgen/internal/generate/clause.go
+++ /dev/null
@@ -1,179 +0,0 @@
-package generate
-
-import (
- "fmt"
- "strings"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/model"
-)
-
-// Clause a symbol of clause, it can be sql condition clause, if clause, where clause, set clause and else clause
-type Clause interface {
- String() string
- Create() string
-}
-
-var (
- _ Clause = new(SQLClause)
- _ Clause = new(IfClause)
- _ Clause = new(ElseClause)
- _ Clause = new(WhereClause)
- _ Clause = new(SetClause)
-)
-
-type clause struct {
- VarName string
- Type model.Status
-}
-
-// SQLClause sql condition clause
-type SQLClause struct {
- clause
- Value []string
-}
-
-func (s SQLClause) String() string {
- sqlString := strings.Join(s.Value, "+")
- // trim left space
- if strings.HasPrefix(sqlString, "\"") {
- sqlString = `"` + strings.TrimLeft(sqlString, `" `)
- }
- // make sure right has only one space
- if !strings.HasSuffix(sqlString, ` "`) {
- sqlString += `+" "`
- }
- // Remove redundant connection symbols
- return strings.ReplaceAll(sqlString, `"+"`, "")
-}
-
-// Create create clause
-func (s SQLClause) Create() string {
- return fmt.Sprintf("%s.WriteString(%s)", s.VarName, s.String())
-}
-
-// Finish finish clause
-func (s SQLClause) Finish() string {
- return fmt.Sprintf("%s.WriteString(%s)", s.VarName, s.String())
-}
-
-// IfClause if clause
-type IfClause struct {
- clause
- Value []Clause
- slice section
-}
-
-func (i IfClause) String() string {
- return i.slice.Value
-}
-
-// Create create clause
-func (i IfClause) Create() string {
- return fmt.Sprintf("%s {", i.String())
-}
-
-// Finish finish clause
-func (i IfClause) Finish() string {
- return "}"
-}
-
-// ElseClause else clause
-type ElseClause struct {
- IfClause
-}
-
-func (e ElseClause) String() (res string) {
- return e.slice.Value
-}
-
-// Create create clause
-func (e ElseClause) Create() string {
- return fmt.Sprintf("} %s {", e.String())
-}
-
-// Finish finish clause
-func (e ElseClause) Finish() string {
- return ""
-}
-
-// WhereClause where clause
-type WhereClause struct {
- clause
- Value []Clause
-}
-
-func (w WhereClause) String() string {
- return fmt.Sprintf("helper.WhereTrim(%s.String())", w.VarName)
-}
-
-// Create create clause
-func (w WhereClause) Create() string {
- return fmt.Sprintf("var %s strings.Builder", w.VarName)
-}
-
-// Finish finish clause
-func (w WhereClause) Finish(name string) string {
- return fmt.Sprintf("helper.JoinWhereBuilder(&%s,%s)", name, w.VarName)
-}
-
-// SetClause set clause
-type SetClause struct {
- clause
- Value []Clause
-}
-
-func (s SetClause) String() string {
- return fmt.Sprintf("helper.SetTrim(%s.String())", s.VarName)
-}
-
-// Create create clause
-func (s SetClause) Create() string {
- return fmt.Sprintf("var %s strings.Builder", s.VarName)
-}
-
-// Finish finish clause
-func (s SetClause) Finish(name string) string {
- return fmt.Sprintf("helper.JoinSetBuilder(&%s,%s)", name, s.VarName)
-}
-
-// TrimClause set clause
-type TrimClause struct {
- clause
- Value []Clause
-}
-
-func (s TrimClause) String() string {
- return fmt.Sprintf("helper.TrimALL(%s.String())", s.VarName)
-}
-
-// Create create trim clause
-func (s TrimClause) Create() string {
- return fmt.Sprintf("var %s strings.Builder", s.VarName)
-}
-
-// Finish finish trim clause
-func (s TrimClause) Finish(name string) string {
- return fmt.Sprintf("helper.JoinTrimAllBuilder(&%s,%s)", name, s.VarName)
-}
-
-// ForClause set clause
-type ForClause struct {
- clause
- Value []Clause
- ForRange ForRange
- forSlice section
-}
-
-func (f ForClause) String() string {
- return f.forSlice.Value + "{"
-}
-
-// Create create clause
-func (f ForClause) Create() string {
- return f.String()
-}
-
-// Finish finish clause
-func (f ForClause) Finish() string {
- return "}"
-}
diff --git a/toolkit/gormgen/internal/generate/clause_test.go b/toolkit/gormgen/internal/generate/clause_test.go
deleted file mode 100644
index 2addeb65..00000000
--- a/toolkit/gormgen/internal/generate/clause_test.go
+++ /dev/null
@@ -1,182 +0,0 @@
-package generate
-
-import (
- "testing"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/parser"
-)
-
-func checkBuildExpr(t *testing.T, SQL string, splitResult, generateResult []string, i *InterfaceMethod) {
- i.SQLString = SQL
- err := i.sqlStateCheckAndSplit()
- if err != nil {
- t.Errorf("err:%s\n", err)
- }
-
- if len(i.Section.members) != len(splitResult) {
- t.Errorf("SQL length exp:%v got:%v", len(generateResult), len(i.Section.members))
- }
- for index := range splitResult {
- if splitResult[index] != i.Section.members[index].Value {
- t.Errorf("SQL expects \nexp:%v \ngot:%v", splitResult[index], i.Section.members[index].Value)
- }
- }
- _, err = i.Section.BuildSQL()
- if err != nil {
- t.Errorf("err:%s", err)
- }
-
- if len(i.Section.Tmpls) != len(generateResult) {
- t.Errorf("SQL length exp:%v got:%v", len(i.Section.Tmpls), len(generateResult))
- }
- for index := range generateResult {
- if generateResult[index] != i.Section.Tmpls[index] {
- t.Errorf("SQL expects \nexp:%v \ngot:%v", generateResult[index], i.Section.Tmpls[index])
- }
- }
-
-}
-func TestClause(t *testing.T) {
-
- testcases := []struct {
- SQL string
- SplitResult []string
- GenerateResult []string
- }{
- {
- SQL: "select * from @@table",
- SplitResult: []string{
- "\"select * from \"",
- "\"users\"",
- },
- GenerateResult: []string{
- "generateSQL.WriteString(\"select * from users \")",
- },
- },
- {
- SQL: "select * from @@table {{where}} id>@id{{end}}",
- SplitResult: []string{
- "\"select * from \"",
- "\"users\"",
- "where",
- "\" id>\"",
- "id",
- "end",
- },
- GenerateResult: []string{
- "generateSQL.WriteString(\"select * from users \")",
- "var whereSQL0 strings.Builder",
- "params = append(params,id)",
- "whereSQL0.WriteString(\"id>? \")",
- "helper.JoinWhereBuilder(&generateSQL,whereSQL0)",
- },
- },
- {
- SQL: "select * from @@table {{where}}{{if id > 0}} id>@id{{end}}{{end}}",
- SplitResult: []string{
- "\"select * from \"",
- "\"users\"",
- "where",
- "if id > 0",
- "\" id>\"",
- "id",
- "end",
- "end",
- },
- GenerateResult: []string{
- "generateSQL.WriteString(\"select * from users \")",
- "var whereSQL0 strings.Builder",
- "if id > 0 {",
- "params = append(params,id)",
- "whereSQL0.WriteString(\"id>? \")",
- "}",
- "helper.JoinWhereBuilder(&generateSQL,whereSQL0)",
- },
- },
- {
- SQL: "update @@table {{set}}{{if name != \"\"}}name=@name{{end}},{{if id>0}}id=@id{{end}}{{end}} where id=@id",
- SplitResult: []string{
- "\"update \"",
- "\"users\"",
- "set",
- "if name != \"\"",
- "\"name=\"",
- "name",
- "end",
- "\",\"",
- "if id>0",
- "\"id=\"",
- "id",
- "end",
- "end",
- "\" where id=\"",
- "id",
- },
- GenerateResult: []string{
- "generateSQL.WriteString(\"update users \")",
- "var setSQL0 strings.Builder",
- "if name != \"\" {",
- "params = append(params,name)",
- "setSQL0.WriteString(\"name=? \")",
- "}",
- "setSQL0.WriteString(\", \")",
- "if id>0 {",
- "params = append(params,id)",
- "setSQL0.WriteString(\"id=? \")",
- "}",
- "helper.JoinSetBuilder(&generateSQL,setSQL0)",
- "params = append(params,id)",
- "generateSQL.WriteString(\"where id=? \")",
- },
- },
- {
- SQL: "select * from @@table {{where}} {{for _, name := range names}}name=@name{{end}}{{end}}",
- SplitResult: []string{
- "\"select * from \"",
- "\"users\"",
- "where",
- "for _, name := range names",
- "\"name=\"",
- "name",
- "end",
- "end",
- },
- GenerateResult: []string{
- "generateSQL.WriteString(\"select * from users \")",
- "var whereSQL0 strings.Builder",
- "for _, name := range names{",
- "params = append(params,name)",
- "whereSQL0.WriteString(\"name=? \")",
- "}",
- "helper.JoinWhereBuilder(&generateSQL,whereSQL0)",
- },
- },
- }
- inface := m()
- for _, testcase := range testcases {
- checkBuildExpr(t, testcase.SQL, testcase.SplitResult, testcase.GenerateResult, inface)
- }
-}
-
-var m = func() *InterfaceMethod {
- var m = new(InterfaceMethod)
- m.Table = "users"
- m.Params = []parser.Param{
- {
- Type: "int",
- Name: "id",
- },
- {
- Type: "string",
- Name: "name",
- },
- {
- Type: "string",
- Name: "names",
- IsArray: true,
- },
- }
-
- return m
-
-}
diff --git a/toolkit/gormgen/internal/generate/export.go b/toolkit/gormgen/internal/generate/export.go
deleted file mode 100644
index c9aefea3..00000000
--- a/toolkit/gormgen/internal/generate/export.go
+++ /dev/null
@@ -1,249 +0,0 @@
-package generate
-
-import (
- "context"
- "fmt"
- "github.com/golang/protobuf/protoc-gen-go/generator"
- "reflect"
- "strings"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/schema"
- "github.com/wubin1989/gorm/utils/tests"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/helper"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/model"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/parser"
-)
-
-// GetQueryStructMeta generate db model by table name
-func GetQueryStructMeta(db *gorm.DB, conf *model.Config) (*QueryStructMeta, error) {
- if _, ok := db.Config.Dialector.(tests.DummyDialector); ok {
- return nil, fmt.Errorf("UseDB() is necessary to generate model struct [%s] from database table [%s]", conf.ModelName, conf.TableName)
- }
-
- conf = conf.Preprocess()
- tableName, structName, fileName := conf.GetNames()
- if tableName == "" {
- return nil, nil
- }
- if err := checkStructName(structName); err != nil {
- return nil, fmt.Errorf("model name %q is invalid: %w", structName, err)
- }
-
- columns, err := getTableColumns(db, conf.GetSchemaName(db), tableName, conf.FieldWithIndexTag)
- if err != nil {
- return nil, err
- }
-
- fields := getFields(db, conf, columns)
- priKeyType := "string"
- pbPrimaryProp := "Id"
- for _, field := range fields {
- if field.PriKey {
- priKeyType = field.Type
- pbPrimaryProp = generator.CamelCase(field.ColumnName)
- break
- }
- }
-
- return (&QueryStructMeta{
- db: db,
- Source: model.Table,
- Generated: true,
- FileName: fileName,
- TableName: tableName,
- ModelStructName: structName,
- QueryStructName: uncaptialize(structName),
- S: strings.ToLower(structName[0:1]),
- StructInfo: parser.Param{Type: structName, Package: conf.ModelPkg},
- ImportPkgPaths: conf.ImportPkgPaths,
- Fields: fields,
- PriKeyType: priKeyType,
- PbPrimaryProp: pbPrimaryProp,
- }).addMethodFromAddMethodOpt(conf.GetModelMethods()...), nil
-}
-
-// GetQueryStructMetaFromObject generate base struct from object
-func GetQueryStructMetaFromObject(obj helper.Object, conf *model.Config) (*QueryStructMeta, error) {
- err := helper.CheckObject(obj)
- if err != nil {
- return nil, err
- }
-
- conf = conf.Preprocess()
-
- tableName := obj.TableName()
- if conf.TableNameNS != nil {
- tableName = conf.TableNameNS(tableName)
- }
-
- structName := obj.StructName()
- if conf.ModelNameNS != nil {
- structName = conf.ModelNameNS(structName)
- }
-
- fileName := obj.FileName()
- if fileName == "" {
- fileName = tableName
- }
- if fileName == "" {
- fileName = structName
- }
- if conf.FileNameNS != nil {
- fileName = conf.FileNameNS(fileName)
- } else {
- fileName = schema.NamingStrategy{SingularTable: true}.TableName(fileName)
- }
-
- fields := make([]*model.Field, 0, 16)
- for _, fl := range obj.Fields() {
- tag := fl.Tag()
- if tag == nil {
- tag = field.Tag{}
- }
- if gt := fl.GORMTag(); gt != "" {
- tag.Set(field.TagKeyGorm, gt)
- }
- if jt := fl.JSONTag(); jt != "" {
- tag.Set(field.TagKeyJson, jt)
- }
-
- fields = append(fields, &model.Field{
- Name: fl.Name(),
- Type: fl.Type(),
- ColumnName: fl.ColumnName(),
- Tag: tag,
- ColumnComment: fl.Comment(),
- MultilineComment: strings.Contains(fl.Comment(), "\n"),
- })
- }
-
- return &QueryStructMeta{
- Source: model.Object,
- Generated: true,
- FileName: fileName,
- TableName: tableName,
- ModelStructName: structName,
- QueryStructName: uncaptialize(structName),
- S: strings.ToLower(structName[0:1]),
- StructInfo: parser.Param{Type: structName, Package: conf.ModelPkg},
- ImportPkgPaths: append(conf.ImportPkgPaths, obj.ImportPkgPaths()...),
- Fields: fields,
- }, nil
-}
-
-// ConvertStructs convert to base structures
-func ConvertStructs(db *gorm.DB, structs ...interface{}) (metas []*QueryStructMeta, err error) {
- for _, st := range structs {
- if isNil(st) {
- continue
- }
- if base, ok := st.(*QueryStructMeta); ok {
- metas = append(metas, base)
- continue
- }
- if !isStructType(reflect.ValueOf(st)) {
- return nil, fmt.Errorf("%s is not a struct", reflect.TypeOf(st).String())
- }
-
- structType := reflect.TypeOf(st)
- name := getStructName(structType.String())
- newStructName := name
- if st, ok := st.(interface{ GenInternalDoName() string }); ok {
- newStructName = st.GenInternalDoName()
- }
-
- meta := &QueryStructMeta{
- S: getPureName(name),
- ModelStructName: name,
- QueryStructName: uncaptialize(newStructName),
- StructInfo: parser.Param{PkgPath: structType.PkgPath(), Type: name, Package: getPackageName(structType.String())},
- Source: model.Struct,
- db: db,
- }
- if err := meta.parseStruct(st); err != nil {
- return nil, fmt.Errorf("transform struct [%s.%s] error:%s", meta.StructInfo.Package, name, err)
- }
- if err := meta.check(); err != nil {
- db.Logger.Warn(context.Background(), err.Error())
- continue
- }
-
- metas = append(metas, meta)
- }
- return
-}
-
-func isNil(i interface{}) bool {
- if i == nil {
- return true
- }
-
- // if v is not ptr, return false(i is not nil)
- // if v is ptr, return v.IsNil()
- v := reflect.ValueOf(i)
- return v.Kind() == reflect.Ptr && v.IsNil()
-}
-
-// BuildDIYMethod check the legitimacy of interfaces
-func BuildDIYMethod(f *parser.InterfaceSet, s *QueryStructMeta, data []*InterfaceMethod) (checkResults []*InterfaceMethod, err error) {
- for _, interfaceInfo := range f.Interfaces {
- if interfaceInfo.MatchStruct(s.ModelStructName) {
- for _, method := range interfaceInfo.Methods {
- t := &InterfaceMethod{
- S: s.S,
- TargetStruct: s.QueryStructName,
- OriginStruct: s.StructInfo,
- MethodName: method.MethodName,
- Params: method.Params,
- Doc: method.Doc,
- Table: s.TableName,
- InterfaceName: interfaceInfo.Name,
- Package: getPackageName(interfaceInfo.Package),
- }
- if err = t.checkMethod(data, s); err != nil {
- return nil, err
- }
- if err = t.checkParams(method.Params); err != nil {
- return
- }
- if err = t.checkResult(method.Result); err != nil {
- return
- }
- if err = t.checkSQL(); err != nil {
- return
- }
- _, err = t.Section.BuildSQL()
- if err != nil {
- err = fmt.Errorf("sql [%s] build err:%w", t.SQLString, err)
- return
- }
- checkResults = append(checkResults, t)
- }
- }
- }
- return
-}
-
-// ParseStructRelationShip parse struct's relationship
-// No one should use it directly in project
-func ParseStructRelationShip(relationship *schema.Relationships) []field.Relation {
- cache := make(map[string]bool)
- return append(append(append(append(
- make([]field.Relation, 0, 4),
- pullRelationShip(cache, relationship.HasOne)...),
- pullRelationShip(cache, relationship.HasMany)...),
- pullRelationShip(cache, relationship.BelongsTo)...),
- pullRelationShip(cache, relationship.Many2Many)...,
- )
-}
-
-// GetStructNames get struct names from base structs
-func GetStructNames(bases []*QueryStructMeta) (names []string) {
- for _, base := range bases {
- names = append(names, base.ModelStructName)
- }
- return names
-}
diff --git a/toolkit/gormgen/internal/generate/generate.go b/toolkit/gormgen/internal/generate/generate.go
deleted file mode 100644
index 22f03d2f..00000000
--- a/toolkit/gormgen/internal/generate/generate.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package generate
-
-import (
- "fmt"
- "regexp"
- "strings"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/schema"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/model"
-)
-
-/*
-** The feature of mapping table from database server to Golang struct
-** Provided by @qqxhb
- */
-
-func getFields(db *gorm.DB, conf *model.Config, columns []*model.Column) (fields []*model.Field) {
- for _, col := range columns {
- col.SetDataTypeMap(conf.DataTypeMap)
- col.WithNS(conf.FieldJSONTagNS)
-
- m := col.ToField(conf.FieldNullable, conf.FieldCoverable, conf.FieldSignable)
-
- if filterField(m, conf.FilterOpts) == nil {
- continue
- }
- if _, ok := col.ColumnType.ColumnType(); ok && !conf.FieldWithTypeTag { // remove type tag if FieldWithTypeTag == false
- m.GORMTag.Remove("type")
- }
-
- m = modifyField(m, conf.ModifyOpts)
- if ns, ok := db.NamingStrategy.(schema.NamingStrategy); ok {
- ns.SingularTable = true
- m.Name = ns.SchemaName(ns.TablePrefix + m.Name)
- } else if db.NamingStrategy != nil {
- m.Name = db.NamingStrategy.SchemaName(m.Name)
- }
-
- fields = append(fields, m)
- }
- for _, create := range conf.CreateOpts {
- m := create.Operator()(nil)
- if m.Relation != nil {
- if m.Relation.Model() != nil {
- stmt := gorm.Statement{DB: db}
- _ = stmt.Parse(m.Relation.Model())
- if stmt.Schema != nil {
- m.Relation.AppendChildRelation(ParseStructRelationShip(&stmt.Schema.Relationships)...)
- }
- }
- m.Type = strings.ReplaceAll(m.Type, conf.ModelPkg+".", "") // remove modelPkg in field's Type, avoid import error
- }
-
- fields = append(fields, m)
- }
- return fields
-}
-
-func filterField(m *model.Field, opts []model.FieldOption) *model.Field {
- for _, opt := range opts {
- if opt.Operator()(m) == nil {
- return nil
- }
- }
- return m
-}
-
-func modifyField(m *model.Field, opts []model.FieldOption) *model.Field {
- for _, opt := range opts {
- m = opt.Operator()(m)
- }
- return m
-}
-
-// get mysql db' name
-var modelNameReg = regexp.MustCompile(`^\w+$`)
-
-func checkStructName(name string) error {
- if name == "" {
- return nil
- }
- if !modelNameReg.MatchString(name) {
- return fmt.Errorf("model name cannot contains invalid character")
- }
- if name[0] < 'A' || name[0] > 'Z' {
- return fmt.Errorf("model name must be initial capital")
- }
- return nil
-}
diff --git a/toolkit/gormgen/internal/generate/interface.go b/toolkit/gormgen/internal/generate/interface.go
deleted file mode 100644
index af22b5cc..00000000
--- a/toolkit/gormgen/internal/generate/interface.go
+++ /dev/null
@@ -1,494 +0,0 @@
-package generate
-
-import (
- "fmt"
- "strconv"
- "strings"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/model"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/parser"
-)
-
-// InterfaceMethod interface's method
-type InterfaceMethod struct { // feature will replace InterfaceMethod to parser.Method
- Doc string // comment
- S string // First letter of
- OriginStruct parser.Param // origin struct name
- TargetStruct string // generated query struct bane
- MethodName string // generated function name
- Params []parser.Param // function input params
- Result []parser.Param // function output params
- ResultData parser.Param // output data
- Section *Section // Parse split SQL into sections
- SQLParams []parser.Param // variable in sql need function input
- SQLString string // SQL
- GormOption string // gorm execute method Find or Exec or Take
- Table string // specified by user. if empty, generate it with gorm
- InterfaceName string // origin interface name
- Package string // interface package name
- HasForParams bool //
-}
-
-// FuncSign function signature
-func (m *InterfaceMethod) FuncSign() string {
- return fmt.Sprintf("%s(%s) (%s)", m.MethodName, m.GetParamInTmpl(), m.GetResultParamInTmpl())
-}
-
-// HasSQLData has variable or for params will creat params map
-func (m *InterfaceMethod) HasSQLData() bool {
- return len(m.SQLParams) > 0 || m.HasForParams
-}
-
-// HasGotPoint parameter has pointer or not
-func (m *InterfaceMethod) HasGotPoint() bool {
- return !m.HasNeedNewResult()
-}
-
-// HasNeedNewResult need pointer or not
-func (m *InterfaceMethod) HasNeedNewResult() bool {
- return !m.ResultData.IsArray && ((m.ResultData.IsNull() && m.ResultData.IsTime()) || m.ResultData.IsMap())
-}
-
-// GormRunMethodName return single data use Take() return array use Find
-func (m *InterfaceMethod) GormRunMethodName() string {
- if m.ResultData.IsArray {
- return "Find"
- }
- return "Take"
-}
-
-// ReturnSQLResult return sql result
-func (m *InterfaceMethod) ReturnSQLResult() bool {
- for _, res := range m.Result {
- if res.IsSQLResult() {
- return true
- }
- }
- return false
-}
-
-// ReturnSQLRow return sql result
-func (m *InterfaceMethod) ReturnSQLRow() bool {
- for _, res := range m.Result {
- if res.IsSQLRow() {
- return true
- }
- }
- return false
-}
-
-// ReturnSQLRows return sql result
-func (m *InterfaceMethod) ReturnSQLRows() bool {
- for _, res := range m.Result {
- if res.IsSQLRows() {
- return true
- }
- }
- return false
-}
-
-// ReturnNothing not return error and rowAffected
-func (m *InterfaceMethod) ReturnNothing() bool {
- for _, res := range m.Result {
- if res.IsError() || res.Name == "rowsAffected" {
- return false
- }
- }
- return true
-}
-
-// ReturnRowsAffected return rows affected
-func (m *InterfaceMethod) ReturnRowsAffected() bool {
- for _, res := range m.Result {
- if res.Name == "rowsAffected" {
- return true
- }
- }
- return false
-}
-
-// ReturnError return error
-func (m *InterfaceMethod) ReturnError() bool {
- for _, res := range m.Result {
- if res.IsError() {
- return true
- }
- }
- return false
-}
-
-// IsRepeatFromDifferentInterface check different interface has same mame method
-func (m *InterfaceMethod) IsRepeatFromDifferentInterface(newMethod *InterfaceMethod) bool {
- return m.MethodName == newMethod.MethodName && m.InterfaceName != newMethod.InterfaceName && m.TargetStruct == newMethod.TargetStruct
-}
-
-// IsRepeatFromSameInterface check different interface has same mame method
-func (m *InterfaceMethod) IsRepeatFromSameInterface(newMethod *InterfaceMethod) bool {
- return m.MethodName == newMethod.MethodName && m.InterfaceName == newMethod.InterfaceName && m.TargetStruct == newMethod.TargetStruct
-}
-
-// GetParamInTmpl return param list
-func (m *InterfaceMethod) GetParamInTmpl() string {
- return paramToString(m.Params)
-}
-
-// GetResultParamInTmpl return result list
-func (m *InterfaceMethod) GetResultParamInTmpl() string {
- return paramToString(m.Result)
-}
-
-// SQLParamName sql param map key,
-func (m *InterfaceMethod) SQLParamName(param string) string {
- return strings.Replace(param, ".", "", -1)
-}
-
-// paramToString param list to string used in tmpl
-func paramToString(params []parser.Param) string {
- var res []string
- for _, param := range params {
- res = append(res, param.TmplString())
- }
- return strings.Join(res, ",")
-}
-
-// DocComment return comment sql add "//" every line
-func (m *InterfaceMethod) DocComment() string {
- return strings.Replace(strings.Replace(strings.TrimSpace(m.Doc), "\n", "\n// ", -1), "// ", "// ", -1)
-}
-
-// checkParams check all parameters
-func (m *InterfaceMethod) checkMethod(methods []*InterfaceMethod, s *QueryStructMeta) (err error) {
- if model.GormKeywords.FullMatch(m.MethodName) {
- return fmt.Errorf("can not use keyword as method name:%s", m.MethodName)
- }
- // TODO check methods Always empty?
- for _, method := range methods {
- if m.IsRepeatFromDifferentInterface(method) {
- return fmt.Errorf("can not generate method with the same name from different interface:[%s.%s] and [%s.%s]",
- m.InterfaceName, m.MethodName, method.InterfaceName, method.MethodName)
- }
- }
- for _, f := range s.Fields {
- if f.Name == m.MethodName {
- return fmt.Errorf("can not generate method same name with struct field:[%s.%s] and [%s.%s]",
- m.InterfaceName, m.MethodName, s.ModelStructName, f.Name)
- }
- }
-
- return nil
-}
-
-// checkParams check all parameters
-func (m *InterfaceMethod) checkParams(params []parser.Param) (err error) {
- paramList := make([]parser.Param, len(params))
- for i, param := range params {
- switch {
- case param.Package == "UNDEFINED":
- param.Package = m.Package
- case param.IsError() || param.IsNull():
- return fmt.Errorf("type error on interface [%s] param: [%s]", m.InterfaceName, param.Name)
- case param.IsGenM():
- param.Type = "map[string]interface{}"
- param.Package = ""
- case param.IsGenT():
- param.Type = m.OriginStruct.Type
- param.Package = m.OriginStruct.Package
- }
- paramList[i] = param
- }
- m.Params = paramList
- return
-}
-
-// checkResult check all parameters and replace gen.T by target structure. Parameters must be one of int/string/struct/map
-func (m *InterfaceMethod) checkResult(result []parser.Param) (err error) {
- resList := make([]parser.Param, len(result))
- var hasError bool
- for i, param := range result {
- if param.Package == "UNDEFINED" {
- param.Package = m.Package
- }
- if param.IsGenM() {
- param.Type = "map[string]interface{}"
- param.Package = ""
- }
- switch {
- case param.InMainPkg():
- return fmt.Errorf("query method cannot return struct of main package in [%s.%s]", m.InterfaceName, m.MethodName)
- case param.IsError():
- if hasError {
- return fmt.Errorf("query method cannot return more than 1 error value in [%s.%s]", m.InterfaceName, m.MethodName)
- }
- param.SetName("err")
- hasError = true
- case param.Eq(m.OriginStruct) || param.IsGenT():
- if !m.ResultData.IsNull() {
- return fmt.Errorf("query method cannot return more than 1 data value in [%s.%s]", m.InterfaceName, m.MethodName)
- }
- param.SetName("result")
- param.Type = m.OriginStruct.Type
- param.Package = m.OriginStruct.Package
- m.ResultData = param
- case param.IsInterface():
- return fmt.Errorf("query method can not return interface in [%s.%s]", m.InterfaceName, m.MethodName)
- case param.IsGenRowsAffected():
- param.Type = "int64"
- param.Package = ""
- param.SetName("rowsAffected")
- m.GormOption = "Exec"
- case param.IsSQLResult():
- param.Type = "Result"
- param.Package = "sql"
- param.SetName("result")
- m.GormOption = "Statement.ConnPool.ExecContext"
- case param.IsSQLRow():
- param.Type = "Row"
- param.Package = "sql"
- param.SetName("row")
- m.GormOption = "Raw"
- param.IsPointer = true
- case param.IsSQLRows():
- param.Type = "Rows"
- param.Package = "sql"
- param.SetName("rows")
- m.GormOption = "Raw"
- param.IsPointer = true
- default:
- if !m.ResultData.IsNull() {
- return fmt.Errorf("query method cannot return more than 1 data value in [%s.%s]", m.InterfaceName, m.MethodName)
- }
- if param.Package == "" && !(param.IsBaseType() || param.IsMap() || param.IsTime()) {
- param.Package = m.Package
- }
- param.SetName("result")
- m.ResultData = param
- }
- resList[i] = param
- }
- m.Result = resList
- return
-}
-
-// checkSQL get sql from comment and check it
-func (m *InterfaceMethod) checkSQL() (err error) {
- m.SQLString = m.parseDocString()
- if err = m.sqlStateCheckAndSplit(); err != nil {
- err = fmt.Errorf("interface %s member method %s check sql err:%w", m.InterfaceName, m.MethodName, err)
- }
- return
-}
-
-func (m *InterfaceMethod) parseDocString() string {
- docString := strings.TrimSpace(m.getSQLDocString())
- switch {
- case strings.HasPrefix(strings.ToLower(docString), "sql("):
- docString = docString[4 : len(docString)-1]
- m.GormOption = "Raw"
- if m.ResultData.IsNull() {
- m.GormOption = "Exec"
- }
- case strings.HasPrefix(strings.ToLower(docString), "where("):
- docString = docString[6 : len(docString)-1]
- m.GormOption = "Where"
- default:
- m.GormOption = "Raw"
- if m.ResultData.IsNull() {
- m.GormOption = "Exec"
- }
- }
-
- // if wrapped by ", trim it
- if strings.HasPrefix(docString, `"`) && strings.HasSuffix(docString, `"`) {
- docString = docString[1 : len(docString)-1]
- }
- return docString
-}
-
-func (m *InterfaceMethod) getSQLDocString() string {
- docString := strings.TrimSpace(m.Doc)
- /*
- // methodName descriptive message
- // (this blank line is needed)
- // sql
- */
- if index := strings.Index(docString, "\n\n"); index != -1 {
- if strings.Contains(docString[index+2:], m.MethodName) {
- docString = docString[:index]
- } else {
- docString = docString[index+2:]
- }
- }
- /* //methodName sql */
- docString = strings.TrimPrefix(docString, m.MethodName)
- // TODO: using sql key word to split comment
- return docString
-}
-
-// sqlStateCheckAndSplit check sql with an adeterministic finite automaton
-func (m *InterfaceMethod) sqlStateCheckAndSplit() error {
- sqlString := m.SQLString
- m.Section = NewSection()
- var buf model.SQLBuffer
- for i := 0; !strOutRange(i, sqlString); i++ {
- b := sqlString[i]
- switch b {
- case '"':
- _ = buf.WriteByte(sqlString[i])
- for i++; ; i++ {
- if strOutRange(i, sqlString) {
- return fmt.Errorf("incomplete SQL:%s", sqlString)
- }
- _ = buf.WriteByte(sqlString[i])
- if sqlString[i] == '"' && sqlString[i-1] != '\\' {
- break
- }
- }
- case '\'':
- _ = buf.WriteByte(sqlString[i])
- for i++; ; i++ {
- if strOutRange(i, sqlString) {
- return fmt.Errorf("incomplete SQL:%s", sqlString)
- }
- _ = buf.WriteByte(sqlString[i])
- if sqlString[i] == '\'' && sqlString[i-1] != '\\' {
- break
- }
- }
- case '\\':
- if sqlString[i+1] == '@' {
- i++
- buf.WriteSQL(sqlString[i])
- continue
- }
- buf.WriteSQL(b)
- case '{', '@':
- if sqlClause := buf.Dump(); strings.TrimSpace(sqlClause) != "" {
- m.Section.members = append(m.Section.members, section{
- Type: model.SQL,
- Value: strconv.Quote(sqlClause),
- })
- }
-
- if strOutRange(i+1, sqlString) {
- return fmt.Errorf("incomplete SQL:%s", sqlString)
- }
- if b == '{' && sqlString[i+1] == '{' {
- for i += 2; ; i++ {
- if strOutRange(i, sqlString) {
- return fmt.Errorf("incomplete SQL:%s", sqlString)
- }
- if sqlString[i] == '"' {
- _ = buf.WriteByte(sqlString[i])
- for i++; ; i++ {
- if strOutRange(i, sqlString) {
- return fmt.Errorf("incomplete SQL:%s", sqlString)
- }
- _ = buf.WriteByte(sqlString[i])
- if sqlString[i] == '"' && sqlString[i-1] != '\\' {
- break
- }
- }
- i++
- }
-
- if strOutRange(i+1, sqlString) {
- return fmt.Errorf("incomplete SQL:%s", sqlString)
- }
- if sqlString[i] == '}' && sqlString[i+1] == '}' {
- i++
- sqlClause := buf.Dump()
- part, err := m.Section.checkTemplate(sqlClause)
- if err != nil {
- return fmt.Errorf("sql [%s] dynamic template %s err:%w", sqlString, sqlClause, err)
- }
- m.Section.members = append(m.Section.members, part)
- break
- }
- buf.WriteSQL(sqlString[i])
- }
- }
- if b == '@' {
- i++
- status := model.DATA
- if sqlString[i] == '@' {
- i++
- status = model.VARIABLE
- }
- for ; ; i++ {
- if strOutRange(i, sqlString) || isEnd(sqlString[i]) {
- varString := buf.Dump()
- params, err := m.Section.checkSQLVar(varString, status, m)
- if err != nil {
- return fmt.Errorf("sql [%s] varable %s err:%s", sqlString, varString, err)
- }
- m.Section.members = append(m.Section.members, params)
- i--
- break
- }
- buf.WriteSQL(sqlString[i])
- }
- }
- default:
- buf.WriteSQL(b)
- }
- }
- if sqlClause := buf.Dump(); strings.TrimSpace(sqlClause) != "" {
- m.Section.members = append(m.Section.members, section{
- Type: model.SQL,
- Value: strconv.Quote(sqlClause),
- })
- }
-
- return nil
-}
-
-// checkSQLVarByParams return external parameters, table name
-func (m *InterfaceMethod) checkSQLVarByParams(param string, status model.Status) (result section, err error) {
- for _, p := range m.Params {
- structName := strings.Split(param, ".")[0]
- if p.Name == structName {
- if p.Name != param {
- p = parser.Param{
- Name: param,
- Type: "string",
- }
- }
- switch status {
- case model.DATA:
- if !m.isParamExist(param) {
- m.SQLParams = append(m.SQLParams, p)
- }
- case model.VARIABLE:
- if p.Type != "string" || p.IsArray {
- err = fmt.Errorf("variable name must be string :%s type is %s", param, p.TypeName())
- return
- }
- param = fmt.Sprintf("%s.Quote(%s)", m.S, param)
- }
- result = section{
- Type: status,
- Value: param,
- }
- return
- }
- }
- if param == "table" {
- result = section{
- Type: model.SQL,
- Value: strconv.Quote(m.Table),
- }
- return
- }
-
- return result, fmt.Errorf("unknow variable param:%s", param)
-}
-
-// isParamExist check param duplicate
-func (m *InterfaceMethod) isParamExist(paramName string) bool {
- for _, param := range m.SQLParams {
- if param.Name == paramName {
- return true
- }
- }
- return false
-}
diff --git a/toolkit/gormgen/internal/generate/query.go b/toolkit/gormgen/internal/generate/query.go
deleted file mode 100644
index f8d340dd..00000000
--- a/toolkit/gormgen/internal/generate/query.go
+++ /dev/null
@@ -1,241 +0,0 @@
-package generate
-
-import (
- "context"
- "fmt"
- "reflect"
- "strings"
-
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/schema"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/model"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/parser"
-)
-
-type FieldParser interface {
- GetFieldGenType(f *schema.Field) string
-}
-
-type dummyFieldParser struct{}
-
-func (dummyFieldParser) GetFieldGenType(*schema.Field) string { return "" }
-
-// QueryStructMeta struct info in generated code
-type QueryStructMeta struct {
- db *gorm.DB
-
- Generated bool // whether to generate db model
- FileName string // generated file name
- S string // the first letter(lower case)of simple Name (receiver)
- QueryStructName string // internal query struct name
- ModelStructName string // origin/model struct name
- TableName string // table name in db server
- StructInfo parser.Param
- Fields []*model.Field
- Source model.SourceCode
- ImportPkgPaths []string
- ModelMethods []*parser.Method // user custom method bind to db base struct
- interfaceMode bool
- PriKeyType string
- PbPrimaryProp string
-}
-
-// parseStruct get all elements of struct with gorm's Parse, ignore unexported elements
-func (b *QueryStructMeta) parseStruct(st interface{}) error {
- stmt := gorm.Statement{DB: b.db}
-
- err := stmt.Parse(st)
- if err != nil {
- return err
- }
- b.TableName = stmt.Table
- b.FileName = strings.ToLower(stmt.Table)
-
- var fp FieldParser = dummyFieldParser{}
- if fps, ok := st.(FieldParser); ok && fps != nil {
- fp = fps
- }
- for _, f := range stmt.Schema.Fields {
- b.appendOrUpdateField(&model.Field{
- Name: f.Name,
- Type: b.getFieldRealType(f.FieldType),
- ColumnName: f.DBName,
- CustomGenType: fp.GetFieldGenType(f),
- })
- }
- for _, r := range ParseStructRelationShip(&stmt.Schema.Relationships) {
- r := r
- b.appendOrUpdateField(&model.Field{Relation: &r})
- }
- return nil
-}
-
-// getFieldRealType get basic type of field
-func (b *QueryStructMeta) getFieldRealType(f reflect.Type) string {
- serializerInterface := reflect.TypeOf((*schema.SerializerInterface)(nil)).Elem()
- if f.Implements(serializerInterface) || reflect.New(f).Type().Implements(serializerInterface) {
- return "serializer"
- }
- scanValuer := reflect.TypeOf((*field.ScanValuer)(nil)).Elem()
- if f.Implements(scanValuer) || reflect.New(f).Type().Implements(scanValuer) {
- return "field"
- }
-
- if f.Kind() == reflect.Ptr {
- f = f.Elem()
- }
- if f.String() == "time.Time" {
- return "time.Time"
- }
- if f.String() == "[]uint8" || f.String() == "json.RawMessage" {
- return "bytes"
- }
- return f.Kind().String()
-}
-
-// ReviseFieldName revise field name
-func (b *QueryStructMeta) ReviseFieldName() {
- b.ReviseFieldNameFor(model.GormKeywords)
-}
-
-// ReviseFieldNameFor revise field name for keywords
-func (b *QueryStructMeta) ReviseFieldNameFor(keywords model.KeyWord) {
- for _, m := range b.Fields {
- m.EscapeKeywordFor(keywords)
- }
-}
-
-// check field if in BaseStruct update else append
-func (b *QueryStructMeta) appendOrUpdateField(f *model.Field) {
- if f.IsRelation() {
- b.appendField(f)
- }
- if f.ColumnName == "" {
- return
- }
- for i, m := range b.Fields {
- if m.Name == f.Name {
- b.Fields[i] = f
- return
- }
- }
- b.appendField(f)
-}
-
-func (b *QueryStructMeta) appendField(f *model.Field) { b.Fields = append(b.Fields, f) }
-
-// HasField check if BaseStruct has fields
-func (b *QueryStructMeta) HasField() bool { return len(b.Fields) > 0 }
-
-// check if struct is exportable and if struct in main package and if field's type is regular
-func (b *QueryStructMeta) check() (err error) {
- if b.StructInfo.InMainPkg() {
- return fmt.Errorf("can't generated data object for struct in main package, ignore:%s", b.ModelStructName)
- }
- if !isCapitalize(b.ModelStructName) {
- return fmt.Errorf("can't generated data object for non-exportable struct, ignore:%s", b.QueryStructName)
- }
- return nil
-}
-
-// Relations related field
-func (b *QueryStructMeta) Relations() (result []field.Relation) {
- for _, f := range b.Fields {
- if f.IsRelation() {
- result = append(result, *f.Relation)
- }
- }
- return result
-}
-
-// StructComment struct comment
-func (b *QueryStructMeta) StructComment() string {
- if b.TableName != "" {
- return fmt.Sprintf(`mapped from table <%s>`, b.TableName)
- }
- return `mapped from object`
-}
-
-// ReviseDIYMethod check diy method duplication name
-func (b *QueryStructMeta) ReviseDIYMethod() error {
- var duplicateMethodName []string
- //var tableName *parser.Method
- methods := make([]*parser.Method, 0, len(b.ModelMethods))
- methodMap := make(map[string]bool, len(b.ModelMethods))
- for _, method := range b.ModelMethods {
- if methodMap[method.MethodName] {
- duplicateMethodName = append(duplicateMethodName, method.MethodName)
- continue
- }
- method.Receiver.Package = ""
- method.Receiver.Type = b.ModelStructName
- methods = append(methods, method)
- methodMap[method.MethodName] = true
- }
- b.ModelMethods = methods
-
- if len(duplicateMethodName) > 0 {
- return fmt.Errorf("can't generate struct with duplicated method, please check method name: %s", strings.Join(duplicateMethodName, ","))
- }
- return nil
-}
-
-func (b *QueryStructMeta) addMethodFromAddMethodOpt(methods ...interface{}) *QueryStructMeta {
- for _, method := range methods {
- modelMethods, err := parser.GetModelMethod(method, 5)
- if err != nil {
- panic("add diy method err:" + err.Error())
- }
- b.ModelMethods = append(b.ModelMethods, modelMethods.Methods...)
- }
-
- err := b.ReviseDIYMethod()
- if err != nil {
- b.db.Logger.Warn(context.Background(), err.Error())
- }
- return b
-}
-
-// IfaceMode object mode
-func (b QueryStructMeta) IfaceMode(on bool) *QueryStructMeta {
- b.interfaceMode = on
- return &b
-}
-
-// ReturnObject return object in generated code
-func (b *QueryStructMeta) ReturnObject() string {
- if b.interfaceMode {
- return fmt.Sprint("I", b.ModelStructName, "Do")
- }
- return fmt.Sprint("*", b.QueryStructName, "Do")
-}
-
-func isStructType(data reflect.Value) bool {
- return data.Kind() == reflect.Struct ||
- (data.Kind() == reflect.Ptr && data.Elem().Kind() == reflect.Struct)
-}
-
-func pullRelationShip(cache map[string]bool, relationships []*schema.Relationship) []field.Relation {
- if len(relationships) == 0 {
- return nil
- }
- result := make([]field.Relation, len(relationships))
- for i, relationship := range relationships {
- var childRelations []field.Relation
- varType := strings.TrimLeft(relationship.Field.FieldType.String(), "[]*")
- if !cache[varType] {
- cache[varType] = true
- childRelations = pullRelationShip(cache, append(append(append(append(
- make([]*schema.Relationship, 0, 4),
- relationship.FieldSchema.Relationships.BelongsTo...),
- relationship.FieldSchema.Relationships.HasOne...),
- relationship.FieldSchema.Relationships.HasMany...),
- relationship.FieldSchema.Relationships.Many2Many...),
- )
- }
- result[i] = *field.NewRelationWithType(field.RelationshipType(relationship.Type), relationship.Name, varType, childRelations...)
- }
- return result
-}
diff --git a/toolkit/gormgen/internal/generate/section.go b/toolkit/gormgen/internal/generate/section.go
deleted file mode 100644
index 408dcdbf..00000000
--- a/toolkit/gormgen/internal/generate/section.go
+++ /dev/null
@@ -1,700 +0,0 @@
-package generate
-
-import (
- "fmt"
- "strconv"
- "strings"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/model"
-)
-
-// NewSection create and initialize Sections
-func NewSection() *Section {
- return &Section{
- ClauseTotal: map[model.Status]int{
- model.WHERE: 0,
- model.SET: 0,
- },
- }
-}
-
-// Section split sql into chunks
-type Section struct {
- members []section
- Tmpls []string
- currentIndex int
- ClauseTotal map[model.Status]int
- forValue []ForRange
-}
-
-// next return next section and increase index by 1
-func (s *Section) next() section {
- if s.currentIndex < len(s.members)-1 {
- s.currentIndex++
- return s.members[s.currentIndex]
- }
- return section{Type: model.END}
-}
-
-// SubIndex take index one step back
-func (s *Section) SubIndex() {
- s.currentIndex--
-}
-
-// HasMore is has more section
-func (s *Section) HasMore() bool {
- return s.currentIndex < len(s.members)-1
-}
-
-// IsNull whether section is empty
-func (s *Section) IsNull() bool {
- return len(s.members) == 0
-}
-
-// current return current section
-func (s *Section) current() section {
- return s.members[s.currentIndex]
-}
-
-func (s *Section) appendTmpl(value string) {
- s.Tmpls = append(s.Tmpls, value)
-}
-
-func (s *Section) hasSameName(value string) bool {
- for _, p := range s.members {
- if p.Type == model.FOR && p.ForRange.value == value {
- return true
- }
- }
- return false
-}
-
-// BuildSQL sql sections and append to tmpl, return a Clause array
-func (s *Section) BuildSQL() ([]Clause, error) {
- if s.IsNull() {
- return nil, fmt.Errorf("sql is null")
- }
- name := "generateSQL"
- res := make([]Clause, 0, len(s.members))
- for {
- c := s.current()
- switch c.Type {
- case model.SQL, model.DATA, model.VARIABLE:
- sqlClause := s.parseSQL(name)
- res = append(res, sqlClause)
- s.appendTmpl(sqlClause.Finish())
- case model.IF:
- ifClause, err := s.parseIF(name)
- if err != nil {
- return nil, err
- }
- res = append(res, ifClause)
- s.appendTmpl(ifClause.Finish())
- case model.WHERE:
- whereClause, err := s.parseWhere()
- if err != nil {
- return nil, err
- }
- res = append(res, whereClause)
- s.appendTmpl(whereClause.Finish(name))
- case model.SET:
- setClause, err := s.parseSet()
- if err != nil {
- return nil, err
- }
- res = append(res, setClause)
- s.appendTmpl(setClause.Finish(name))
- case model.TRIM:
- trimClause, err := s.parseTrim()
- if err != nil {
- return nil, err
- }
- res = append(res, trimClause)
- s.appendTmpl(trimClause.Finish(name))
- case model.FOR:
- forClause, err := s.parseFor(name)
- _, _ = forClause, err
- if err != nil {
- return nil, err
- }
- res = append(res, forClause)
- s.appendTmpl(forClause.Finish())
- case model.END:
- default:
- return nil, fmt.Errorf("unknow clause:%s", c.Value)
- }
- if !s.HasMore() {
- break
- }
- c = s.next()
- }
- return res, nil
-}
-
-// parseIF parse if clause
-func (s *Section) parseIF(name string) (res IfClause, err error) {
- c := s.current()
- res.slice = c
-
- s.appendTmpl(res.Create())
- if !s.HasMore() {
- return
- }
- c = s.next()
- for {
- switch c.Type {
- case model.SQL, model.DATA, model.VARIABLE:
- sqlClause := s.parseSQL(name)
- res.Value = append(res.Value, sqlClause)
- s.appendTmpl(sqlClause.Finish())
- case model.IF:
- var ifClause IfClause
- ifClause, err = s.parseIF(name)
- if err != nil {
- return
- }
- res.Value = append(res.Value, ifClause)
- s.appendTmpl(ifClause.Finish())
- case model.WHERE:
- var whereClause WhereClause
- whereClause, err = s.parseWhere()
- if err != nil {
- return
- }
- res.Value = append(res.Value, whereClause)
- s.appendTmpl(whereClause.Finish(name))
- case model.SET:
- var setClause SetClause
- setClause, err = s.parseSet()
- if err != nil {
- return
- }
- res.Value = append(res.Value, setClause)
- s.appendTmpl(setClause.Finish(name))
- case model.ELSE:
- var elseClause ElseClause
- elseClause, err = s.parseElSE(name)
- if err != nil {
- return
- }
- res.Value = append(res.Value, elseClause)
- case model.FOR:
- var forClause ForClause
- forClause, err = s.parseFor(name)
- if err != nil {
- return
- }
- res.Value = append(res.Value, forClause)
- s.appendTmpl(res.Finish())
- case model.TRIM:
- var trimClause TrimClause
- trimClause, err = s.parseTrim()
- if err != nil {
- return
- }
- res.Value = append(res.Value, trimClause)
- s.appendTmpl(trimClause.Finish(name))
- case model.END:
- return
- default:
- err = fmt.Errorf("unknow clause : %s", c.Value)
- return
- }
- if !s.HasMore() {
- break
- }
- c = s.next()
- }
- if c.isEnd() {
- err = fmt.Errorf("incomplete SQL,if not end")
- }
- return
-}
-
-// parseElSE parse else clause, the clause' type must be one of if, where, set, SQL condition
-func (s *Section) parseElSE(name string) (res ElseClause, err error) {
- res.slice = s.current()
- s.appendTmpl(res.Create())
-
- if !s.HasMore() {
- return
- }
- c := s.next()
- for {
- switch c.Type {
- case model.SQL, model.DATA, model.VARIABLE:
- sqlClause := s.parseSQL(name)
- res.Value = append(res.Value, sqlClause)
- s.appendTmpl(sqlClause.Create())
- case model.IF:
- var ifClause IfClause
- ifClause, err = s.parseIF(name)
- if err != nil {
- return
- }
- res.Value = append(res.Value, ifClause)
- s.appendTmpl(ifClause.Finish())
- case model.WHERE:
- var whereClause WhereClause
- whereClause, err = s.parseWhere()
- if err != nil {
- return
- }
- res.Value = append(res.Value, whereClause)
- s.appendTmpl(whereClause.Finish(name))
- case model.SET:
- var setClause SetClause
- setClause, err = s.parseSet()
- if err != nil {
- return
- }
- res.Value = append(res.Value, setClause)
- s.appendTmpl(setClause.Finish(name))
- case model.ELSE:
- var elseClause ElseClause
- elseClause, err = s.parseElSE(name)
- if err != nil {
- return
- }
- res.Value = append(res.Value, elseClause)
- case model.FOR:
- var forClause ForClause
- forClause, err = s.parseFor(name)
- if err != nil {
- return
- }
- res.Value = append(res.Value, forClause)
- s.appendTmpl(forClause.Finish())
- case model.TRIM:
- var trimClause TrimClause
- trimClause, err = s.parseTrim()
- if err != nil {
- return
- }
- res.Value = append(res.Value, trimClause)
- s.appendTmpl(trimClause.Finish(name))
- default:
- s.SubIndex()
- return
- }
- if !s.HasMore() {
- break
- }
- c = s.next()
- }
- return
-}
-
-// parseWhere parse where clause, the clause' type must be one of if, SQL condition
-func (s *Section) parseWhere() (res WhereClause, err error) {
- c := s.current()
- res.VarName = s.GetName(c.Type)
- s.appendTmpl(res.Create())
- res.Type = c.Type
-
- if !s.HasMore() {
- return
- }
- c = s.next()
- for {
- switch c.Type {
- case model.SQL, model.DATA, model.VARIABLE:
- sqlClause := s.parseSQL(res.VarName)
- res.Value = append(res.Value, sqlClause)
- s.appendTmpl(sqlClause.Finish())
- case model.IF:
- var ifClause IfClause
- ifClause, err = s.parseIF(res.VarName)
- if err != nil {
- return
- }
- res.Value = append(res.Value, ifClause)
- s.appendTmpl(ifClause.Finish())
- case model.FOR:
- var forClause ForClause
- forClause, err = s.parseFor(res.VarName)
- if err != nil {
- return
- }
- res.Value = append(res.Value, forClause)
- s.appendTmpl(forClause.Finish())
- case model.WHERE:
- var whereClause WhereClause
- whereClause, err = s.parseWhere()
- if err != nil {
- return
- }
- res.Value = append(res.Value, whereClause)
- s.appendTmpl(whereClause.Finish(res.VarName))
- case model.TRIM:
- var trimClause TrimClause
- trimClause, err = s.parseTrim()
- if err != nil {
- return
- }
- res.Value = append(res.Value, trimClause)
- s.appendTmpl(trimClause.Finish(res.VarName))
- case model.END:
- return
- default:
- err = fmt.Errorf("unknow clause : %s", c.Value)
- return
- }
- if !s.HasMore() {
- break
- }
- c = s.next()
- }
- if c.isEnd() {
- return
- }
- err = fmt.Errorf("incomplete SQL,where not end")
- return
-}
-
-// parseSet parse set clause, the clause' type must be one of if, SQL condition
-func (s *Section) parseSet() (res SetClause, err error) {
- c := s.current()
- res.VarName = s.GetName(c.Type)
- s.appendTmpl(res.Create())
- if !s.HasMore() {
- return
- }
- c = s.next()
-
- res.Type = c.Type
- for {
- switch c.Type {
- case model.SQL, model.DATA, model.VARIABLE:
- sqlClause := s.parseSQL(res.VarName)
- res.Value = append(res.Value, sqlClause)
- s.appendTmpl(sqlClause.Finish())
- case model.IF:
- var ifClause IfClause
- ifClause, err = s.parseIF(res.VarName)
- if err != nil {
- return
- }
- res.Value = append(res.Value, ifClause)
- s.appendTmpl(ifClause.Finish())
- case model.FOR:
- var forClause ForClause
- forClause, err = s.parseFor(res.VarName)
- if err != nil {
- return
- }
- res.Value = append(res.Value, forClause)
- s.appendTmpl(forClause.Finish())
- case model.WHERE:
- var whereClause WhereClause
- whereClause, err = s.parseWhere()
- if err != nil {
- return
- }
- res.Value = append(res.Value, whereClause)
- s.appendTmpl(whereClause.Finish(res.VarName))
- case model.TRIM:
- var trimClause TrimClause
- trimClause, err = s.parseTrim()
- if err != nil {
- return
- }
- res.Value = append(res.Value, trimClause)
- s.appendTmpl(trimClause.Finish(res.VarName))
- case model.END:
- return
- default:
- err = fmt.Errorf("unknow clause : %s", c.Value)
- return
- }
- if !s.HasMore() {
- break
- }
- c = s.next()
- }
- if c.isEnd() {
- err = fmt.Errorf("incomplete SQL,set not end")
- }
- return
-}
-
-// parseTrim parse set clause, the clause' type must be one of if, SQL condition
-func (s *Section) parseTrim() (res TrimClause, err error) {
- c := s.current()
- res.VarName = s.GetName(c.Type)
- s.appendTmpl(res.Create())
- if !s.HasMore() {
- return
- }
- c = s.next()
-
- res.Type = c.Type
- for {
- switch c.Type {
- case model.SQL, model.DATA, model.VARIABLE:
- sqlClause := s.parseSQL(res.VarName)
- res.Value = append(res.Value, sqlClause)
- s.appendTmpl(sqlClause.Finish())
- case model.IF:
- var ifClause IfClause
- ifClause, err = s.parseIF(res.VarName)
- if err != nil {
- return
- }
- res.Value = append(res.Value, ifClause)
- s.appendTmpl(ifClause.Finish())
- case model.FOR:
- var forClause ForClause
- forClause, err = s.parseFor(res.VarName)
- if err != nil {
- return
- }
- res.Value = append(res.Value, forClause)
- s.appendTmpl(forClause.Finish())
- case model.WHERE:
- var whereClause WhereClause
- whereClause, err = s.parseWhere()
- if err != nil {
- return
- }
- res.Value = append(res.Value, whereClause)
- s.appendTmpl(whereClause.Finish(res.VarName))
- case model.END:
- return
- default:
- err = fmt.Errorf("unknow clause : %s", c.Value)
- return
- }
- if !s.HasMore() {
- break
- }
- c = s.next()
- }
- if c.isEnd() {
- err = fmt.Errorf("incomplete SQL,set not end")
- }
- return
-}
-
-func (s *Section) parseFor(name string) (res ForClause, err error) {
- c := s.current()
- res.forSlice = c
- s.appendTmpl(res.Create())
- s.forValue = append(s.forValue, res.forSlice.ForRange)
-
- if !s.HasMore() {
- return
- }
- c = s.next()
- for {
- switch c.Type {
- case model.SQL, model.DATA, model.VARIABLE:
- strClause := s.parseSQL(name)
- res.Value = append(res.Value, strClause)
- s.appendTmpl(fmt.Sprintf("%s.WriteString(%s)", name, strClause.String()))
- case model.IF:
- var ifClause IfClause
- ifClause, err = s.parseIF(name)
- if err != nil {
- return
- }
- res.Value = append(res.Value, ifClause)
- s.appendTmpl(ifClause.Finish())
- case model.FOR:
- var forClause ForClause
- forClause, err = s.parseFor(name)
- if err != nil {
- return
- }
- res.Value = append(res.Value, forClause)
- s.appendTmpl(forClause.Finish())
- case model.TRIM:
- var trimClause TrimClause
- trimClause, err = s.parseTrim()
- if err != nil {
- return
- }
- res.Value = append(res.Value, trimClause)
- s.appendTmpl(trimClause.Finish(name))
- case model.END:
- s.forValue = s.forValue[:len(s.forValue)-1]
- return
- default:
- err = fmt.Errorf("unknow clause : %s", c.Value)
- return
- }
- if !s.HasMore() {
- break
- }
- c = s.next()
- }
- if c.isEnd() {
- err = fmt.Errorf("incomplete SQL,set not end")
- }
- return
-}
-
-// parseSQL parse sql condition, the clause' type must be one of SQL condition, VARIABLE, Data
-func (s *Section) parseSQL(name string) (res SQLClause) {
- res.VarName = name
- res.Type = model.SQL
- for {
- c := s.current()
- switch c.Type {
- case model.SQL:
- res.Value = append(res.Value, c.Value)
- case model.VARIABLE:
- res.Value = append(res.Value, c.Value)
- case model.DATA:
- s.appendTmpl(fmt.Sprintf("params = append(params,%s)", c.Value))
- res.Value = append(res.Value, "\"?\"")
- default:
- s.SubIndex()
- return
- }
- if !s.HasMore() {
- return
- }
- c = s.next()
- }
-}
-
-// checkSQLVar check sql variable by for loops value and external params
-func (s *Section) checkSQLVar(param string, status model.Status, method *InterfaceMethod) (result section, err error) {
- if status == model.VARIABLE && param == "table" {
- result = section{
- Type: model.SQL,
- Value: strconv.Quote(method.Table),
- }
- return
- }
- if status == model.DATA {
- method.HasForParams = true
- }
- if status == model.VARIABLE {
- param = fmt.Sprintf("%s.Quote(%s)", method.S, param)
- }
- result = section{
- Type: status,
- Value: param,
- }
- return
-}
-
-// GetName ...
-func (s *Section) GetName(status model.Status) string {
- switch status {
- case model.WHERE:
- defer func() { s.ClauseTotal[model.WHERE]++ }()
- return fmt.Sprintf("whereSQL%d", s.ClauseTotal[model.WHERE])
- case model.SET:
- defer func() { s.ClauseTotal[model.SET]++ }()
- return fmt.Sprintf("setSQL%d", s.ClauseTotal[model.SET])
- case model.TRIM:
- defer func() { s.ClauseTotal[model.TRIM]++ }()
- return fmt.Sprintf("trimSQL%d", s.ClauseTotal[model.TRIM])
- default:
- return "generateSQL"
- }
-}
-
-// checkTemplate check sql template's syntax (if/else/where/set/for)
-func (s *Section) checkTemplate(tmpl string) (part section, err error) {
- part.Value = tmpl
- part.SQLSlice = s
- part.splitTemplate()
-
- err = part.checkTemplate()
-
- return
-}
-
-type section struct {
- Type model.Status
- Value string
- ForRange ForRange
- SQLSlice *Section
- splitList []string
-}
-
-func (s *section) isEnd() bool {
- return s.Type == model.END
-}
-
-func (s *section) String() string {
- if s.Type == model.FOR {
- return s.ForRange.String()
- }
- return s.Value
-}
-
-func (s *section) splitTemplate() {
- s.splitList = strings.FieldsFunc(strings.TrimSpace(s.Value), func(r rune) bool {
- return r == ':' || r == ' ' || r == '=' || r == ','
- })
-}
-
-func (s *section) checkTemplate() error {
- if len(s.splitList) == 0 {
- return fmt.Errorf("template is null")
- }
- if model.GenKeywords.Contain(s.Value) {
- return fmt.Errorf("template can not use gen keywords")
- }
-
- err := s.sectionType(s.splitList[0])
- if err != nil {
- return err
- }
-
- if s.Type == model.FOR {
- if len(s.splitList) != 5 {
- return fmt.Errorf("for range syntax error: %s", s.Value)
- }
- if s.SQLSlice.hasSameName(s.splitList[2]) {
- return fmt.Errorf("cannot use the same value name in different for loops")
- }
- s.ForRange.index = s.splitList[1]
- s.ForRange.value = s.splitList[2]
- s.ForRange.rangeList = s.splitList[4]
- }
- return nil
-}
-
-func (s *section) sectionType(str string) error {
- switch str {
- case "if":
- s.Type = model.IF
- case "else":
- s.Type = model.ELSE
- case "for":
- s.Type = model.FOR
- case "where":
- s.Type = model.WHERE
- case "set":
- s.Type = model.SET
- case "end":
- s.Type = model.END
- case "trim":
- s.Type = model.TRIM
- default:
- return fmt.Errorf("unknown syntax: %s", str)
- }
- return nil
-}
-
-func (s *section) SQLParamName() string {
- return strings.Replace(s.Value, ".", "", -1)
-}
-
-// ForRange for range clause for diy method
-type ForRange struct {
- index string
- value string
- suffix string
- rangeList string
-}
-
-func (f *ForRange) String() string {
- return fmt.Sprintf("for %s, %s := range %s", f.index, f.value, f.rangeList)
-}
diff --git a/toolkit/gormgen/internal/generate/table.go b/toolkit/gormgen/internal/generate/table.go
deleted file mode 100644
index 2e9bee37..00000000
--- a/toolkit/gormgen/internal/generate/table.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package generate
-
-import (
- "context"
- "errors"
-
- "github.com/wubin1989/gorm"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/model"
-)
-
-// ITableInfo table info interface
-type ITableInfo interface {
- GetTableColumns(schemaName string, tableName string) (result []*model.Column, err error)
-
- GetTableIndex(schemaName string, tableName string) (indexes []gorm.Index, err error)
-}
-
-func getTableInfo(db *gorm.DB) ITableInfo {
- return &tableInfo{db}
-}
-
-func getTableColumns(db *gorm.DB, schemaName string, tableName string, indexTag bool) (result []*model.Column, err error) {
- if db == nil {
- return nil, errors.New("gorm db is nil")
- }
-
- mt := getTableInfo(db)
- result, err = mt.GetTableColumns(schemaName, tableName)
- if err != nil {
- return nil, err
- }
- if !indexTag || len(result) == 0 {
- return result, nil
- }
-
- index, err := mt.GetTableIndex(schemaName, tableName)
- if err != nil { //ignore find index err
- db.Logger.Warn(context.Background(), "GetTableIndex for %s,err=%s", tableName, err.Error())
- return result, nil
- }
- if len(index) == 0 {
- return result, nil
- }
-
- im := model.GroupByColumn(index)
- for _, c := range result {
- c.Indexes = im[c.Name()]
- }
- return result, nil
-}
-
-type tableInfo struct{ *gorm.DB }
-
-// GetTableColumns struct
-func (t *tableInfo) GetTableColumns(schemaName string, tableName string) (result []*model.Column, err error) {
- types, err := t.Migrator().ColumnTypes(tableName)
- if err != nil {
- return nil, err
- }
- for _, column := range types {
- result = append(result, &model.Column{ColumnType: column, TableName: tableName, UseScanType: t.Dialector.Name() != "mysql" && t.Dialector.Name() != "sqlite"})
- }
- return result, nil
-}
-
-// GetTableIndex index
-func (t *tableInfo) GetTableIndex(schemaName string, tableName string) (indexes []gorm.Index, err error) {
- return t.Migrator().GetIndexes(tableName)
-}
diff --git a/toolkit/gormgen/internal/generate/test.go b/toolkit/gormgen/internal/generate/test.go
deleted file mode 100644
index 0152a675..00000000
--- a/toolkit/gormgen/internal/generate/test.go
+++ /dev/null
@@ -1,54 +0,0 @@
-package generate
-
-import (
- "fmt"
- "strconv"
- "strings"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/internal/parser"
-)
-
-// GetTestParamInTmpl return param list
-func (m *InterfaceMethod) GetTestParamInTmpl() string {
- return testParamToString(m.Params)
-}
-
-// GetTestResultParamInTmpl return result list
-func (m *InterfaceMethod) GetTestResultParamInTmpl() string {
- var res []string
- for i := range m.Result {
- tmplString := fmt.Sprintf("res%d", i+1)
- res = append(res, tmplString)
- }
- return strings.Join(res, ",")
-}
-
-// testParamToString param list to string used in tmpl
-func testParamToString(params []parser.Param) string {
- var res []string
- for i, param := range params {
- // TODO manage array and pointer
- typ := param.Type
- if param.Package != "" {
- typ = param.Package + "." + typ
- }
- if param.IsArray {
- typ = "[]" + typ
- }
- if param.IsPointer {
- typ = "*" + typ
- }
- res = append(res, fmt.Sprintf("tt.Input.Args[%d].(%s)", i, typ))
- }
- return strings.Join(res, ",")
-}
-
-// GetAssertInTmpl assert in diy test
-func (m *InterfaceMethod) GetAssertInTmpl() string {
- var res []string
- for i := range m.Result {
- tmplString := fmt.Sprintf("assert(t, %v, res%d, tt.Expectation.Ret[%d])", strconv.Quote(m.MethodName), i+1, i)
- res = append(res, tmplString)
- }
- return strings.Join(res, "\n")
-}
diff --git a/toolkit/gormgen/internal/generate/utils.go b/toolkit/gormgen/internal/generate/utils.go
deleted file mode 100644
index 8832d43e..00000000
--- a/toolkit/gormgen/internal/generate/utils.go
+++ /dev/null
@@ -1,61 +0,0 @@
-package generate
-
-import (
- "strings"
-)
-
-func isCapitalize(s string) bool {
- if len(s) < 1 {
- return false
- }
- b := s[0]
- if b >= 'A' && b <= 'Z' {
- return true
- }
- return false
-}
-
-func isEnd(b byte) bool {
- switch {
- case b >= 'a' && b <= 'z':
- return false
- case b >= 'A' && b <= 'Z':
- return false
- case b >= '0' && b <= '9':
- return false
- case b == '-' || b == '_' || b == '.':
- return false
- default:
- return true
- }
-}
-
-func getPackageName(fullName string) string {
- return strings.Split(delPointerSym(fullName), ".")[0]
-}
-
-func strOutRange(index int, str string) bool {
- return index >= len(str)
-}
-
-func delPointerSym(name string) string {
- return strings.TrimLeft(name, "*")
-}
-
-func getPureName(s string) string {
- return string(strings.ToLower(delPointerSym(s))[0])
-}
-
-// not need capitalize
-func getStructName(t string) string {
- list := strings.Split(t, ".")
- return list[len(list)-1]
-}
-
-func uncaptialize(s string) string {
- if s == "" {
- return ""
- }
-
- return strings.ToLower(s[:1]) + s[1:]
-}
diff --git a/toolkit/gormgen/internal/model/base.go b/toolkit/gormgen/internal/model/base.go
deleted file mode 100644
index 72bc43b4..00000000
--- a/toolkit/gormgen/internal/model/base.go
+++ /dev/null
@@ -1,267 +0,0 @@
-package model
-
-import (
- "bytes"
- "github.com/sirupsen/logrus"
- "strings"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
-)
-
-const (
- // DefaultModelPkg ...
- DefaultModelPkg = "model"
- // DefaultDtoPkg ...
- DefaultDtoPkg = "dto"
-)
-
-// Status sql status
-type Status int
-
-const (
- // UNKNOWN ...
- UNKNOWN Status = iota
- // SQL ...
- SQL
- // DATA ...
- DATA
- // VARIABLE ...
- VARIABLE
- // IF ...
- IF
- // ELSE ...
- ELSE
- // WHERE ...
- WHERE
- // SET ...
- SET
- // FOR ...
- FOR
- // END ...
- END
- // TRIM ...
- TRIM
-)
-
-// SourceCode source code
-type SourceCode int
-
-const (
- // Struct ...
- Struct SourceCode = iota
- // Table ...
- Table
- // Object ...
- Object
-)
-
-// GormKeywords ...
-var GormKeywords = KeyWord{
- words: []string{
- "UnderlyingDB", "UseDB", "UseModel", "UseTable", "Quote", "Debug", "TableName", "WithContext",
- "As", "Not", "Or", "Build", "Columns", "Hints",
- "Distinct", "Omit",
- "Select", "Where", "Order", "Group", "Having", "Limit", "Offset",
- "Join", "LeftJoin", "RightJoin",
- "Save", "Create", "CreateInBatches",
- "Update", "Updates", "UpdateColumn", "UpdateColumns",
- "Find", "FindInBatches", "First", "Take", "Last", "Pluck", "Count",
- "Scan", "ScanRows", "Row", "Rows",
- "Delete", "Unscoped",
- "Scopes",
- },
-}
-
-// DOKeywords ...
-var DOKeywords = KeyWord{
- words: []string{
- "Alias", "TableName", "WithContext",
- },
-}
-
-// GenKeywords ...
-var GenKeywords = KeyWord{
- words: []string{
- "generateSQL", "whereClause", "setClause",
- },
-}
-
-// KeyWord ...
-type KeyWord struct {
- words []string
-}
-
-// FullMatch full match
-func (g *KeyWord) FullMatch(word string) bool {
- for _, item := range g.words {
- if word == item {
- return true
- }
- }
- return false
-}
-
-// Contain contain
-func (g *KeyWord) Contain(text string) bool {
- for _, item := range g.words {
- if strings.Contains(text, item) {
- return true
- }
- }
- return false
-}
-
-var (
- defaultDataType = "string"
- dataType dataTypeMap = map[string]dataTypeMapping{
- "numeric": func(string) string { return "int32" },
- "integer": func(string) string { return "int32" },
- "int": func(string) string { return "int32" },
- "smallint": func(string) string { return "int32" },
- "mediumint": func(string) string { return "int32" },
- "bigint": func(string) string { return "int64" },
- "float": func(string) string { return "float32" },
- "real": func(string) string { return "float64" },
- "double": func(string) string { return "float64" },
- "decimal": func(string) string { return "float64" },
- "char": func(string) string { return "string" },
- "varchar": func(string) string { return "string" },
- "tinytext": func(string) string { return "string" },
- "mediumtext": func(string) string { return "string" },
- "longtext": func(string) string { return "string" },
- "bytea": func(string) string { return "[]byte" },
- "binary": func(string) string { return "[]byte" },
- "varbinary": func(string) string { return "[]byte" },
- "tinyblob": func(string) string { return "[]byte" },
- "blob": func(string) string { return "[]byte" },
- "mediumblob": func(string) string { return "[]byte" },
- "longblob": func(string) string { return "[]byte" },
- "text": func(string) string { return "string" },
- "json": func(string) string { return "string" },
- "enum": func(string) string { return "string" },
- "time": func(string) string { return "time.Time" },
- "date": func(string) string { return "time.Time" },
- "datetime": func(string) string { return "time.Time" },
- "timestamp": func(string) string { return "time.Time" },
- "year": func(string) string { return "int32" },
- "bit": func(string) string { return "[]uint8" },
- "boolean": func(string) string { return "bool" },
- "tinyint": func(detailType string) string {
- if strings.HasPrefix(strings.TrimSpace(detailType), "tinyint(1)") {
- return "bool"
- }
- return "int32"
- },
- }
-)
-
-type dataTypeMapping func(detailType string) (finalType string)
-
-type dataTypeMap map[string]dataTypeMapping
-
-func (m dataTypeMap) Get(dataType, detailType string) string {
- if convert, ok := m[strings.ToLower(dataType)]; ok {
- return convert(detailType)
- }
- d := detailType
- if strings.Contains(detailType, " ") {
- d = detailType[:strings.Index(detailType, " ")]
- }
- if convert, ok := m[strings.ToLower(d)]; ok {
- return convert(detailType)
- }
- logrus.Info(dataType+" ===> ", detailType)
- return defaultDataType
-}
-
-// Field user input structures
-type Field struct {
- Name string
- Type string
- ColumnName string
- ColumnComment string
- MultilineComment bool
- Tag field.Tag
- GORMTag field.GormTag
- CustomGenType string
- Relation *field.Relation
- PriKey bool
-}
-
-// Tags ...
-func (m *Field) Tags() string {
- if _, ok := m.Tag[field.TagKeyGorm]; ok {
- return m.Tag.Build()
- }
-
- if gormTag := strings.TrimSpace(m.GORMTag.Build()); gormTag != "" {
- m.Tag.Set(field.TagKeyGorm, gormTag)
- }
- return m.Tag.Build()
-}
-
-// IsRelation ...
-func (m *Field) IsRelation() bool { return m.Relation != nil }
-
-// GenType ...
-func (m *Field) GenType() string {
- if m.IsRelation() {
- return m.Type
- }
- if m.CustomGenType != "" {
- return m.CustomGenType
- }
- typ := strings.TrimLeft(m.Type, "*")
- switch typ {
- case "string", "bytes":
- return strings.Title(typ)
- case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64":
- return strings.Title(typ)
- case "float64", "float32":
- return strings.Title(typ)
- case "bool":
- return strings.Title(typ)
- case "time.Time":
- return "Time"
- case "json.RawMessage", "[]byte":
- return "Bytes"
- case "serializer":
- return "Serializer"
- default:
- return "Field"
- }
-}
-
-// EscapeKeyword escape keyword
-func (m *Field) EscapeKeyword() *Field {
- return m.EscapeKeywordFor(GormKeywords)
-}
-
-// EscapeKeywordFor escape for specified keyword
-func (m *Field) EscapeKeywordFor(keywords KeyWord) *Field {
- if keywords.FullMatch(m.Name) {
- m.Name += "_"
- }
- return m
-}
-
-// SQLBuffer sql buffer
-type SQLBuffer struct{ bytes.Buffer }
-
-// WriteSQL ...
-func (s *SQLBuffer) WriteSQL(b byte) {
- switch b {
- case '\n', '\t', ' ':
- if s.Len() == 0 || s.Bytes()[s.Len()-1] != ' ' {
- _ = s.WriteByte(' ')
- }
- default:
- _ = s.WriteByte(b)
- }
-}
-
-// Dump ...
-func (s *SQLBuffer) Dump() string {
- defer s.Reset()
- return s.String()
-}
diff --git a/toolkit/gormgen/internal/model/config.go b/toolkit/gormgen/internal/model/config.go
deleted file mode 100644
index a65be1de..00000000
--- a/toolkit/gormgen/internal/model/config.go
+++ /dev/null
@@ -1,115 +0,0 @@
-package model
-
-import (
- "path/filepath"
- "strings"
-
- "github.com/wubin1989/gorm"
-)
-
-// Config model configuration
-type Config struct {
- ModelPkg string
- TablePrefix string
- TableName string
- ModelName string
-
- ImportPkgPaths []string
- ModelOpts []Option
-
- NameStrategy
- FieldConfig
- MethodConfig
-}
-
-// NameStrategy name strategy
-type NameStrategy struct {
- SchemaNameOpts []SchemaNameOpt
-
- TableNameNS func(tableName string) string
- ModelNameNS func(tableName string) string
- FileNameNS func(tableName string) string
-}
-
-// FieldConfig field configuration
-type FieldConfig struct {
- DataTypeMap map[string]func(columnType gorm.ColumnType) (dataType string)
-
- FieldNullable bool // generate pointer when field is nullable
- FieldCoverable bool // generate pointer when field has default value
- FieldSignable bool // detect integer field's unsigned type, adjust generated data type
- FieldWithIndexTag bool // generate with gorm index tag
- FieldWithTypeTag bool // generate with gorm column type tag
-
- FieldJSONTagNS func(columnName string, columnType string) string
-
- ModifyOpts []FieldOption
- FilterOpts []FieldOption
- CreateOpts []FieldOption
-}
-
-// MethodConfig method configuration
-type MethodConfig struct {
- MethodOpts []MethodOption
-}
-
-// Preprocess revise invalid field
-func (cfg *Config) Preprocess() *Config {
- if cfg.ModelPkg == "" {
- cfg.ModelPkg = DefaultModelPkg
- }
- cfg.ModelPkg = filepath.Base(cfg.ModelPkg)
-
- cfg.ModifyOpts, cfg.FilterOpts, cfg.CreateOpts, cfg.MethodOpts = sortOptions(cfg.ModelOpts)
-
- return cfg
-}
-
-// GetNames get names
-func (cfg *Config) GetNames() (tableName, structName, fileName string) {
- tableName, structName = cfg.TableName, cfg.ModelName
-
- if cfg.ModelNameNS != nil {
- structName = cfg.ModelNameNS(tableName)
- }
-
- if cfg.TableNameNS != nil {
- tableName = cfg.TableNameNS(tableName)
- }
- if !strings.HasPrefix(tableName, cfg.TablePrefix) {
- tableName = cfg.TablePrefix + tableName
- }
-
- fileName = strings.ToLower(tableName)
- if cfg.FileNameNS != nil {
- fileName = cfg.FileNameNS(cfg.TableName)
- }
-
- return
-}
-
-// GetModelMethods get diy method from option
-func (cfg *Config) GetModelMethods() (methods []interface{}) {
- if cfg == nil {
- return
- }
-
- for _, opt := range cfg.MethodOpts {
- methods = append(methods, opt.Methods()...)
- }
- return
-}
-
-// GetSchemaName get schema name
-func (cfg *Config) GetSchemaName(db *gorm.DB) string {
- if cfg == nil {
- return ""
- }
-
- for _, opt := range cfg.SchemaNameOpts {
- if name := opt(db); name != "" {
- return name
- }
- }
- return ""
-}
diff --git a/toolkit/gormgen/internal/model/options.go b/toolkit/gormgen/internal/model/options.go
deleted file mode 100644
index 466b1e50..00000000
--- a/toolkit/gormgen/internal/model/options.go
+++ /dev/null
@@ -1,89 +0,0 @@
-package model
-
-import (
- "github.com/wubin1989/gorm"
-)
-
-// SchemaNameOpt schema name option
-type SchemaNameOpt func(*gorm.DB) string
-
-var defaultSchemaNameOpt = SchemaNameOpt(func(db *gorm.DB) string { return db.Migrator().CurrentDatabase() })
-
-// Option field option
-type Option interface{ OptionType() string }
-
-const fieldType = "field"
-
-// FieldOption ...
-type FieldOption interface {
- Option
- Operator() func(*Field) *Field
-}
-
-const methodType = "method"
-
-// MethodOption ...
-type MethodOption interface {
- Option
- Methods() (methods []interface{})
-}
-
-var (
- _ Option = ModifyFieldOpt(nil)
- _ Option = FilterFieldOpt(nil)
- _ Option = CreateFieldOpt(nil)
-
- _ Option = AddMethodOpt(nil)
-)
-
-// ModifyFieldOpt modify field option
-type ModifyFieldOpt func(*Field) *Field
-
-// OptionType implement for interface Option
-func (ModifyFieldOpt) OptionType() string { return fieldType }
-
-// Operator implement for FieldOpt
-func (o ModifyFieldOpt) Operator() func(*Field) *Field { return o }
-
-// FilterFieldOpt filter field option
-type FilterFieldOpt ModifyFieldOpt
-
-// OptionType implement for interface Option
-func (FilterFieldOpt) OptionType() string { return fieldType }
-
-// Operator implement for FieldOpt
-func (o FilterFieldOpt) Operator() func(*Field) *Field { return o }
-
-// CreateFieldOpt create field option
-type CreateFieldOpt ModifyFieldOpt
-
-// OptionType implement for interface Option
-func (CreateFieldOpt) OptionType() string { return fieldType }
-
-// Operator implement for FieldOpt
-func (o CreateFieldOpt) Operator() func(*Field) *Field { return o }
-
-// AddMethodOpt diy method option
-type AddMethodOpt func() (methods []interface{})
-
-// OptionType implement for interface Option
-func (AddMethodOpt) OptionType() string { return methodType }
-
-// Methods ...
-func (o AddMethodOpt) Methods() []interface{} { return o() }
-
-func sortOptions(opts []Option) (modifyOpts []FieldOption, filterOpts []FieldOption, createOpts []FieldOption, methodOpt []MethodOption) {
- for _, opt := range opts {
- switch opt := opt.(type) {
- case ModifyFieldOpt:
- modifyOpts = append(modifyOpts, opt)
- case FilterFieldOpt:
- filterOpts = append(filterOpts, opt)
- case CreateFieldOpt:
- createOpts = append(createOpts, opt)
- case AddMethodOpt:
- methodOpt = append(methodOpt, opt)
- }
- }
- return
-}
diff --git a/toolkit/gormgen/internal/model/tbl_column.go b/toolkit/gormgen/internal/model/tbl_column.go
deleted file mode 100644
index a2aec330..00000000
--- a/toolkit/gormgen/internal/model/tbl_column.go
+++ /dev/null
@@ -1,166 +0,0 @@
-package model
-
-import (
- "fmt"
- "reflect"
- "strings"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen/field"
- "github.com/wubin1989/gorm"
-)
-
-// Column table column's info
-type Column struct {
- gorm.ColumnType
- TableName string `gorm:"column:TABLE_NAME"`
- Indexes []*Index `gorm:"-"`
- UseScanType bool `gorm:"-"`
- dataTypeMap map[string]func(columnType gorm.ColumnType) (dataType string) `gorm:"-"`
- jsonTagNS func(columnName string, columnType string) string `gorm:"-"`
-}
-
-// SetDataTypeMap set data type map
-func (c *Column) SetDataTypeMap(m map[string]func(columnType gorm.ColumnType) (dataType string)) {
- c.dataTypeMap = m
-}
-
-// GetDataType get data type
-func (c *Column) GetDataType() (fieldtype string) {
- if mapping, ok := c.dataTypeMap[c.DatabaseTypeName()]; ok {
- return mapping(c.ColumnType)
- }
- if c.UseScanType && c.ScanType() != nil {
- return c.ScanType().String()
- }
- return dataType.Get(c.DatabaseTypeName(), c.columnType())
-}
-
-// WithNS with name strategy
-func (c *Column) WithNS(jsonTagNS func(columnName string, columnType string) string) {
- c.jsonTagNS = jsonTagNS
- if c.jsonTagNS == nil {
- c.jsonTagNS = func(n string, _ string) string { return n }
- }
-}
-
-// ToField convert to field
-func (c *Column) ToField(nullable, coverable, signable bool) *Field {
- fieldType := c.GetDataType()
- if signable && strings.Contains(c.columnType(), "unsigned") && strings.HasPrefix(fieldType, "int") {
- fieldType = "u" + fieldType
- }
- switch {
- case c.Name() == "deleted_at" && fieldType == "time.Time":
- fieldType = "gorm.DeletedAt"
- case coverable && c.needDefaultTag(c.defaultTagValue()):
- fieldType = "*" + fieldType
- case nullable:
- if n, ok := c.Nullable(); ok && n {
- fieldType = "*" + fieldType
- }
- }
-
- var comment string
- if c, ok := c.Comment(); ok {
- comment = c
- }
-
- isPriKey, ok := c.PrimaryKey()
-
- return &Field{
- Name: c.Name(),
- Type: fieldType,
- ColumnName: c.Name(),
- MultilineComment: c.multilineComment(),
- GORMTag: c.buildGormTag(),
- Tag: map[string]string{field.TagKeyJson: c.jsonTagNS(c.Name(), fieldType)},
- ColumnComment: comment,
- PriKey: ok && isPriKey,
- }
-}
-
-func (c *Column) multilineComment() bool {
- cm, ok := c.Comment()
- return ok && strings.Contains(cm, "\n")
-}
-
-func (c *Column) buildGormTag() field.GormTag {
- tag := field.GormTag{
- field.TagKeyGormColumn: []string{c.Name()},
- field.TagKeyGormType: []string{c.columnType()},
- }
- isPriKey, ok := c.PrimaryKey()
- isValidPriKey := ok && isPriKey
- if isValidPriKey {
- tag.Set(field.TagKeyGormPrimaryKey, "")
- if at, ok := c.AutoIncrement(); ok {
- tag.Set(field.TagKeyGormAutoIncrement, fmt.Sprintf("%t", at))
- }
- } else if n, ok := c.Nullable(); ok && !n {
- tag.Set(field.TagKeyGormNotNull, "")
- }
-
- for _, idx := range c.Indexes {
- if idx == nil {
- continue
- }
- if pk, _ := idx.PrimaryKey(); pk { //ignore PrimaryKey
- continue
- }
- if uniq, _ := idx.Unique(); uniq {
- tag.Append(field.TagKeyGormUniqueIndex, fmt.Sprintf("%s,priority:%d", idx.Name(), idx.Priority))
- } else {
- tag.Append(field.TagKeyGormIndex, fmt.Sprintf("%s,priority:%d", idx.Name(), idx.Priority))
- }
- }
-
- if dtValue := c.defaultTagValue(); c.needDefaultTag(dtValue) { // cannot set default tag for primary key
- tag.Set(field.TagKeyGormDefault, dtValue)
- }
- if comment, ok := c.Comment(); ok && comment != "" {
- if c.multilineComment() {
- comment = strings.ReplaceAll(comment, "\n", "\\n")
- }
- tag.Set(field.TagKeyGormComment, comment)
- }
- return tag
-}
-
-// needDefaultTag check if default tag needed
-func (c *Column) needDefaultTag(defaultTagValue string) bool {
- if defaultTagValue == "" {
- return false
- }
- if c.ScanType() != nil {
- switch c.ScanType().Kind() {
- case reflect.Bool:
- return defaultTagValue != "false"
- case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32, reflect.Float64:
- return defaultTagValue != "0"
- case reflect.String:
- return defaultTagValue != ""
- case reflect.Struct:
- return strings.Trim(defaultTagValue, "'0:- ") != ""
- }
- }
- return c.Name() != "created_at" && c.Name() != "updated_at"
-}
-
-// defaultTagValue return gorm default tag's value
-func (c *Column) defaultTagValue() string {
- value, ok := c.DefaultValue()
- if !ok {
- return ""
- }
- if value != "" && strings.TrimSpace(value) == "" {
- return "'" + value + "'"
- }
- return value
-}
-
-func (c *Column) columnType() (v string) {
- if cl, ok := c.ColumnType.ColumnType(); ok {
- return cl
- }
- return c.DatabaseTypeName()
-}
diff --git a/toolkit/gormgen/internal/model/tbl_index.go b/toolkit/gormgen/internal/model/tbl_index.go
deleted file mode 100644
index 36c6644c..00000000
--- a/toolkit/gormgen/internal/model/tbl_index.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package model
-
-import "github.com/wubin1989/gorm"
-
-// Index table index info
-type Index struct {
- gorm.Index
- Priority int32 `gorm:"column:SEQ_IN_INDEX"`
-}
-
-// GroupByColumn group columns
-func GroupByColumn(indexList []gorm.Index) map[string][]*Index {
- columnIndexMap := make(map[string][]*Index, len(indexList))
- if len(indexList) == 0 {
- return columnIndexMap
- }
-
- for _, idx := range indexList {
- if idx == nil {
- continue
- }
- for i, col := range idx.Columns() {
- columnIndexMap[col] = append(columnIndexMap[col], &Index{
- Index: idx,
- Priority: int32(i + 1),
- })
- }
- }
- return columnIndexMap
-}
diff --git a/toolkit/gormgen/internal/parser/export.go b/toolkit/gormgen/internal/parser/export.go
deleted file mode 100644
index 4753484a..00000000
--- a/toolkit/gormgen/internal/parser/export.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package parser
-
-import (
- "fmt"
- "go/build"
- "os"
- "path/filepath"
- "reflect"
- "runtime"
- "strings"
-)
-
-// InterfacePath interface path
-type InterfacePath struct {
- Name string
- FullName string
- Files []string
- Package string
-}
-
-// GetInterfacePath get interface's directory path and all files it contains
-func GetInterfacePath(v interface{}) (paths []*InterfacePath, err error) {
- value := reflect.ValueOf(v)
- if value.Kind() != reflect.Func {
- err = fmt.Errorf("model param is not function:%s", value.String())
- return
- }
-
- for i := 0; i < value.Type().NumIn(); i++ {
- var path InterfacePath
- arg := value.Type().In(i)
- path.FullName = arg.String()
-
- // keep the last model
- for _, n := range strings.Split(arg.String(), ".") {
- path.Name = n
- }
-
- ctx := build.Default
- var p *build.Package
-
- if strings.Split(arg.String(), ".")[0] == "main" {
- _, file, _, _ := runtime.Caller(3)
- p, err = ctx.ImportDir(filepath.Dir(file), build.ImportComment)
- } else {
- p, err = ctx.Import(arg.PkgPath(), "", build.ImportComment)
- }
-
- if err != nil {
- return
- }
-
- for _, file := range p.GoFiles {
- goFile := fmt.Sprintf("%s/%s", p.Dir, file)
- if fileExists(goFile) {
- path.Files = append(path.Files, goFile)
- }
- }
-
- if len(path.Files) == 0 {
- err = fmt.Errorf("interface file not found:%s", value.String())
- return
- }
-
- paths = append(paths, &path)
- }
-
- return
-}
-
-func fileExists(path string) bool {
- _, err := os.Stat(path)
- return err == nil
-}
-
-// GetModelMethod get diy methods
-func GetModelMethod(v interface{}, skip int) (method *DIYMethods, err error) {
- method = new(DIYMethods)
-
- // get diy method info by input value, must input a function or a struct
- value := reflect.ValueOf(v)
- switch value.Kind() {
- case reflect.Func:
- fullPath := runtime.FuncForPC(value.Pointer()).Name()
- err = method.parserPath(fullPath)
- if err != nil {
- return nil, err
- }
- case reflect.Struct:
- method.pkgPath = value.Type().PkgPath()
- method.BaseStructType = value.Type().Name()
- default:
- return nil, fmt.Errorf("method param must be a function or struct")
- }
-
- var p *build.Package
-
- // if struct in main file
- ctx := build.Default
- if method.pkgPath == "main" {
- _, file, _, _ := runtime.Caller(skip)
- p, err = ctx.ImportDir(filepath.Dir(file), build.ImportComment)
- } else {
- p, err = ctx.Import(method.pkgPath, "", build.ImportComment)
- }
- if err != nil {
- return nil, fmt.Errorf("diy method dir not found:%s.%s %w", method.pkgPath, method.MethodName, err)
- }
-
- for _, file := range p.GoFiles {
- goFile := p.Dir + "/" + file
- if fileExists(goFile) {
- method.pkgFiles = append(method.pkgFiles, goFile)
- }
- }
- if len(method.pkgFiles) == 0 {
- return nil, fmt.Errorf("diy method file not found:%s.%s", method.pkgPath, method.MethodName)
- }
-
- // read files got methods
- return method, method.LoadMethods()
-}
diff --git a/toolkit/gormgen/internal/parser/method.go b/toolkit/gormgen/internal/parser/method.go
deleted file mode 100644
index 0d37f0cc..00000000
--- a/toolkit/gormgen/internal/parser/method.go
+++ /dev/null
@@ -1,135 +0,0 @@
-package parser
-
-import (
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "io/ioutil"
- "strings"
-)
-
-// Method Apply to query struct and base struct custom method
-type Method struct {
- Receiver Param
- MethodName string
- Doc string
- Params []Param
- Result []Param
- Body string
-}
-
-// FuncSign function signature
-func (m Method) FuncSign() string {
- return fmt.Sprintf("%s(%s) (%s)", m.MethodName, m.GetParamInTmpl(), m.GetResultParamInTmpl())
-}
-
-// GetBaseStructTmpl return method bind info string
-func (m *Method) GetBaseStructTmpl() string {
- return m.Receiver.TmplString()
-}
-
-// GetParamInTmpl return param list
-func (m *Method) GetParamInTmpl() string {
- return paramToString(m.Params)
-}
-
-// GetResultParamInTmpl return result list
-func (m *Method) GetResultParamInTmpl() string {
- return paramToString(m.Result)
-}
-
-// paramToString param list to string used in tmpl
-func paramToString(params []Param) string {
- res := make([]string, len(params))
- for i, param := range params {
- res[i] = param.TmplString()
- }
- return strings.Join(res, ",")
-}
-
-// DocComment return comment sql add "//" every line
-func (m *Method) DocComment() string {
- return strings.Replace(strings.TrimSpace(m.Doc), "\n", "\n//", -1)
-}
-
-// DIYMethods user Custom methods bind to db base struct
-type DIYMethods struct {
- BaseStructType string
- MethodName string
- pkgPath string
- currentFile string
- pkgFiles []string
- Methods []*Method
-}
-
-func (m *DIYMethods) parserPath(path string) error {
- pathList := strings.Split(path, ".")
- if len(pathList) < 3 {
- return fmt.Errorf("parser diy method error")
- }
-
- m.pkgPath = strings.Join(pathList[:len(pathList)-2], ".")
- methodName := pathList[len(pathList)-1]
- m.MethodName = methodName[:len(methodName)-3]
-
- structName := pathList[len(pathList)-2]
- m.BaseStructType = strings.Trim(structName, "()*")
- return nil
-}
-
-// Visit ast visit function
-func (m *DIYMethods) Visit(n ast.Node) (w ast.Visitor) {
- switch t := n.(type) {
- case *ast.FuncDecl:
- // check base struct and method name is expect
- structMeta := getParamList(t.Recv)
- if len(structMeta) != 1 {
- return
- }
- if structMeta[0].Type != m.BaseStructType {
- return
- }
- // if m.MethodName is null will generate all methods
- if m.MethodName != "" && m.MethodName != t.Name.Name {
- return
- }
-
- // use ast read bind start package is UNDEFINED ,set it null string
- structMeta[0].Package = ""
- m.Methods = append(m.Methods, &Method{
- Receiver: structMeta[0],
- MethodName: t.Name.String(),
- Doc: t.Doc.Text(),
- Body: getBody(m.currentFile, int(t.Body.Pos()), int(t.Body.End())),
- Params: getParamList(t.Type.Params),
- Result: getParamList(t.Type.Results),
- })
- }
-
- return m
-}
-
-// read old file get method body
-func getBody(fileName string, start, end int) string {
- f1, err := ioutil.ReadFile(fileName)
- if err != nil {
- return "{}"
- }
-
- return string(f1[start-1 : end-1])
-}
-
-// LoadMethods ast read file get diy method
-func (m *DIYMethods) LoadMethods() error {
- for _, filename := range m.pkgFiles {
- f, err := parser.ParseFile(token.NewFileSet(), filename, nil, parser.ParseComments)
- if err != nil {
- return fmt.Errorf("can't parse file %q: %s", filename, err)
- }
- m.currentFile = filename
- ast.Walk(m, f)
- }
-
- return nil
-}
diff --git a/toolkit/gormgen/internal/parser/parser.go b/toolkit/gormgen/internal/parser/parser.go
deleted file mode 100644
index aec2d6f4..00000000
--- a/toolkit/gormgen/internal/parser/parser.go
+++ /dev/null
@@ -1,323 +0,0 @@
-package parser
-
-import (
- "fmt"
- "go/ast"
- "go/parser"
- "go/token"
- "log"
- "path"
- "path/filepath"
- "strconv"
- "strings"
-)
-
-// InterfaceSet ...
-type InterfaceSet struct {
- Interfaces []InterfaceInfo
- imports map[string]string // package name -> quoted "package path"
-}
-
-// InterfaceInfo ...
-type InterfaceInfo struct {
- Name string
- Doc string
- Methods []*Method
- Package string
- ApplyStruct []string
-}
-
-// MatchStruct ...
-func (i *InterfaceInfo) MatchStruct(name string) bool {
- for _, s := range i.ApplyStruct {
- if s == name {
- return true
- }
- }
- return false
-}
-
-// ParseFile get interface's info from source file
-func (i *InterfaceSet) ParseFile(paths []*InterfacePath, structNames []string) error {
- for _, path := range paths {
- for _, file := range path.Files {
- absFilePath, err := filepath.Abs(file)
- if err != nil {
- return fmt.Errorf("file not found: %s", file)
- }
-
- err = i.getInterfaceFromFile(absFilePath, path.Name, path.FullName, structNames)
- if err != nil {
- return fmt.Errorf("can't get interface from %s:%s", path.FullName, err)
- }
- }
- }
- return nil
-}
-
-// Visit ast visit function
-func (i *InterfaceSet) Visit(n ast.Node) (w ast.Visitor) {
- switch n := n.(type) {
- case *ast.ImportSpec:
- importName, _ := strconv.Unquote(n.Path.Value)
- importName = path.Base(importName)
- if n.Name != nil {
- name := n.Name.Name
- // ignore dummy imports
- // TODO: full support for dot imports requires type checking the whole package
- if name == "_" || name == "." {
- return i
- }
- importName = name
- }
- i.imports[importName] = n.Path.Value
- case *ast.TypeSpec:
- if data, ok := n.Type.(*ast.InterfaceType); ok {
- r := InterfaceInfo{
- Methods: []*Method{},
- }
- methods := data.Methods.List
- r.Name = n.Name.Name
- r.Doc = n.Doc.Text()
-
- for _, m := range methods {
- for _, name := range m.Names {
- method := &Method{
- MethodName: name.Name,
- Doc: m.Doc.Text(),
- Params: getParamList(m.Type.(*ast.FuncType).Params),
- Result: getParamList(m.Type.(*ast.FuncType).Results),
- }
- fixParamPackagePath(i.imports, method.Params)
- r.Methods = append(r.Methods, method)
- }
- }
- i.Interfaces = append(i.Interfaces, r)
- }
- }
- return i
-}
-
-// getInterfaceFromFile get interfaces
-// get all interfaces from file and compare with specified name
-func (i *InterfaceSet) getInterfaceFromFile(filename string, name, Package string, structNames []string) error {
- fileset := token.NewFileSet()
- f, err := parser.ParseFile(fileset, filename, nil, parser.ParseComments)
- if err != nil {
- return fmt.Errorf("can't parse file %q: %s", filename, err)
- }
-
- astResult := &InterfaceSet{imports: make(map[string]string)}
- ast.Walk(astResult, f)
-
- for _, info := range astResult.Interfaces {
- if name == info.Name {
- info.Package = Package
- info.ApplyStruct = structNames
- i.Interfaces = append(i.Interfaces, info)
- }
- }
-
- return nil
-}
-
-// Param parameters in method
-type Param struct { // (user model.User)
- PkgPath string // package's path: internal/model
- Package string // package's name: model
- Name string // param's name: user
- Type string // param's type: User
- IsArray bool // is array or not
- IsPointer bool // is pointer or not
-}
-
-// Eq if param equal to another
-func (p *Param) Eq(q Param) bool {
- return p.Package == q.Package && p.Type == q.Type
-}
-
-// IsError ...
-func (p *Param) IsError() bool {
- return p.Type == "error"
-}
-
-// IsGenM ...
-func (p *Param) IsGenM() bool {
- return p.Package == "gen" && p.Type == "M"
-}
-
-// IsGenRowsAffected ...
-func (p *Param) IsGenRowsAffected() bool {
- return p.Package == "gen" && p.Type == "RowsAffected"
-}
-
-// IsMap ...
-func (p *Param) IsMap() bool {
- return strings.HasPrefix(p.Type, "map[")
-}
-
-// IsGenT ...
-func (p *Param) IsGenT() bool {
- return p.Package == "gen" && p.Type == "T"
-}
-
-// IsInterface ...
-func (p *Param) IsInterface() bool {
- return p.Type == "interface{}"
-}
-
-// IsNull ...
-func (p *Param) IsNull() bool {
- return p.Package == "" && p.Type == "" && p.Name == ""
-}
-
-// InMainPkg ...
-func (p *Param) InMainPkg() bool {
- return p.Package == "main"
-}
-
-// IsTime ...
-func (p *Param) IsTime() bool {
- return p.Package == "time" && p.Type == "Time"
-}
-
-// IsSQLResult ...
-func (p *Param) IsSQLResult() bool {
- return (p.Package == "sql" && p.Type == "Result") || (p.Package == "gen" && p.Type == "SQLResult")
-}
-
-// IsSQLRow ...
-func (p *Param) IsSQLRow() bool {
- return (p.Package == "sql" && p.Type == "Row") || (p.Package == "gen" && p.Type == "SQLRow")
-}
-
-// IsSQLRows ...
-func (p *Param) IsSQLRows() bool {
- return (p.Package == "sql" && p.Type == "Rows") || (p.Package == "gen" && p.Type == "SQLRows")
-}
-
-// SetName ...
-func (p *Param) SetName(name string) {
- p.Name = name
-}
-
-// TypeName ...
-func (p *Param) TypeName() string {
- if p.IsArray {
- return "[]" + p.Type
- }
- return p.Type
-}
-
-// TmplString param to string in tmpl
-func (p *Param) TmplString() string {
- var res strings.Builder
- if p.Name != "" {
- res.WriteString(p.Name)
- res.WriteString(" ")
- }
-
- if p.IsArray {
- res.WriteString("[]")
- }
- if p.IsPointer {
- res.WriteString("*")
- }
- if p.Package != "" {
- res.WriteString(p.Package)
- res.WriteString(".")
- }
- res.WriteString(p.Type)
- return res.String()
-}
-
-// IsBaseType judge whether the param type is basic type
-func (p *Param) IsBaseType() bool {
- switch p.Type {
- case "string", "byte":
- return true
- case "int", "int8", "int16", "int32", "int64", "uint", "uint8", "uint16", "uint32", "uint64":
- return true
- case "float64", "float32":
- return true
- case "bool":
- return true
- case "time.Time":
- return true
- default:
- return false
- }
-}
-
-func (p *Param) astGetParamType(param *ast.Field) {
- switch v := param.Type.(type) {
- case *ast.Ident:
- p.Type = v.Name
- if v.Obj != nil {
- p.Package = "UNDEFINED" // set a placeholder
- }
- case *ast.SelectorExpr:
- p.astGetEltType(v)
- case *ast.ArrayType:
- p.astGetEltType(v.Elt)
- p.IsArray = true
- case *ast.Ellipsis:
- p.astGetEltType(v.Elt)
- p.IsArray = true
- case *ast.MapType:
- p.astGetMapType(v)
- case *ast.InterfaceType:
- p.Type = "interface{}"
- case *ast.StarExpr:
- p.IsPointer = true
- p.astGetEltType(v.X)
- default:
- log.Fatalf("unknow param type: %+v", v)
- }
-}
-
-func (p *Param) astGetEltType(expr ast.Expr) {
- switch v := expr.(type) {
- case *ast.Ident:
- p.Type = v.Name
- if v.Obj != nil {
- p.Package = "UNDEFINED"
- }
- case *ast.SelectorExpr:
- p.Type = v.Sel.Name
- p.astGetPackageName(v.X)
- case *ast.MapType:
- p.astGetMapType(v)
- case *ast.StarExpr:
- p.IsPointer = true
- p.astGetEltType(v.X)
- case *ast.InterfaceType:
- p.Type = "interface{}"
- case *ast.ArrayType:
- p.astGetEltType(v.Elt)
- p.Type = "[]" + p.Type
- default:
- log.Fatalf("unknow param type: %+v", v)
- }
-}
-
-func (p *Param) astGetPackageName(expr ast.Expr) {
- switch v := expr.(type) {
- case *ast.Ident:
- p.Package = v.Name
- }
-}
-
-func (p *Param) astGetMapType(expr *ast.MapType) {
- p.Type = fmt.Sprintf("map[%s]%s", astGetType(expr.Key), astGetType(expr.Value))
-}
-
-func astGetType(expr ast.Expr) string {
- switch v := expr.(type) {
- case *ast.Ident:
- return v.Name
- case *ast.InterfaceType:
- return "interface{}"
- }
- return ""
-}
diff --git a/toolkit/gormgen/internal/parser/utils.go b/toolkit/gormgen/internal/parser/utils.go
deleted file mode 100644
index 297c46ee..00000000
--- a/toolkit/gormgen/internal/parser/utils.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package parser
-
-import "go/ast"
-
-func getParamList(fields *ast.FieldList) []Param {
- if fields == nil {
- return nil
- }
- var pars []Param
- if len(fields.List) < 1 {
- return nil
- }
- for _, field := range fields.List {
- if field.Names == nil {
- par := Param{}
- par.astGetParamType(field)
- pars = append(pars, par)
- continue
- }
-
- for _, name := range field.Names {
- par := Param{
- Name: name.Name,
- }
- par.astGetParamType(field)
- pars = append(pars, par)
- continue
- }
- }
- return pars
-}
-
-func fixParamPackagePath(imports map[string]string, params []Param) {
- for i := range params {
- if importPath, exist := imports[params[i].Package]; exist {
- params[i].PkgPath = importPath
- }
- }
-}
diff --git a/toolkit/gormgen/internal/template/appendsvc.go b/toolkit/gormgen/internal/template/appendsvc.go
deleted file mode 100644
index 83fac09a..00000000
--- a/toolkit/gormgen/internal/template/appendsvc.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package template
-
-const AppendSvc = `
-// PostGen{{.ModelStructName}} {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-PostGen{{.ModelStructName}}(ctx context.Context, body model.{{.ModelStructName}}) (model.{{.ModelStructName}}, error)
-
-// GetGen{{.ModelStructName}} {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-GetGen{{.ModelStructName}}(ctx context.Context, body model.{{.ModelStructName}}) (model.{{.ModelStructName}}, error)
-
-// PutGen{{.ModelStructName}} {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-PutGen{{.ModelStructName}}(ctx context.Context, body model.{{.ModelStructName}}) error
-
-// DeleteGen{{.ModelStructName}} {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-DeleteGen{{.ModelStructName}}(ctx context.Context, body model.{{.ModelStructName}}) error
-
-// GetGen{{.ModelStructName}}s {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-GetGen{{.ModelStructName}}s(ctx context.Context, parameter dto.Parameter) (data dto.Page, err error)
-
-`
diff --git a/toolkit/gormgen/internal/template/appendsvcimpl.go b/toolkit/gormgen/internal/template/appendsvcimpl.go
deleted file mode 100644
index 4f8425e7..00000000
--- a/toolkit/gormgen/internal/template/appendsvcimpl.go
+++ /dev/null
@@ -1,64 +0,0 @@
-package template
-
-const AppendSvcImpl = `
-// PostGen{{.ModelStructName}}Rpc {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-func (receiver *{{.InterfaceName}}Impl) PostGen{{.ModelStructName}}Rpc(ctx context.Context, body *pb.{{.ModelStructName}}) (data *pb.{{.ModelStructName}}, err error) {
- var m model.{{.ModelStructName}}
- copier.DeepCopy(body, &m)
- u := receiver.q.{{.ModelStructName}}
- if err = u.WithContext(ctx).Create(&m); err != nil {
- return nil, errors.WithStack(err)
- }
- data = new(pb.{{.ModelStructName}})
- copier.DeepCopy(m, data)
- return
-}
-
-// GetGen{{.ModelStructName}}Rpc {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-func (receiver *{{.InterfaceName}}Impl) GetGen{{.ModelStructName}}Rpc(ctx context.Context, body *pb.{{.ModelStructName}}) (data *pb.{{.ModelStructName}}, err error) {
- u := receiver.q.{{.ModelStructName}}
- m, err := u.WithContext(ctx).Where(u.ID.Eq(*body.{{.PbPrimaryProp}})).First()
- if err != nil {
- return nil, errors.WithStack(err)
- }
- data = new(pb.{{.ModelStructName}})
- copier.DeepCopy(m, data)
- return
-}
-
-// PutGen{{.ModelStructName}}Rpc {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-func (receiver *{{.InterfaceName}}Impl) PutGen{{.ModelStructName}}Rpc(ctx context.Context, body *pb.{{.ModelStructName}}) (*emptypb.Empty, error) {
- var m model.{{.ModelStructName}}
- copier.DeepCopy(body, &m)
- u := receiver.q.{{.ModelStructName}}
- _, err := u.WithContext(ctx).Where(u.ID.Eq(*body.{{.PbPrimaryProp}})).Updates(m)
- return &emptypb.Empty{}, errors.WithStack(err)
-}
-
-// DeleteGen{{.ModelStructName}}Rpc {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-func (receiver *{{.InterfaceName}}Impl) DeleteGen{{.ModelStructName}}Rpc(ctx context.Context, body *pb.{{.ModelStructName}}) (*emptypb.Empty, error) {
- u := receiver.q.{{.ModelStructName}}
- _, err := u.WithContext(ctx).Where(u.ID.Eq(*body.{{.PbPrimaryProp}})).Delete()
- return &emptypb.Empty{}, errors.WithStack(err)
-}
-
-// GetGen{{.ModelStructName}}sRpc {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-func (receiver *{{.InterfaceName}}Impl) GetGen{{.ModelStructName}}sRpc(ctx context.Context, request *pb.Parameter) (data *pb.Page, err error) {
- var body dto.Parameter
- copier.DeepCopy(request, &body)
- resCxt := receiver.pg.With(receiver.q.Db().Model(&model.{{.ModelStructName}}{})).Request(body)
- paginated := resCxt.Response(&[]model.{{.ModelStructName}}{})
- if resCxt.Error() != nil {
- return nil, errors.WithStack(resCxt.Error())
- }
- data = new(pb.Page)
- copier.DeepCopy(paginated, data)
- return
-}
-
-`
diff --git a/toolkit/gormgen/internal/template/appendsvcimplgrpc.go b/toolkit/gormgen/internal/template/appendsvcimplgrpc.go
deleted file mode 100644
index 651f51a7..00000000
--- a/toolkit/gormgen/internal/template/appendsvcimplgrpc.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package template
-
-const AppendSvcImplGrpc = `
-// PostGen{{.ModelStructName}}Rpc {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-func (receiver *{{.InterfaceName}}Impl) PostGen{{.ModelStructName}}Rpc(ctx context.Context, request *pb.{{.ModelStructName}}) (*pb.PostGen{{.ModelStructName}}RpcResponse, error) {
- var body dto.{{.ModelStructName}}
- copier.DeepCopy(request, &body)
- data, err := receiver.PostGen{{.ModelStructName}}(ctx, body)
- return &pb.PostGen{{.ModelStructName}}RpcResponse{
- Data: data,
- }, errors.WithStack(err)
-}
-
-// PostGen{{.ModelStructName}}sRpc {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-func (receiver *{{.InterfaceName}}Impl) PostGen{{.ModelStructName}}sRpc(ctx context.Context, request *pb.PostGen{{.ModelStructName}}sRpcRequest) (*pb.PostGen{{.ModelStructName}}sRpcResponse, error) {
- list := make([]dto.{{.ModelStructName}}, 0, len(request.Body))
- for _, item := range request.Body {
- var d dto.{{.ModelStructName}}
- copier.DeepCopy(item, &d)
- list = append(list, d)
- }
- data, err := receiver.PostGen{{.ModelStructName}}s(ctx, list)
- return &pb.PostGen{{.ModelStructName}}sRpcResponse{
- Data: data,
- }, errors.WithStack(err)
-}
-
-// GetGen{{.ModelStructName}}IdRpc {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-func (receiver *{{.InterfaceName}}Impl) GetGen{{.ModelStructName}}IdRpc(ctx context.Context, request *pb.GetGen{{.ModelStructName}}IdRpcRequest) (*pb.{{.ModelStructName}}, error) {
- data, err := receiver.GetGen{{.ModelStructName}}_Id(ctx, request.Id)
- if err != nil {
- return nil, errors.WithStack(err)
- }
- var ret pb.{{.ModelStructName}}
- copier.DeepCopy(data, &ret)
- return &ret, nil
-}
-
-// PutGen{{.ModelStructName}}Rpc {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-func (receiver *{{.InterfaceName}}Impl) PutGen{{.ModelStructName}}Rpc(ctx context.Context, request *pb.{{.ModelStructName}}) (*emptypb.Empty, error) {
- var body dto.{{.ModelStructName}}
- copier.DeepCopy(request, &body)
- return &emptypb.Empty{}, errors.WithStack(receiver.PutGen{{.ModelStructName}}(ctx, body))
-}
-
-// DeleteGen{{.ModelStructName}}IdRpc {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-func (receiver *{{.InterfaceName}}Impl) DeleteGen{{.ModelStructName}}IdRpc(ctx context.Context, request *pb.DeleteGen{{.ModelStructName}}IdRpcRequest) (*emptypb.Empty, error) {
- return &emptypb.Empty{}, errors.WithStack(receiver.DeleteGen{{.ModelStructName}}_Id(ctx, request.Id))
-}
-
-// GetGen{{.ModelStructName}}sRpc {{.StructComment}}
-` + NotEditMarkForGDDShort + `
-func (receiver *{{.InterfaceName}}Impl) GetGen{{.ModelStructName}}sRpc(ctx context.Context, request *pb.Parameter) (*pb.Page, error) {
- filters := make([]interface{}, 0, len(request.Filters))
- for _, item := range request.Filters {
- str := wrappers.StringValue{}
- if err := anypb.UnmarshalTo(item, &str, proto.UnmarshalOptions{}); err != nil {
- return nil, errors.WithStack(err)
- }
- filters = append(filters, str.Value)
- }
- var parameter dto.Parameter
- copier.DeepCopy(request, ¶meter)
- parameter.Filters = filters
- data, err := receiver.GetGen{{.ModelStructName}}s(ctx, parameter)
- if err != nil {
- return nil, errors.WithStack(err)
- }
- items := make([]*anypb.Any, 0, len(data.Items))
- for _, item := range data.Items {
- d := dto.{{.ModelStructName}}(item.(model.{{.ModelStructName}}))
- var msg pb.{{.ModelStructName}}
- copier.DeepCopy(d, &msg)
- a, err := anypb.New(&msg)
- if err != nil {
- return nil, errors.WithStack(err)
- }
- items = append(items, a)
- }
- var ret pb.Page
- copier.DeepCopy(data, &ret)
- ret.Items = items
- return &ret, nil
-}
-
-`
diff --git a/toolkit/gormgen/internal/template/base.go b/toolkit/gormgen/internal/template/base.go
deleted file mode 100644
index fa2a06cf..00000000
--- a/toolkit/gormgen/internal/template/base.go
+++ /dev/null
@@ -1,29 +0,0 @@
-package template
-
-const NotEditMark = `
-// Code generated by github.com/wubin1989/gen. DO NOT EDIT.
-// Code generated by github.com/wubin1989/gen. DO NOT EDIT.
-// Code generated by github.com/wubin1989/gen. DO NOT EDIT.
-`
-
-const EditMark = `
-// Code generated by github.com/wubin1989/gen. YOU CAN EDIT.
-// Code generated by github.com/wubin1989/gen. YOU CAN EDIT.
-// Code generated by github.com/wubin1989/gen. YOU CAN EDIT.
-`
-
-const NotEditMarkForGDDShort = `// Code generated by github.com/wubin1989/gen for go-doudou. DO NOT EDIT.`
-
-const EditMarkForGDD = `
-// Code generated by github.com/wubin1989/gen for go-doudou. YOU CAN EDIT.
-// Code generated by github.com/wubin1989/gen for go-doudou. YOU CAN EDIT.
-// Code generated by github.com/wubin1989/gen for go-doudou. YOU CAN EDIT.
-`
-
-const Header = NotEditMark + `
-package {{.Package}}
-
-import(
- {{range .ImportPkgPaths}}{{.}}` + "\n" + `{{end}}
-)
-`
diff --git a/toolkit/gormgen/internal/template/dto.go b/toolkit/gormgen/internal/template/dto.go
deleted file mode 100644
index b8b77166..00000000
--- a/toolkit/gormgen/internal/template/dto.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package template
-
-// Dto used as a variable because it cannot load template file after packed, params still can pass file
-const Dto = EditMark + `
-package dto
-
-import (
- "encoding/json"
- "time"
-
- "github.com/wubin1989/datatypes"
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/schema"
- {{range .ImportPkgPaths}}{{.}} ` + "\n" + `{{end}}
-)
-
-// {{.ModelStructName}} {{.StructComment}}
-type {{.ModelStructName}} struct {
- {{range .Fields}}
- {{if .MultilineComment -}}
- /*
-{{.ColumnComment}}
- */
- {{end -}}
- {{.Name}} {{.Type | convert}} ` + "`{{.Tags}}` " +
- "{{if not .MultilineComment}}{{if .ColumnComment}}// {{.ColumnComment}}{{end}}{{end}}" +
- `{{end}}
-}
-
-`
diff --git a/toolkit/gormgen/internal/template/method.go b/toolkit/gormgen/internal/template/method.go
deleted file mode 100644
index 4b2ab2a2..00000000
--- a/toolkit/gormgen/internal/template/method.go
+++ /dev/null
@@ -1,438 +0,0 @@
-package template
-
-// DIYMethod DIY method
-const DIYMethod = `
-
-// {{.DocComment }}
-func ({{.S}} {{.TargetStruct}}Do){{.FuncSign}}{
- {{if .HasSQLData}}var params []interface{}
-
- {{end}}var generateSQL strings.Builder
- {{range $line:=.Section.Tmpls}}{{$line}}
- {{end}}
-
- {{if .HasNeedNewResult}}result ={{if .ResultData.IsMap}}make{{else}}new{{end}}({{if ne .ResultData.Package ""}}{{.ResultData.Package}}.{{end}}{{.ResultData.Type}}){{end}}
- {{if .ReturnSQLResult}}stmt := {{.S}}.UnderlyingDB().Statement
- result,{{if .ReturnError}}err{{else}}_{{end}} = stmt.ConnPool.ExecContext(stmt.Context,generateSQL.String(){{if .HasSQLData}},params...{{end}}) // ignore_security_alert
- {{else if .ReturnSQLRow}}row = {{.S}}.UnderlyingDB().Raw(generateSQL.String(){{if .HasSQLData}},params...{{end}}).Row() // ignore_security_alert
- {{else if .ReturnSQLRows}}rows,{{if .ReturnError}}err{{else}}_{{end}} = {{.S}}.UnderlyingDB().Raw(generateSQL.String(){{if .HasSQLData}},params...{{end}}).Rows() // ignore_security_alert
- {{else}}var executeSQL *gorm.DB
- executeSQL = {{.S}}.UnderlyingDB().{{.GormOption}}(generateSQL.String(){{if .HasSQLData}},params...{{end}}){{if not .ResultData.IsNull}}.{{.GormRunMethodName}}({{if .HasGotPoint}}&{{end}}{{.ResultData.Name}}){{end}} // ignore_security_alert
- {{if .ReturnRowsAffected}}rowsAffected = executeSQL.RowsAffected
- {{end}}{{if .ReturnError}}err = executeSQL.Error
- {{end}}{{if .ReturnNothing}}_ = executeSQL
- {{end}}{{end}}
- return
-}
-
-`
-
-// CRUDMethod CRUD method
-const CRUDMethod = `
-func ({{.S}} {{.QueryStructName}}Do) Debug() {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Debug())
-}
-
-func ({{.S}} {{.QueryStructName}}Do) WithContext(ctx context.Context) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.WithContext(ctx))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) ReadDB() {{.ReturnObject}} {
- return {{.S}}.Clauses(dbresolver.Read)
-}
-
-func ({{.S}} {{.QueryStructName}}Do) WriteDB() {{.ReturnObject}} {
- return {{.S}}.Clauses(dbresolver.Write)
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Session(config *gorm.Session) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Session(config))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Clauses(conds ...clause.Expression) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Clauses(conds...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Returning(value interface{}, columns ...string) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Returning(value, columns...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Not(conds ...gormgen.Condition) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Not(conds...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Or(conds ...gormgen.Condition) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Or(conds...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Select(conds ...field.Expr) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Select(conds...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Where(conds ...gormgen.Condition) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Where(conds...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Exists(subquery interface{UnderlyingDB() *gorm.DB}) {{.ReturnObject}} {
- return {{.S}}.Where(field.CompareSubQuery(field.ExistsOp, nil, subquery.UnderlyingDB()))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Order(conds ...field.Expr) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Order(conds...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Distinct(cols ...field.Expr) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Distinct(cols...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Omit(cols ...field.Expr) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Omit(cols...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Join(table schema.Tabler, on ...field.Expr) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Join(table, on...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) LeftJoin(table schema.Tabler, on ...field.Expr) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.LeftJoin(table, on...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) RightJoin(table schema.Tabler, on ...field.Expr) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.RightJoin(table, on...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Group(cols ...field.Expr) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Group(cols...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Having(conds ...gormgen.Condition) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Having(conds...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Limit(limit int) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Limit(limit))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Offset(offset int) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Offset(offset))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Scopes(funcs ...func(gormgen.Dao) gormgen.Dao) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Scopes(funcs...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Unscoped() {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Unscoped())
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Create(values ...*{{.StructInfo.Package}}.{{.StructInfo.Type}}) error {
- if len(values) == 0 {
- return nil
- }
- return {{.S}}.DO.Create(values)
-}
-
-func ({{.S}} {{.QueryStructName}}Do) CreateInBatches(values []*{{.StructInfo.Package}}.{{.StructInfo.Type}}, batchSize int) error {
- return {{.S}}.DO.CreateInBatches(values, batchSize)
-}
-
-// Save : !!! underlying implementation is different with GORM
-// The method is equivalent to executing the statement: db.Clauses(clause.OnConflict{UpdateAll: true}).Create(values)
-func ({{.S}} {{.QueryStructName}}Do) Save(values ...*{{.StructInfo.Package}}.{{.StructInfo.Type}}) error {
- if len(values) == 0 {
- return nil
- }
- return {{.S}}.DO.Save(values)
-}
-
-func ({{.S}} {{.QueryStructName}}Do) First() (*{{.StructInfo.Package}}.{{.StructInfo.Type}}, error) {
- if result, err := {{.S}}.DO.First(); err != nil {
- return nil, err
- } else {
- return result.(*{{.StructInfo.Package}}.{{.StructInfo.Type}}), nil
- }
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Take() (*{{.StructInfo.Package}}.{{.StructInfo.Type}}, error) {
- if result, err := {{.S}}.DO.Take(); err != nil {
- return nil, err
- } else {
- return result.(*{{.StructInfo.Package}}.{{.StructInfo.Type}}), nil
- }
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Last() (*{{.StructInfo.Package}}.{{.StructInfo.Type}}, error) {
- if result, err := {{.S}}.DO.Last(); err != nil {
- return nil, err
- } else {
- return result.(*{{.StructInfo.Package}}.{{.StructInfo.Type}}), nil
- }
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Find() ([]*{{.StructInfo.Package}}.{{.StructInfo.Type}}, error) {
- result, err := {{.S}}.DO.Find()
- return result.([]*{{.StructInfo.Package}}.{{.StructInfo.Type}}), err
-}
-
-func ({{.S}} {{.QueryStructName}}Do) FindInBatch(batchSize int, fc func(tx gormgen.Dao, batch int) error) (results []*{{.StructInfo.Package}}.{{.StructInfo.Type}}, err error) {
- buf := make([]*{{.StructInfo.Package}}.{{.StructInfo.Type}}, 0, batchSize)
- err = {{.S}}.DO.FindInBatches(&buf, batchSize, func(tx gormgen.Dao, batch int) error {
- defer func() { results = append(results, buf...) }()
- return fc(tx, batch)
- })
- return results, err
-}
-
-func ({{.S}} {{.QueryStructName}}Do) FindInBatches(result *[]*{{.StructInfo.Package}}.{{.StructInfo.Type}}, batchSize int, fc func(tx gormgen.Dao, batch int) error) error {
- return {{.S}}.DO.FindInBatches(result, batchSize, fc)
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Attrs(attrs ...field.AssignExpr) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Attrs(attrs...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Assign(attrs ...field.AssignExpr) {{.ReturnObject}} {
- return {{.S}}.withDO({{.S}}.DO.Assign(attrs...))
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Joins(fields ...field.RelationField) {{.ReturnObject}} {
- for _, _f := range fields {
- {{.S}} = *{{.S}}.withDO({{.S}}.DO.Joins(_f))
- }
- return &{{.S}}
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Preload(fields ...field.RelationField) {{.ReturnObject}} {
- for _, _f := range fields {
- {{.S}} = *{{.S}}.withDO({{.S}}.DO.Preload(_f))
- }
- return &{{.S}}
-}
-
-func ({{.S}} {{.QueryStructName}}Do) FirstOrInit() (*{{.StructInfo.Package}}.{{.StructInfo.Type}}, error) {
- if result, err := {{.S}}.DO.FirstOrInit(); err != nil {
- return nil, err
- } else {
- return result.(*{{.StructInfo.Package}}.{{.StructInfo.Type}}), nil
- }
-}
-
-func ({{.S}} {{.QueryStructName}}Do) FirstOrCreate() (*{{.StructInfo.Package}}.{{.StructInfo.Type}}, error) {
- if result, err := {{.S}}.DO.FirstOrCreate(); err != nil {
- return nil, err
- } else {
- return result.(*{{.StructInfo.Package}}.{{.StructInfo.Type}}), nil
- }
-}
-
-func ({{.S}} {{.QueryStructName}}Do) FindByPage(offset int, limit int) (result []*{{.StructInfo.Package}}.{{.StructInfo.Type}}, count int64, err error) {
- result, err = {{.S}}.Offset(offset).Limit(limit).Find()
- if err != nil{
- return
- }
-
- if size := len(result); 0 < limit && 0 < size && size < limit {
- count = int64(size+offset)
- return
- }
-
- count, err = {{.S}}.Offset(-1).Limit(-1).Count()
- return
-}
-
-func ({{.S}} {{.QueryStructName}}Do) ScanByPage(result interface{}, offset int, limit int) (count int64, err error) {
- count, err = {{.S}}.Count()
- if err != nil {
- return
- }
-
- err = {{.S}}.Offset(offset).Limit(limit).Scan(result)
- return
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Scan(result interface{}) (err error) {
- return {{.S}}.DO.Scan(result)
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Fetch(result interface{}) (err error) {
- return {{.S}}.DO.Fetch(result)
-}
-
-func ({{.S}} {{.QueryStructName}}Do) Delete(models ...*{{.StructInfo.Package}}.{{.StructInfo.Type}}) (result gormgen.ResultInfo, err error) {
- return {{.S}}.DO.Delete(models)
-}
-
-func ({{.S}} *{{.QueryStructName}}Do) withDO(do gormgen.Dao) (*{{.QueryStructName}}Do) {
- {{.S}}.DO = *do.(*gormgen.DO)
- return {{.S}}
-}
-
-`
-
-// CRUDMethodTest CRUD method test
-const CRUDMethodTest = `
-func init() {
- InitializeDB()
- err := db.AutoMigrate(&{{.StructInfo.Package}}.{{.ModelStructName}}{})
- if err != nil{
- fmt.Printf("Error: AutoMigrate(&{{.StructInfo.Package}}.{{.ModelStructName}}{}) fail: %s", err)
- }
-}
-
-func Test_{{.QueryStructName}}Query(t *testing.T) {
- {{.QueryStructName}} := new{{.ModelStructName}}(db)
- {{.QueryStructName}} = *{{.QueryStructName}}.As({{.QueryStructName}}.TableName())
- _do := {{.QueryStructName}}.WithContext(context.Background()).Debug()
-
- primaryKey := field.NewString({{.QueryStructName}}.TableName(), clause.PrimaryKey)
- _, err := _do.Unscoped().Where(primaryKey.IsNotNull()).Delete()
- if err != nil {
- t.Error("clean table <{{.TableName}}> fail:", err)
- return
- }
-
- _, ok := {{.QueryStructName}}.GetFieldByName("")
- if ok {
- t.Error("GetFieldByName(\"\") from {{.QueryStructName}} success")
- }
-
- err = _do.Create(&{{.StructInfo.Package}}.{{.ModelStructName}}{})
- if err != nil {
- t.Error("create item in table <{{.TableName}}> fail:", err)
- }
-
- err = _do.Save(&{{.StructInfo.Package}}.{{.ModelStructName}}{})
- if err != nil {
- t.Error("create item in table <{{.TableName}}> fail:", err)
- }
-
- err = _do.CreateInBatches([]*{{.StructInfo.Package}}.{{.ModelStructName}}{ {}, {} }, 10)
- if err != nil {
- t.Error("create item in table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.Select({{.QueryStructName}}.ALL).Take()
- if err != nil {
- t.Error("Take() on table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.First()
- if err != nil {
- t.Error("First() on table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.Last()
- if err != nil {
- t.Error("First() on table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.Where(primaryKey.IsNotNull()).FindInBatch(10, func(tx gormgen.Dao, batch int) error { return nil })
- if err != nil {
- t.Error("FindInBatch() on table <{{.TableName}}> fail:", err)
- }
-
- err = _do.Where(primaryKey.IsNotNull()).FindInBatches(&[]*{{.StructInfo.Package}}.{{.ModelStructName}}{}, 10, func(tx gormgen.Dao, batch int) error { return nil })
- if err != nil {
- t.Error("FindInBatches() on table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.Select({{.QueryStructName}}.ALL).Where(primaryKey.IsNotNull()).Order(primaryKey.Desc()).Find()
- if err != nil {
- t.Error("Find() on table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.Distinct(primaryKey).Take()
- if err != nil {
- t.Error("select Distinct() on table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.Select({{.QueryStructName}}.ALL).Omit(primaryKey).Take()
- if err != nil {
- t.Error("Omit() on table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.Group(primaryKey).Find()
- if err != nil {
- t.Error("Group() on table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.Scopes(func(dao gormgen.Dao) gormgen.Dao { return dao.Where(primaryKey.IsNotNull()) }).Find()
- if err != nil {
- t.Error("Scopes() on table <{{.TableName}}> fail:", err)
- }
-
- _, _, err = _do.FindByPage(0, 1)
- if err != nil {
- t.Error("FindByPage() on table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.ScanByPage(&{{.StructInfo.Package}}.{{.ModelStructName}}{}, 0, 1)
- if err != nil {
- t.Error("ScanByPage() on table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.Attrs(primaryKey).Assign(primaryKey).FirstOrInit()
- if err != nil {
- t.Error("FirstOrInit() on table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.Attrs(primaryKey).Assign(primaryKey).FirstOrCreate()
- if err != nil {
- t.Error("FirstOrCreate() on table <{{.TableName}}> fail:", err)
- }
-
- var _a _another
- var _aPK = field.NewString(_a.TableName(), clause.PrimaryKey)
-
- err = _do.Join(&_a, primaryKey.EqCol(_aPK)).Scan(map[string]interface{}{})
- if err != nil {
- t.Error("Join() on table <{{.TableName}}> fail:", err)
- }
-
- err = _do.LeftJoin(&_a, primaryKey.EqCol(_aPK)).Scan(map[string]interface{}{})
- if err != nil {
- t.Error("LeftJoin() on table <{{.TableName}}> fail:", err)
- }
-
- _, err = _do.Not().Or().Clauses().Take()
- if err != nil {
- t.Error("Not/Or/Clauses on table <{{.TableName}}> fail:", err)
- }
-}
-`
-
-// DIYMethodTestBasic DIY method test basic
-const DIYMethodTestBasic = `
-type Input struct {
- Args []interface{}
-}
-
-type Expectation struct {
- Ret []interface{}
-}
-
-type TestCase struct {
- Input
- Expectation
-}
-
-`
-
-// DIYMethodTest DIY method test
-const DIYMethodTest = `
-
-var {{.OriginStruct.Type}}{{.MethodName}}TestCase = []TestCase{}
-
-func Test_{{.TargetStruct}}_{{.MethodName}}(t *testing.T) {
- {{.TargetStruct}} := new{{.OriginStruct.Type}}(db)
- do := {{.TargetStruct}}.WithContext(context.Background()).Debug()
-
- for i, tt := range {{.OriginStruct.Type}}{{.MethodName}}TestCase {
- t.Run("{{.MethodName}}_"+strconv.Itoa(i), func(t *testing.T) {
- {{.GetTestResultParamInTmpl}} := do.{{.MethodName}}({{.GetTestParamInTmpl}})
- {{.GetAssertInTmpl}}
- })
- }
-}
-
-`
diff --git a/toolkit/gormgen/internal/template/model.go b/toolkit/gormgen/internal/template/model.go
deleted file mode 100644
index f5ad31f8..00000000
--- a/toolkit/gormgen/internal/template/model.go
+++ /dev/null
@@ -1,53 +0,0 @@
-package template
-
-// Model used as a variable because it cannot load template file after packed, params still can pass file
-const Model = NotEditMark + `
-package {{.StructInfo.Package}}
-
-import (
- "{{.ConfigPackage}}"
- "fmt"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-
- "encoding/json"
- "time"
-
- "github.com/wubin1989/datatypes"
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/gorm/schema"
- {{range .ImportPkgPaths}}{{.}} ` + "\n" + `{{end}}
-)
-
-// {{.ModelStructName}} {{.StructComment}}
-type {{.ModelStructName}} struct {
- {{range .Fields}}
- {{if .MultilineComment -}}
- /*
-{{.ColumnComment}}
- */
- {{end -}}
- {{.Name}} {{.Type | convert}} ` + "`{{.Tags}}` " +
- "{{if not .MultilineComment}}{{if .ColumnComment}}// {{.ColumnComment}}{{end}}{{end}}" +
- `{{end}}
-}
-
-// TableName {{.ModelStructName}}'s table name
-func (*{{.ModelStructName}}) TableName() string {
- {{if .TableName -}}var TableName{{.ModelStructName}} string{{- end}}
-
- if stringutils.IsNotEmpty(config.G_Config.Db.Name) {
- TableName{{.ModelStructName}} = fmt.Sprintf("%s.{{.TableName}}", config.G_Config.Db.Name)
- } else {
- TableName{{.ModelStructName}} = "{{.TableName}}"
- }
-
- return TableName{{.ModelStructName}}
-}
-`
-
-// ModelMethod model struct DIY method
-const ModelMethod = `
-
-{{if .Doc -}}// {{.DocComment -}}{{end}}
-func ({{.GetBaseStructTmpl}}){{.MethodName}}({{.GetParamInTmpl}})({{.GetResultParamInTmpl}}){{.Body}}
-`
diff --git a/toolkit/gormgen/internal/template/query.go b/toolkit/gormgen/internal/template/query.go
deleted file mode 100644
index d554d730..00000000
--- a/toolkit/gormgen/internal/template/query.go
+++ /dev/null
@@ -1,211 +0,0 @@
-package template
-
-// DefaultQuery default query
-const DefaultQuery = `
-var (
- Q =new(Query)
- {{range $name,$d :=.Data -}}
- {{$d.ModelStructName}} *{{$d.QueryStructName}}
- {{end -}}
-)
-
-func SetDefault(db *gorm.DB, opts ...gormgen.DOOption) {
- *Q = *Use(db,opts...)
- {{range $name,$d :=.Data -}}
- {{$d.ModelStructName}} = &Q.{{$d.ModelStructName}}
- {{end -}}
-}
-
-`
-
-// QueryMethod query method template
-const QueryMethod = `
-func Use(db *gorm.DB, opts ...gormgen.DOOption) *Query {
- return &Query{
- db: db,
- {{range $name,$d :=.Data -}}
- {{$d.ModelStructName}}: new{{$d.ModelStructName}}(db,opts...),
- {{end -}}
- }
-}
-
-type Query struct{
- db *gorm.DB
-
- {{range $name,$d :=.Data -}}
- {{$d.ModelStructName}} {{$d.QueryStructName}}
- {{end}}
-}
-
-func (q *Query) Available() bool { return q.db != nil }
-
-func (q *Query) Clone(db *gorm.DB) *Query {
- return &Query{
- db: db,
- {{range $name,$d :=.Data -}}
- {{$d.ModelStructName}}: q.{{$d.ModelStructName}}.clone(db),
- {{end}}
- }
-}
-
-func (q *Query) Db() *gorm.DB {
- return q.db
-}
-
-func (q *Query) ReadDB() *Query {
- return q.ReplaceDB(q.db.Clauses(dbresolver.Read))
-}
-
-func (q *Query) WriteDB() *Query {
- return q.ReplaceDB(q.db.Clauses(dbresolver.Write))
-}
-
-func (q *Query) ReplaceDB(db *gorm.DB) *Query {
- return &Query{
- db: db,
- {{range $name,$d :=.Data -}}
- {{$d.ModelStructName}}: q.{{$d.ModelStructName}}.replaceDB(db),
- {{end}}
- }
-}
-
-type queryCtx struct{
- {{range $name,$d :=.Data -}}
- {{$d.ModelStructName}} {{$d.ReturnObject}}
- {{end}}
-}
-
-func (q *Query) WithContext(ctx context.Context) *queryCtx {
- return &queryCtx{
- {{range $name,$d :=.Data -}}
- {{$d.ModelStructName}}: q.{{$d.ModelStructName}}.WithContext(ctx),
- {{end}}
- }
-}
-
-func (q *Query) Transaction(fc func(tx *Query) error, opts ...*sql.TxOptions) error {
- return q.db.Transaction(func(tx *gorm.DB) error { return fc(q.Clone(tx)) }, opts...)
-}
-
-func (q *Query) Begin(opts ...*sql.TxOptions) *QueryTx {
- tx := q.db.Begin(opts...)
- return &QueryTx{Query: q.Clone(tx), Error: tx.Error}
-}
-
-type QueryTx struct {
- *Query
- Error error
-}
-
-func (q *QueryTx) Commit() error {
- return q.db.Commit().Error
-}
-
-func (q *QueryTx) Rollback() error {
- return q.db.Rollback().Error
-}
-
-func (q *QueryTx) SavePoint(name string) error {
- return q.db.SavePoint(name).Error
-}
-
-func (q *QueryTx) RollbackTo(name string) error {
- return q.db.RollbackTo(name).Error
-}
-
-`
-
-// QueryMethodTest query method test template
-const QueryMethodTest = `
-
-const dbName = "gen_test.db"
-
-var db *gorm.DB
-var once sync.Once
-
-func init() {
- InitializeDB()
- db.AutoMigrate(&_another{})
-}
-
-func InitializeDB() {
- once.Do(func() {
- var err error
- db, err = gorm.Open(sqlite.Open(dbName), &gorm.Config{})
- if err != nil {
- panic(fmt.Errorf("open sqlite %q fail: %w", dbName, err))
- }
- })
-}
-
-func assert(t *testing.T, methodName string, res, exp interface{}) {
- if !reflect.DeepEqual(res, exp) {
- t.Errorf("%v() gotResult = %v, want %v", methodName, res, exp)
- }
-}
-
-type _another struct {
- ID uint64 ` + "`" + `gorm:"primaryKey"` + "`" + `
-}
-
-func (*_another) TableName() string { return "another_for_unit_test" }
-
-func Test_Available(t *testing.T) {
- if !Use(db).Available() {
- t.Errorf("query.Available() == false")
- }
-}
-
-func Test_WithContext(t *testing.T) {
- query := Use(db)
- if !query.Available() {
- t.Errorf("query Use(db) fail: query.Available() == false")
- }
-
- type Content string
- var key, value Content = "gen_tag", "unit_test"
- qCtx := query.WithContext(context.WithValue(context.Background(), key, value))
-
- for _, ctx := range []context.Context{
- {{range $name,$d :=.Data -}}
- qCtx.{{$d.ModelStructName}}.UnderlyingDB().Statement.Context,
- {{end}}
- } {
- if v := ctx.Value(key); v != value {
- t.Errorf("get value from context fail, expect %q, got %q", value, v)
- }
- }
-}
-
-func Test_Transaction(t *testing.T) {
- query := Use(db)
- if !query.Available() {
- t.Errorf("query Use(db) fail: query.Available() == false")
- }
-
- err := query.Transaction(func(tx *Query) error { return nil })
- if err != nil {
- t.Errorf("query.Transaction execute fail: %s", err)
- }
-
- tx := query.Begin()
-
- err = tx.SavePoint("point")
- if err != nil {
- t.Errorf("query tx SavePoint fail: %s", err)
- }
- err = tx.RollbackTo("point")
- if err != nil {
- t.Errorf("query tx RollbackTo fail: %s", err)
- }
- err = tx.Commit()
- if err != nil {
- t.Errorf("query tx Commit fail: %s", err)
- }
-
- err = query.Begin().Rollback()
- if err != nil {
- t.Errorf("query tx Rollback fail: %s", err)
- }
-}
-`
diff --git a/toolkit/gormgen/internal/template/struct.go b/toolkit/gormgen/internal/template/struct.go
deleted file mode 100644
index 215f1165..00000000
--- a/toolkit/gormgen/internal/template/struct.go
+++ /dev/null
@@ -1,301 +0,0 @@
-package template
-
-const (
- // TableQueryStruct table query struct
- TableQueryStruct = createMethod + `
- type {{.QueryStructName}} struct {
- {{.QueryStructName}}Do
- ` + fields + `
- }
- ` + tableMethod + asMethond + updateFieldMethod + getFieldMethod + getFieldExprByName + fillFieldMapMethod + cloneMethod + replaceMethod + relationship + defineMethodStruct
-
- // TableQueryStructWithContext table query struct with context
- TableQueryStructWithContext = createMethod + `
- type {{.QueryStructName}} struct {
- {{.QueryStructName}}Do {{.QueryStructName}}Do
- ` + fields + `
- }
- ` + tableMethod + asMethond + updateFieldMethod + `
-
- func ({{.S}} *{{.QueryStructName}}) WithContext(ctx context.Context) {{.ReturnObject}} { return {{.S}}.{{.QueryStructName}}Do.WithContext(ctx)}
-
- func ({{.S}} {{.QueryStructName}}) TableName() string { return {{.S}}.{{.QueryStructName}}Do.TableName() }
-
- func ({{.S}} {{.QueryStructName}}) Alias() string { return {{.S}}.{{.QueryStructName}}Do.Alias() }
-
- func ({{.S}} {{.QueryStructName}}) Columns(cols ...field.Expr) gormgen.Columns { return {{.S}}.{{.QueryStructName}}Do.Columns(cols...) }
-
- ` + getFieldMethod + getFieldExprByName + fillFieldMapMethod + cloneMethod + replaceMethod + relationship + defineMethodStruct
-
- // TableQueryIface table query interface
- TableQueryIface = defineDoInterface
-)
-
-const (
- createMethod = `
- func new{{.ModelStructName}}(db *gorm.DB, opts ...gormgen.DOOption) {{.QueryStructName}} {
- _{{.QueryStructName}} := {{.QueryStructName}}{}
-
- _{{.QueryStructName}}.{{.QueryStructName}}Do.UseDB(db,opts...)
- _{{.QueryStructName}}.{{.QueryStructName}}Do.UseModel(&{{.StructInfo.Package}}.{{.StructInfo.Type}}{})
-
- tableName := _{{.QueryStructName}}.{{.QueryStructName}}Do.TableName()
- _{{$.QueryStructName}}.ALL = field.NewAsterisk(tableName)
- {{range .Fields -}}
- {{if not .IsRelation -}}
- {{- if .ColumnName -}}_{{$.QueryStructName}}.{{.Name}} = field.New{{.GenType}}(tableName, "{{.ColumnName}}"){{- end -}}
- {{- else -}}
- _{{$.QueryStructName}}.{{.Relation.Name}} = {{$.QueryStructName}}{{.Relation.RelationshipName}}{{.Relation.Name}}{
- db: db.Session(&gorm.Session{}),
-
- {{.Relation.StructFieldInit}}
- }
- {{end}}
- {{end}}
-
- _{{$.QueryStructName}}.fillFieldMap()
-
- return _{{.QueryStructName}}
- }
- `
- fields = `
- ALL field.Asterisk
- {{range .Fields -}}
- {{if not .IsRelation -}}
- {{if .MultilineComment -}}
- /*
-{{.ColumnComment}}
- */
- {{end -}}
- {{- if .ColumnName -}}{{.Name}} field.{{.GenType}}{{if not .MultilineComment}}{{if .ColumnComment}}// {{.ColumnComment}}{{end}}{{end}}{{- end -}}
- {{- else -}}
- {{.Relation.Name}} {{$.QueryStructName}}{{.Relation.RelationshipName}}{{.Relation.Name}}
- {{end}}
- {{end}}
-
- fieldMap map[string]field.Expr
-`
- tableMethod = `
-func ({{.S}} {{.QueryStructName}}) Table(newTableName string) *{{.QueryStructName}} {
- {{.S}}.{{.QueryStructName}}Do.UseTable(newTableName)
- return {{.S}}.updateTableName(newTableName)
-}
-`
-
- asMethond = `
-func ({{.S}} {{.QueryStructName}}) As(alias string) *{{.QueryStructName}} {
- {{.S}}.{{.QueryStructName}}Do.DO = *({{.S}}.{{.QueryStructName}}Do.As(alias).(*gormgen.DO))
- return {{.S}}.updateTableName(alias)
-}
-`
- updateFieldMethod = `
-func ({{.S}} *{{.QueryStructName}}) updateTableName(table string) *{{.QueryStructName}} {
- {{.S}}.ALL = field.NewAsterisk(table)
- {{range .Fields -}}
- {{if not .IsRelation -}}
- {{- if .ColumnName -}}{{$.S}}.{{.Name}} = field.New{{.GenType}}(table, "{{.ColumnName}}"){{- end -}}
- {{end}}
- {{end}}
-
- {{.S}}.fillFieldMap()
-
- return {{.S}}
-}
-`
-
- cloneMethod = `
-func ({{.S}} {{.QueryStructName}}) clone(db *gorm.DB) {{.QueryStructName}} {
- {{.S}}.{{.QueryStructName}}Do.ReplaceConnPool(db.Statement.ConnPool)
- return {{.S}}
-}
-`
- replaceMethod = `
-func ({{.S}} {{.QueryStructName}}) replaceDB(db *gorm.DB) {{.QueryStructName}} {
- {{.S}}.{{.QueryStructName}}Do.ReplaceDB(db)
- return {{.S}}
-}
-`
- getFieldMethod = `
-func ({{.S}} *{{.QueryStructName}}) GetFieldByName(fieldName string) (field.OrderExpr, bool) {
- _f, ok := {{.S}}.fieldMap[fieldName]
- if !ok || _f == nil {
- return nil, false
- }
- _oe,ok := _f.(field.OrderExpr)
- return _oe,ok
-}
-`
- getFieldExprByName = `
-func ({{.S}} *{{.QueryStructName}}) GetFieldExprByName(fieldName string) (field.Expr, bool) {
- _f, ok := {{.S}}.fieldMap[fieldName]
- if !ok || _f == nil {
- return nil, false
- }
- return _f, ok
-}
-`
- relationship = `{{range .Fields}}{{if .IsRelation}}` +
- `{{- $relation := .Relation }}{{- $relationship := $relation.RelationshipName}}` +
- relationStruct + relationTx +
- `{{end}}{{end}}`
- defineMethodStruct = `type {{.QueryStructName}}Do struct { gormgen.DO }`
-
- fillFieldMapMethod = `
-func ({{.S}} *{{.QueryStructName}}) fillFieldMap() {
- {{.S}}.fieldMap = make(map[string]field.Expr, {{len .Fields}})
- {{range .Fields -}}
- {{if not .IsRelation -}}
- {{- if .ColumnName -}}{{$.S}}.fieldMap["{{.ColumnName}}"] = {{$.S}}.{{.Name}}{{- end -}}
- {{end}}
- {{end -}}
-}
-`
-
- defineDoInterface = `
-
-type I{{.ModelStructName}}Do interface {
- gormgen.SubQuery
- Debug() I{{.ModelStructName}}Do
- WithContext(ctx context.Context) I{{.ModelStructName}}Do
- WithResult(fc func(tx gormgen.Dao)) gormgen.ResultInfo
- ReplaceDB(db *gorm.DB)
- ReadDB() I{{.ModelStructName}}Do
- WriteDB() I{{.ModelStructName}}Do
- As(alias string) gormgen.Dao
- Session(config *gorm.Session) I{{.ModelStructName}}Do
- Columns(cols ...field.Expr) gormgen.Columns
- Clauses(conds ...clause.Expression) I{{.ModelStructName}}Do
- Not(conds ...gormgen.Condition) I{{.ModelStructName}}Do
- Or(conds ...gormgen.Condition) I{{.ModelStructName}}Do
- Select(conds ...field.Expr) I{{.ModelStructName}}Do
- Where(conds ...gormgen.Condition) I{{.ModelStructName}}Do
- Order(conds ...field.Expr) I{{.ModelStructName}}Do
- Distinct(cols ...field.Expr) I{{.ModelStructName}}Do
- Omit(cols ...field.Expr) I{{.ModelStructName}}Do
- Join(table schema.Tabler, on ...field.Expr) I{{.ModelStructName}}Do
- LeftJoin(table schema.Tabler, on ...field.Expr) I{{.ModelStructName}}Do
- RightJoin(table schema.Tabler, on ...field.Expr) I{{.ModelStructName}}Do
- Group(cols ...field.Expr) I{{.ModelStructName}}Do
- Having(conds ...gormgen.Condition) I{{.ModelStructName}}Do
- Limit(limit int) I{{.ModelStructName}}Do
- Offset(offset int) I{{.ModelStructName}}Do
- Count() (count int64, err error)
- Scopes(funcs ...func(gormgen.Dao) gormgen.Dao) I{{.ModelStructName}}Do
- Unscoped() I{{.ModelStructName}}Do
- Create(values ...*{{.StructInfo.Package}}.{{.StructInfo.Type}}) error
- CreateInBatches(values []*{{.StructInfo.Package}}.{{.StructInfo.Type}}, batchSize int) error
- Save(values ...*{{.StructInfo.Package}}.{{.StructInfo.Type}}) error
- First() (*{{.StructInfo.Package}}.{{.StructInfo.Type}}, error)
- Take() (*{{.StructInfo.Package}}.{{.StructInfo.Type}}, error)
- Last() (*{{.StructInfo.Package}}.{{.StructInfo.Type}}, error)
- Find() ([]*{{.StructInfo.Package}}.{{.StructInfo.Type}}, error)
- FindInBatch(batchSize int, fc func(tx gormgen.Dao, batch int) error) (results []*{{.StructInfo.Package}}.{{.StructInfo.Type}}, err error)
- FindInBatches(result *[]*{{.StructInfo.Package}}.{{.StructInfo.Type}}, batchSize int, fc func(tx gormgen.Dao, batch int) error) error
- Pluck(column field.Expr, dest interface{}) error
- Delete(...*{{.StructInfo.Package}}.{{.StructInfo.Type}}) (info gormgen.ResultInfo, err error)
- Update(column field.Expr, value interface{}) (info gormgen.ResultInfo, err error)
- UpdateSimple(columns ...field.AssignExpr) (info gormgen.ResultInfo, err error)
- Updates(value interface{}) (info gormgen.ResultInfo, err error)
- UpdateColumn(column field.Expr, value interface{}) (info gormgen.ResultInfo, err error)
- UpdateColumnSimple(columns ...field.AssignExpr) (info gormgen.ResultInfo, err error)
- UpdateColumns(value interface{}) (info gormgen.ResultInfo, err error)
- UpdateFrom(q gormgen.SubQuery) gormgen.Dao
- Attrs(attrs ...field.AssignExpr) I{{.ModelStructName}}Do
- Assign(attrs ...field.AssignExpr) I{{.ModelStructName}}Do
- Joins(fields ...field.RelationField) I{{.ModelStructName}}Do
- Preload(fields ...field.RelationField) I{{.ModelStructName}}Do
- FirstOrInit() (*{{.StructInfo.Package}}.{{.StructInfo.Type}}, error)
- FirstOrCreate() (*{{.StructInfo.Package}}.{{.StructInfo.Type}}, error)
- FindByPage(offset int, limit int) (result []*{{.StructInfo.Package}}.{{.StructInfo.Type}}, count int64, err error)
- ScanByPage(result interface{}, offset int, limit int) (count int64, err error)
- Scan(result interface{}) (err error)
- Fetch(result interface{}) (err error)
- Returning(value interface{}, columns ...string) I{{.ModelStructName}}Do
- UnderlyingDB() *gorm.DB
- schema.Tabler
-
- {{range .Interfaces -}}
- {{.FuncSign}}
- {{end}}
-}
-`
-)
-
-const (
- relationStruct = `
-type {{$.QueryStructName}}{{$relationship}}{{$relation.Name}} struct{
- db *gorm.DB
-
- field.RelationField
-
- {{$relation.StructField}}
-}
-
-func (a {{$.QueryStructName}}{{$relationship}}{{$relation.Name}}) Where(conds ...field.Expr) *{{$.QueryStructName}}{{$relationship}}{{$relation.Name}} {
- if len(conds) == 0 {
- return &a
- }
-
- exprs := make([]clause.Expression, 0, len(conds))
- for _, cond := range conds {
- exprs = append(exprs, cond.BeCond().(clause.Expression))
- }
- a.db = a.db.Clauses(clause.Where{Exprs: exprs})
- return &a
-}
-
-func (a {{$.QueryStructName}}{{$relationship}}{{$relation.Name}}) WithContext(ctx context.Context) *{{$.QueryStructName}}{{$relationship}}{{$relation.Name}} {
- a.db = a.db.WithContext(ctx)
- return &a
-}
-
-func (a {{$.QueryStructName}}{{$relationship}}{{$relation.Name}}) Session(session *gorm.Session) *{{$.QueryStructName}}{{$relationship}}{{$relation.Name}} {
- a.db = a.db.Session(session)
- return &a
-}
-
-func (a {{$.QueryStructName}}{{$relationship}}{{$relation.Name}}) Model(m *{{$.StructInfo.Package}}.{{$.StructInfo.Type}}) *{{$.QueryStructName}}{{$relationship}}{{$relation.Name}}Tx {
- return &{{$.QueryStructName}}{{$relationship}}{{$relation.Name}}Tx{a.db.Model(m).Association(a.Name())}
-}
-
-`
- relationTx = `
-type {{$.QueryStructName}}{{$relationship}}{{$relation.Name}}Tx struct{ tx *gorm.Association }
-
-func (a {{$.QueryStructName}}{{$relationship}}{{$relation.Name}}Tx) Find() (result {{if eq $relationship "HasMany" "ManyToMany"}}[]{{end}}*{{$relation.Type}}, err error) {
- return result, a.tx.Find(&result)
-}
-
-func (a {{$.QueryStructName}}{{$relationship}}{{$relation.Name}}Tx) Append(values ...*{{$relation.Type}}) (err error) {
- targetValues := make([]interface{}, len(values))
- for i, v := range values {
- targetValues[i] = v
- }
- return a.tx.Append(targetValues...)
-}
-
-func (a {{$.QueryStructName}}{{$relationship}}{{$relation.Name}}Tx) Replace(values ...*{{$relation.Type}}) (err error) {
- targetValues := make([]interface{}, len(values))
- for i, v := range values {
- targetValues[i] = v
- }
- return a.tx.Replace(targetValues...)
-}
-
-func (a {{$.QueryStructName}}{{$relationship}}{{$relation.Name}}Tx) Delete(values ...*{{$relation.Type}}) (err error) {
- targetValues := make([]interface{}, len(values))
- for i, v := range values {
- targetValues[i] = v
- }
- return a.tx.Delete(targetValues...)
-}
-
-func (a {{$.QueryStructName}}{{$relationship}}{{$relation.Name}}Tx) Clear() error {
- return a.tx.Clear()
-}
-
-func (a {{$.QueryStructName}}{{$relationship}}{{$relation.Name}}Tx) Count() int64 {
- return a.tx.Count()
-}
-`
-)
diff --git a/toolkit/gormgen/internal/template/svcimpl.go b/toolkit/gormgen/internal/template/svcimpl.go
deleted file mode 100644
index 220b98ff..00000000
--- a/toolkit/gormgen/internal/template/svcimpl.go
+++ /dev/null
@@ -1,31 +0,0 @@
-package template
-
-const SvcImpl = EditMarkForGDD + `
-package service
-
-import ()
-
-var _ {{.InterfaceName}} = (*{{.InterfaceName}}Impl)(nil)
-
-type {{.InterfaceName}}Impl struct {
- conf *config.Config
- pg *paginate.Pagination
- q *query.Query
-}
-
-func New{{.InterfaceName}}(conf *config.Config) *{{.InterfaceName}}Impl {
- pg := paginate.New(&paginate.Config{
- FieldSelectorEnabled: true,
- })
- return &{{.InterfaceName}}Impl{
- conf: conf,
- pg: pg,
- q: query.Q,
- }
-}
-
-func (receiver {{.InterfaceName}}Impl) clone(q *query.Query) *{{.InterfaceName}}Impl {
- receiver.q = q
- return &receiver
-}
-`
diff --git a/toolkit/gormgen/internal/template/svcimplimport.go b/toolkit/gormgen/internal/template/svcimplimport.go
deleted file mode 100644
index e4a4c53a..00000000
--- a/toolkit/gormgen/internal/template/svcimplimport.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package template
-
-const SvcImplImport = `
- "context"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/errorx"
- "{{.ConfigPackage}}"
- "{{.DtoPackage}}"
- "{{.ModelPackage}}"
- "{{.QueryPackage}}"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
- "github.com/unionj-cloud/go-doudou/v2/framework/database"
- paginate "github.com/unionj-cloud/go-doudou/v2/toolkit/pagination/gorm"
- "github.com/pkg/errors"
- pb "{{.TransportGrpcPackage}}"
-`
diff --git a/toolkit/gormgen/internal/template/svcimplimportgrpc.go b/toolkit/gormgen/internal/template/svcimplimportgrpc.go
deleted file mode 100644
index 9e89ee40..00000000
--- a/toolkit/gormgen/internal/template/svcimplimportgrpc.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package template
-
-const SvcImplImportGrpc = `
- "github.com/golang/protobuf/ptypes/wrappers"
- "google.golang.org/protobuf/proto"
- "google.golang.org/protobuf/types/known/anypb"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
- "google.golang.org/protobuf/types/known/emptypb"
- pb "{{.TransportGrpcPackage}}"
-`
diff --git a/toolkit/gormgen/internal/utils/common.go b/toolkit/gormgen/internal/utils/common.go
deleted file mode 100644
index d4b585bf..00000000
--- a/toolkit/gormgen/internal/utils/common.go
+++ /dev/null
@@ -1 +0,0 @@
-package utils
diff --git a/toolkit/gormgen/internal/utils/pools/export.go b/toolkit/gormgen/internal/utils/pools/export.go
deleted file mode 100644
index fa886635..00000000
--- a/toolkit/gormgen/internal/utils/pools/export.go
+++ /dev/null
@@ -1,9 +0,0 @@
-// Package pools : goroutine pools
-package pools
-
-// NewPool return a new pool
-func NewPool(size int) Pool {
- var p pool
- p.Init(size)
- return &p
-}
diff --git a/toolkit/gormgen/internal/utils/pools/pool.go b/toolkit/gormgen/internal/utils/pools/pool.go
deleted file mode 100644
index a84e489c..00000000
--- a/toolkit/gormgen/internal/utils/pools/pool.go
+++ /dev/null
@@ -1,73 +0,0 @@
-package pools
-
-import "sync"
-
-// Pool goroutine pool
-type Pool interface {
- // Wait 等待令牌
- Wait()
- // Done 归还令牌
- Done()
- // Num 当前发放的令牌书
- Num() int
- // Size 总令牌数
- Size() int
-
- // WaitAll 同步等待令牌全部归还
- WaitAll()
- // AsyncWaitAll 异步等待令牌全部归还
- AsyncWaitAll() <-chan struct{}
-}
-
-type pool struct {
- pool chan struct{}
-
- wg sync.WaitGroup
-}
-
-func (p *pool) Init(size int) {
- if size >= 0 {
- p.pool = make(chan struct{}, size)
- }
-}
-
-func (p *pool) Wait() {
- if p.pool != nil {
- p.wg.Add(1)
- p.pool <- struct{}{}
- }
-}
-
-func (p *pool) Done() {
- if p.pool != nil {
- <-p.pool
- p.wg.Done()
- }
-}
-
-func (p *pool) Num() int {
- if p.pool != nil {
- return len(p.pool)
- }
- return 0
-}
-
-func (p *pool) Size() int {
- if p.pool != nil {
- return cap(p.pool)
- }
- return 0
-}
-
-func (p *pool) WaitAll() {
- p.wg.Wait()
-}
-
-func (p *pool) AsyncWaitAll() <-chan struct{} {
- sig := make(chan struct{})
- go func() {
- p.WaitAll()
- sig <- struct{}{}
- }()
- return sig
-}
diff --git a/toolkit/gormgen/sec_check.go b/toolkit/gormgen/sec_check.go
deleted file mode 100644
index 621cd5e8..00000000
--- a/toolkit/gormgen/sec_check.go
+++ /dev/null
@@ -1,114 +0,0 @@
-package gormgen
-
-import (
- "errors"
- "fmt"
- "strings"
-
- "github.com/wubin1989/dbresolver"
- "github.com/wubin1989/gorm/clause"
- "github.com/wubin1989/hints"
-)
-
-func checkConds(conds []clause.Expression) error {
- for _, cond := range conds {
- if err := CheckClause(cond); err != nil {
- return err
- }
- }
- return nil
-}
-
-var banClauses = map[string]bool{
- // "INSERT": true,
- "VALUES": true,
- // "ON CONFLICT": true,
- "SELECT": true,
- "FROM": true,
- "WHERE": true,
- "GROUP BY": true,
- "ORDER BY": true,
- "LIMIT": true,
- // "FOR": true,
- "UPDATE": true,
- "SET": true,
- "DELETE": true,
-}
-
-// CheckClause check security of Expression
-func CheckClause(cond clause.Expression) error {
- switch cond := cond.(type) {
- case hints.Hints, hints.IndexHint, dbresolver.Operation:
- return nil
- case clause.OnConflict:
- return checkOnConflict(cond)
- case clause.Locking:
- return checkLocking(cond)
- case clause.Insert:
- return checkInsert(cond)
- case clause.Interface:
- if banClauses[cond.Name()] {
- return fmt.Errorf("clause %s is banned", cond.Name())
- }
- return nil
- }
- return fmt.Errorf("unknown clause %v", cond)
-}
-
-func checkOnConflict(c clause.OnConflict) error {
- for _, item := range c.DoUpdates {
- switch item.Value.(type) {
- case clause.Expr, *clause.Expr:
- return errors.New("OnConflict clause assignment with gorm.Expr is banned for security reasons for now")
- }
- }
- return nil
-}
-
-func checkLocking(c clause.Locking) error {
- if strength := strings.ToUpper(strings.TrimSpace(c.Strength)); strength != "UPDATE" && strength != "SHARE" {
- return errors.New("Locking clause's Strength only allow assignments of UPDATE/SHARE")
- }
- if c.Table.Raw {
- return errors.New("Locking clause's Table cannot be set Raw==true")
- }
- if options := strings.ToUpper(strings.TrimSpace(c.Options)); options != "" && options != "NOWAIT" && options != "SKIP LOCKED" {
- return errors.New("Locking clause's Options only allow assignments of NOWAIT/SKIP LOCKED for now")
- }
- return nil
-}
-
-// checkInsert check if clause.Insert is safe
-// https://dev.mysql.com/doc/refman/8.0/en/sql-statements.html#insert
-func checkInsert(c clause.Insert) error {
- if c.Table.Raw == true {
- return errors.New("Table Raw cannot be true")
- }
-
- if c.Modifier == "" {
- return nil
- }
-
- var priority, ignore string
- if modifiers := strings.SplitN(strings.ToUpper(strings.TrimSpace(c.Modifier)), " ", 2); len(modifiers) == 2 {
- priority, ignore = strings.TrimSpace(modifiers[0]), strings.TrimSpace(modifiers[1])
- } else {
- ignore = strings.TrimSpace(modifiers[0])
- }
- if priority != "" && !in(priority, "LOW_PRIORITY", "DELAYED", "HIGH_PRIORITY") {
- return errors.New("invalid priority value")
- }
- if ignore != "" && ignore != "IGNORE" {
- return errors.New("invalid modifiers value, should be IGNORE")
- }
- return nil
-}
-
-func in(s string, v ...string) bool {
- for _, vv := range v {
- if vv == s {
- return true
- }
- }
- return false
-}
diff --git a/toolkit/gormgen/tests/go.sum b/toolkit/gormgen/tests/go.sum
deleted file mode 100644
index 4080a7af..00000000
--- a/toolkit/gormgen/tests/go.sum
+++ /dev/null
@@ -1,2231 +0,0 @@
-bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8=
-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.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.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg=
-cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8=
-cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0=
-cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY=
-cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM=
-cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY=
-cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ=
-cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI=
-cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4=
-cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc=
-cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA=
-cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A=
-cloud.google.com/go v0.102.0/go.mod h1:oWcCzKlqJ5zgHQt9YsaeTY9KzIvjyy0ArmiBUgpQ+nc=
-cloud.google.com/go v0.102.1/go.mod h1:XZ77E9qnTEnrgEOvr4xzfdX5TRo7fB4T2F4O6+34hIU=
-cloud.google.com/go v0.104.0/go.mod h1:OO6xxXdJyvuJPcEPBLN9BJPD+jep5G1+2U5B5gkRYtA=
-cloud.google.com/go/aiplatform v1.22.0/go.mod h1:ig5Nct50bZlzV6NvKaTwmplLLddFx0YReh9WfTO5jKw=
-cloud.google.com/go/aiplatform v1.24.0/go.mod h1:67UUvRBKG6GTayHKV8DBv2RtR1t93YRu5B1P3x99mYY=
-cloud.google.com/go/analytics v0.11.0/go.mod h1:DjEWCu41bVbYcKyvlws9Er60YE4a//bK6mnhWvQeFNI=
-cloud.google.com/go/analytics v0.12.0/go.mod h1:gkfj9h6XRf9+TS4bmuhPEShsh3hH8PAZzm/41OOhQd4=
-cloud.google.com/go/area120 v0.5.0/go.mod h1:DE/n4mp+iqVyvxHN41Vf1CR602GiHQjFPusMFW6bGR4=
-cloud.google.com/go/area120 v0.6.0/go.mod h1:39yFJqWVgm0UZqWTOdqkLhjoC7uFfgXRC8g/ZegeAh0=
-cloud.google.com/go/artifactregistry v1.6.0/go.mod h1:IYt0oBPSAGYj/kprzsBjZ/4LnG/zOcHyFHjWPCi6SAQ=
-cloud.google.com/go/artifactregistry v1.7.0/go.mod h1:mqTOFOnGZx8EtSqK/ZWcsm/4U8B77rbcLP6ruDU2Ixk=
-cloud.google.com/go/asset v1.5.0/go.mod h1:5mfs8UvcM5wHhqtSv8J1CtxxaQq3AdBxxQi2jGW/K4o=
-cloud.google.com/go/asset v1.7.0/go.mod h1:YbENsRK4+xTiL+Ofoj5Ckf+O17kJtgp3Y3nn4uzZz5s=
-cloud.google.com/go/assuredworkloads v1.5.0/go.mod h1:n8HOZ6pff6re5KYfBXcFvSViQjDwxFkAkmUFffJRbbY=
-cloud.google.com/go/assuredworkloads v1.6.0/go.mod h1:yo2YOk37Yc89Rsd5QMVECvjaMKymF9OP+QXWlKXUkXw=
-cloud.google.com/go/automl v1.5.0/go.mod h1:34EjfoFGMZ5sgJ9EoLsRtdPSNZLcfflJR39VbVNS2M0=
-cloud.google.com/go/automl v1.6.0/go.mod h1:ugf8a6Fx+zP0D59WLhqgTDsQI9w07o64uf/Is3Nh5p8=
-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/bigquery v1.42.0/go.mod h1:8dRTJxhtG+vwBKzE5OseQn/hiydoQN3EedCaOdYmxRA=
-cloud.google.com/go/billing v1.4.0/go.mod h1:g9IdKBEFlItS8bTtlrZdVLWSSdSyFUZKXNS02zKMOZY=
-cloud.google.com/go/billing v1.5.0/go.mod h1:mztb1tBc3QekhjSgmpf/CV4LzWXLzCArwpLmP2Gm88s=
-cloud.google.com/go/binaryauthorization v1.1.0/go.mod h1:xwnoWu3Y84jbuHa0zd526MJYmtnVXn0syOjaJgy4+dM=
-cloud.google.com/go/binaryauthorization v1.2.0/go.mod h1:86WKkJHtRcv5ViNABtYMhhNWRrD1Vpi//uKEy7aYEfI=
-cloud.google.com/go/cloudtasks v1.5.0/go.mod h1:fD92REy1x5woxkKEkLdvavGnPJGEn8Uic9nWuLzqCpY=
-cloud.google.com/go/cloudtasks v1.6.0/go.mod h1:C6Io+sxuke9/KNRkbQpihnW93SWDU3uXt92nu85HkYI=
-cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow=
-cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM=
-cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M=
-cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s=
-cloud.google.com/go/compute v1.6.1/go.mod h1:g85FgpzFvNULZ+S8AYq87axRKuf2Kh7deLqV/jJ3thU=
-cloud.google.com/go/compute v1.7.0/go.mod h1:435lt8av5oL9P3fv1OEzSbSUe+ybHXGMPQHHZWZxy9U=
-cloud.google.com/go/compute/metadata v0.2.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k=
-cloud.google.com/go/containeranalysis v0.5.1/go.mod h1:1D92jd8gRR/c0fGMlymRgxWD3Qw9C1ff6/T7mLgVL8I=
-cloud.google.com/go/containeranalysis v0.6.0/go.mod h1:HEJoiEIu+lEXM+k7+qLCci0h33lX3ZqoYFdmPcoO7s4=
-cloud.google.com/go/datacatalog v1.3.0/go.mod h1:g9svFY6tuR+j+hrTw3J2dNcmI0dzmSiyOzm8kpLq0a0=
-cloud.google.com/go/datacatalog v1.5.0/go.mod h1:M7GPLNQeLfWqeIm3iuiruhPzkt65+Bx8dAKvScX8jvs=
-cloud.google.com/go/datacatalog v1.6.0/go.mod h1:+aEyF8JKg+uXcIdAmmaMUmZ3q1b/lKLtXCmXdnc0lbc=
-cloud.google.com/go/dataflow v0.6.0/go.mod h1:9QwV89cGoxjjSR9/r7eFDqqjtvbKxAK2BaYU6PVk9UM=
-cloud.google.com/go/dataflow v0.7.0/go.mod h1:PX526vb4ijFMesO1o202EaUmouZKBpjHsTlCtB4parQ=
-cloud.google.com/go/dataform v0.3.0/go.mod h1:cj8uNliRlHpa6L3yVhDOBrUXH+BPAO1+KFMQQNSThKo=
-cloud.google.com/go/dataform v0.4.0/go.mod h1:fwV6Y4Ty2yIFL89huYlEkwUPtS7YZinZbzzj5S9FzCE=
-cloud.google.com/go/datalabeling v0.5.0/go.mod h1:TGcJ0G2NzcsXSE/97yWjIZO0bXj0KbVlINXMG9ud42I=
-cloud.google.com/go/datalabeling v0.6.0/go.mod h1:WqdISuk/+WIGeMkpw/1q7bK/tFEZxsrFJOJdY2bXvTQ=
-cloud.google.com/go/dataqna v0.5.0/go.mod h1:90Hyk596ft3zUQ8NkFfvICSIfHFh1Bc7C4cK3vbhkeo=
-cloud.google.com/go/dataqna v0.6.0/go.mod h1:1lqNpM7rqNLVgWBJyk5NF6Uen2PHym0jtVJonplVsDA=
-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/datastream v1.2.0/go.mod h1:i/uTP8/fZwgATHS/XFu0TcNUhuA0twZxxQ3EyCUQMwo=
-cloud.google.com/go/datastream v1.3.0/go.mod h1:cqlOX8xlyYF/uxhiKn6Hbv6WjwPPuI9W2M9SAXwaLLQ=
-cloud.google.com/go/dialogflow v1.15.0/go.mod h1:HbHDWs33WOGJgn6rfzBW1Kv807BE3O1+xGbn59zZWI4=
-cloud.google.com/go/dialogflow v1.16.1/go.mod h1:po6LlzGfK+smoSmTBnbkIZY2w8ffjz/RcGSS+sh1el0=
-cloud.google.com/go/documentai v1.7.0/go.mod h1:lJvftZB5NRiFSX4moiye1SMxHx0Bc3x1+p9e/RfXYiU=
-cloud.google.com/go/documentai v1.8.0/go.mod h1:xGHNEB7CtsnySCNrCFdCyyMz44RhFEEX2Q7UD0c5IhU=
-cloud.google.com/go/domains v0.6.0/go.mod h1:T9Rz3GasrpYk6mEGHh4rymIhjlnIuB4ofT1wTxDeT4Y=
-cloud.google.com/go/domains v0.7.0/go.mod h1:PtZeqS1xjnXuRPKE/88Iru/LdfoRyEHYA9nFQf4UKpg=
-cloud.google.com/go/edgecontainer v0.1.0/go.mod h1:WgkZ9tp10bFxqO8BLPqv2LlfmQF1X8lZqwW4r1BTajk=
-cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
-cloud.google.com/go/functions v1.6.0/go.mod h1:3H1UA3qiIPRWD7PeZKLvHZ9SaQhR26XIJcC0A5GbvAk=
-cloud.google.com/go/functions v1.7.0/go.mod h1:+d+QBcWM+RsrgZfV9xo6KfA1GlzJfxcfZcRPEhDDfzg=
-cloud.google.com/go/gaming v1.5.0/go.mod h1:ol7rGcxP/qHTRQE/RO4bxkXq+Fix0j6D4LFPzYTIrDM=
-cloud.google.com/go/gaming v1.6.0/go.mod h1:YMU1GEvA39Qt3zWGyAVA9bpYz/yAhTvaQ1t2sK4KPUA=
-cloud.google.com/go/gkeconnect v0.5.0/go.mod h1:c5lsNAg5EwAy7fkqX/+goqFsU1Da/jQFqArp+wGNr/o=
-cloud.google.com/go/gkeconnect v0.6.0/go.mod h1:Mln67KyU/sHJEBY8kFZ0xTeyPtzbq9StAVvEULYK16A=
-cloud.google.com/go/gkehub v0.9.0/go.mod h1:WYHN6WG8w9bXU0hqNxt8rm5uxnk8IH+lPY9J2TV7BK0=
-cloud.google.com/go/gkehub v0.10.0/go.mod h1:UIPwxI0DsrpsVoWpLB0stwKCP+WFVG9+y977wO+hBH0=
-cloud.google.com/go/grafeas v0.2.0/go.mod h1:KhxgtF2hb0P191HlY5besjYm6MqTSTj3LSI+M+ByZHc=
-cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY=
-cloud.google.com/go/language v1.4.0/go.mod h1:F9dRpNFQmJbkaop6g0JhSBXCNlO90e1KWx5iDdxbWic=
-cloud.google.com/go/language v1.6.0/go.mod h1:6dJ8t3B+lUYfStgls25GusK04NLh3eDLQnWM3mdEbhI=
-cloud.google.com/go/lifesciences v0.5.0/go.mod h1:3oIKy8ycWGPUyZDR/8RNnTOYevhaMLqh5vLUXs9zvT8=
-cloud.google.com/go/lifesciences v0.6.0/go.mod h1:ddj6tSX/7BOnhxCSd3ZcETvtNr8NZ6t/iPhY2Tyfu08=
-cloud.google.com/go/mediatranslation v0.5.0/go.mod h1:jGPUhGTybqsPQn91pNXw0xVHfuJ3leR1wj37oU3y1f4=
-cloud.google.com/go/mediatranslation v0.6.0/go.mod h1:hHdBCTYNigsBxshbznuIMFNe5QXEowAuNmmC7h8pu5w=
-cloud.google.com/go/memcache v1.4.0/go.mod h1:rTOfiGZtJX1AaFUrOgsMHX5kAzaTQ8azHiuDoTPzNsE=
-cloud.google.com/go/memcache v1.5.0/go.mod h1:dk3fCK7dVo0cUU2c36jKb4VqKPS22BTkf81Xq617aWM=
-cloud.google.com/go/metastore v1.5.0/go.mod h1:2ZNrDcQwghfdtCwJ33nM0+GrBGlVuh8rakL3vdPY3XY=
-cloud.google.com/go/metastore v1.6.0/go.mod h1:6cyQTls8CWXzk45G55x57DVQ9gWg7RiH65+YgPsNh9s=
-cloud.google.com/go/networkconnectivity v1.4.0/go.mod h1:nOl7YL8odKyAOtzNX73/M5/mGZgqqMeryi6UPZTk/rA=
-cloud.google.com/go/networkconnectivity v1.5.0/go.mod h1:3GzqJx7uhtlM3kln0+x5wyFvuVH1pIBJjhCpjzSt75o=
-cloud.google.com/go/networksecurity v0.5.0/go.mod h1:xS6fOCoqpVC5zx15Z/MqkfDwH4+m/61A3ODiDV1xmiQ=
-cloud.google.com/go/networksecurity v0.6.0/go.mod h1:Q5fjhTr9WMI5mbpRYEbiexTzROf7ZbDzvzCrNl14nyU=
-cloud.google.com/go/notebooks v1.2.0/go.mod h1:9+wtppMfVPUeJ8fIWPOq1UnATHISkGXGqTkxeieQ6UY=
-cloud.google.com/go/notebooks v1.3.0/go.mod h1:bFR5lj07DtCPC7YAAJ//vHskFBxA5JzYlH68kXVdk34=
-cloud.google.com/go/osconfig v1.7.0/go.mod h1:oVHeCeZELfJP7XLxcBGTMBvRO+1nQ5tFG9VQTmYS2Fs=
-cloud.google.com/go/osconfig v1.8.0/go.mod h1:EQqZLu5w5XA7eKizepumcvWx+m8mJUhEwiPqWiZeEdg=
-cloud.google.com/go/oslogin v1.4.0/go.mod h1:YdgMXWRaElXz/lDk1Na6Fh5orF7gvmJ0FGLIs9LId4E=
-cloud.google.com/go/oslogin v1.5.0/go.mod h1:D260Qj11W2qx/HVF29zBg+0fd6YCSjSqLUkY/qEenQU=
-cloud.google.com/go/phishingprotection v0.5.0/go.mod h1:Y3HZknsK9bc9dMi+oE8Bim0lczMU6hrX0UpADuMefr0=
-cloud.google.com/go/phishingprotection v0.6.0/go.mod h1:9Y3LBLgy0kDTcYET8ZH3bq/7qni15yVUoAxiFxnlSUA=
-cloud.google.com/go/privatecatalog v0.5.0/go.mod h1:XgosMUvvPyxDjAVNDYxJ7wBW8//hLDDYmnsNcMGq1K0=
-cloud.google.com/go/privatecatalog v0.6.0/go.mod h1:i/fbkZR0hLN29eEWiiwue8Pb+GforiEIBnV9yrRUOKI=
-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/recaptchaenterprise v1.3.1/go.mod h1:OdD+q+y4XGeAlxRaMn1Y7/GveP6zmq76byL6tjPE7d4=
-cloud.google.com/go/recaptchaenterprise/v2 v2.1.0/go.mod h1:w9yVqajwroDNTfGuhmOjPDN//rZGySaf6PtFVcSCa7o=
-cloud.google.com/go/recaptchaenterprise/v2 v2.2.0/go.mod h1:/Zu5jisWGeERrd5HnlS3EUGb/D335f9k51B/FVil0jk=
-cloud.google.com/go/recommendationengine v0.5.0/go.mod h1:E5756pJcVFeVgaQv3WNpImkFP8a+RptV6dDLGPILjvg=
-cloud.google.com/go/recommendationengine v0.6.0/go.mod h1:08mq2umu9oIqc7tDy8sx+MNJdLG0fUi3vaSVbztHgJ4=
-cloud.google.com/go/recommender v1.5.0/go.mod h1:jdoeiBIVrJe9gQjwd759ecLJbxCDED4A6p+mqoqDvTg=
-cloud.google.com/go/recommender v1.6.0/go.mod h1:+yETpm25mcoiECKh9DEScGzIRyDKpZ0cEhWGo+8bo+c=
-cloud.google.com/go/redis v1.7.0/go.mod h1:V3x5Jq1jzUcg+UNsRvdmsfuFnit1cfe3Z/PGyq/lm4Y=
-cloud.google.com/go/redis v1.8.0/go.mod h1:Fm2szCDavWzBk2cDKxrkmWBqoCiL1+Ctwq7EyqBCA/A=
-cloud.google.com/go/retail v1.8.0/go.mod h1:QblKS8waDmNUhghY2TI9O3JLlFk8jybHeV4BF19FrE4=
-cloud.google.com/go/retail v1.9.0/go.mod h1:g6jb6mKuCS1QKnH/dpu7isX253absFl6iE92nHwlBUY=
-cloud.google.com/go/scheduler v1.4.0/go.mod h1:drcJBmxF3aqZJRhmkHQ9b3uSSpQoltBPGPxGAWROx6s=
-cloud.google.com/go/scheduler v1.5.0/go.mod h1:ri073ym49NW3AfT6DZi21vLZrG07GXr5p3H1KxN5QlI=
-cloud.google.com/go/secretmanager v1.6.0/go.mod h1:awVa/OXF6IiyaU1wQ34inzQNc4ISIDIrId8qE5QGgKA=
-cloud.google.com/go/security v1.5.0/go.mod h1:lgxGdyOKKjHL4YG3/YwIL2zLqMFCKs0UbQwgyZmfJl4=
-cloud.google.com/go/security v1.7.0/go.mod h1:mZklORHl6Bg7CNnnjLH//0UlAlaXqiG7Lb9PsPXLfD0=
-cloud.google.com/go/security v1.8.0/go.mod h1:hAQOwgmaHhztFhiQ41CjDODdWP0+AE1B3sX4OFlq+GU=
-cloud.google.com/go/securitycenter v1.13.0/go.mod h1:cv5qNAqjY84FCN6Y9z28WlkKXyWsgLO832YiWwkCWcU=
-cloud.google.com/go/securitycenter v1.14.0/go.mod h1:gZLAhtyKv85n52XYWt6RmeBdydyxfPeTrpToDPw4Auc=
-cloud.google.com/go/servicedirectory v1.4.0/go.mod h1:gH1MUaZCgtP7qQiI+F+A+OpeKF/HQWgtAddhTbhL2bs=
-cloud.google.com/go/servicedirectory v1.5.0/go.mod h1:QMKFL0NUySbpZJ1UZs3oFAmdvVxhhxB6eJ/Vlp73dfg=
-cloud.google.com/go/speech v1.6.0/go.mod h1:79tcr4FHCimOp56lwC01xnt/WPJZc4v3gzyT7FoBkCM=
-cloud.google.com/go/speech v1.7.0/go.mod h1:KptqL+BAQIhMsj1kOP2la5DSEEerPDuOP/2mmkhHhZQ=
-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.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y=
-cloud.google.com/go/storage v1.23.0/go.mod h1:vOEEDNFnciUMhBeT6hsJIn3ieU5cFRmzeLgDvXzfIXc=
-cloud.google.com/go/talent v1.1.0/go.mod h1:Vl4pt9jiHKvOgF9KoZo6Kob9oV4lwd/ZD5Cto54zDRw=
-cloud.google.com/go/talent v1.2.0/go.mod h1:MoNF9bhFQbiJ6eFD3uSsg0uBALw4n4gaCaEjBw9zo8g=
-cloud.google.com/go/videointelligence v1.6.0/go.mod h1:w0DIDlVRKtwPCn/C4iwZIJdvC69yInhW0cfi+p546uU=
-cloud.google.com/go/videointelligence v1.7.0/go.mod h1:k8pI/1wAhjznARtVT9U1llUaFNPh7muw8QyOUpavru4=
-cloud.google.com/go/vision v1.2.0/go.mod h1:SmNwgObm5DpFBme2xpyOyasvBc1aPdjvMk2bBk0tKD0=
-cloud.google.com/go/vision/v2 v2.2.0/go.mod h1:uCdV4PpN1S0jyCyq8sIM42v2Y6zOLkZs+4R9LrGYwFo=
-cloud.google.com/go/vision/v2 v2.3.0/go.mod h1:UO61abBx9QRMFkNBbf1D8B1LXdS2cGiiCRx0vSpZoUo=
-cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xXZmFiHmGE=
-cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg=
-cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0=
-cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M=
-dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
-github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8=
-github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.0.0/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
-github.com/Azure/azure-sdk-for-go/sdk/azcore v1.1.2/go.mod h1:uGG2W01BaETf0Ozp+QxxKJdMBNRWPdstHG0Fmdwn1/U=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.0.0/go.mod h1:+6sju8gk8FRmSajX3Oz4G5Gm7P+mbqE9FVaXXFYTkCM=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.1.0/go.mod h1:bhXu1AjYL+wutSL/kpSq6s7733q2Rb0yuot9Zgfqa/0=
-github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.1/go.mod h1:gLa1CL2RNE4s7M3yopJ/p0iq5DdY6Yv5ZUt9MTRZOQM=
-github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
-github.com/Azure/azure-sdk-for-go/sdk/internal v1.0.0/go.mod h1:eWRD7oawr1Mu1sLCawqVc0CUiF43ia3qQMxLscsKQ9w=
-github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
-github.com/Azure/go-ansiterm v0.0.0-20210608223527-2377c96fe795/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8=
-github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=
-github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw=
-github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg=
-github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A=
-github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=
-github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
-github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=
-github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=
-github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=
-github.com/AzureAD/microsoft-authentication-library-for-go v0.4.0/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
-github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
-github.com/AzureAD/microsoft-authentication-library-for-go v0.8.1/go.mod h1:4qFor3D/HDsvBME35Xy9rwW9DecL+M2sNw1ybjPtwA0=
-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/ClickHouse/ch-go v0.47.3/go.mod h1:m3LHc5FeQ1Jjee5EEay5e7hQmSk4SuKyMfifNUz8l3g=
-github.com/ClickHouse/ch-go v0.48.0/go.mod h1:KBY72ltlOlHelc4Jn4hlReP8Caek8d6RG4ZkoPsWxzc=
-github.com/ClickHouse/clickhouse-go v1.5.4/go.mod h1:EaI/sW7Azgz9UATzd5ZdZHRUhHgv5+JMS9NSr2smCJI=
-github.com/ClickHouse/clickhouse-go/v2 v2.3.0/go.mod h1:f2kb1LPopJdIyt0Y0vxNk9aiQCyhCmeVcyvOOaPCT4Q=
-github.com/CloudyKit/fastprinter v0.0.0-20170127035650-74b38d55f37a/go.mod h1:EFZQ978U7x8IRnstaskI3IysnWY5Ao3QgZUKOXlsAdw=
-github.com/CloudyKit/jet v2.1.3-0.20180809161101-62edd43e4f88+incompatible/go.mod h1:HPYO+50pSWkPoj9Q/eq0aRGByCL6ScRlUmiEX5Zgm+w=
-github.com/DataDog/datadog-go v3.2.0+incompatible/go.mod h1:LButxg5PwREeZtORoXG3tL4fMGNddJ+vMq1mwgfaqoQ=
-github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
-github.com/Jeffail/gabs/v2 v2.6.1/go.mod h1:xCn81vdHKxFUuWWAaD5jCTQDNPBMh5pPs9IJ+NcziBI=
-github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKzY=
-github.com/Joker/jade v1.0.1-0.20190614124447-d475f43051e7/go.mod h1:6E6s8o2AE4KhCrqr6GRJjdC/gNfTdxkIXvuGZZda2VM=
-github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs=
-github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA=
-github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA=
-github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw=
-github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
-github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0=
-github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
-github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
-github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84=
-github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
-github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
-github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg=
-github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ=
-github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8=
-github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg=
-github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00=
-github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600=
-github.com/Microsoft/hcsshim v0.8.25/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg=
-github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU=
-github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY=
-github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
-github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
-github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
-github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
-github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
-github.com/Shopify/goreferrer v0.0.0-20181106222321-ec9c9a553398/go.mod h1:a1uqRtAwp2Xwc6WNPJEufxJ7fx3npB4UV/JOLmbu5I0=
-github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ=
-github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg=
-github.com/acomagu/bufpipe v1.0.4/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4=
-github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=
-github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw=
-github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE=
-github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE=
-github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
-github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
-github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
-github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
-github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0=
-github.com/aliyun/alibaba-cloud-sdk-go v1.61.1704/go.mod h1:RcDobYh8k5VP6TNybz9m++gL3ijVI5wueVr0EM10VsU=
-github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
-github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
-github.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
-github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
-github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
-github.com/antlr/antlr4 v0.0.0-20200124162019-2d7f727a00b7/go.mod h1:T7PbCXFs94rrTttyxjbyT5+/1V8T2TYDejxUfHJjw1Y=
-github.com/apolloconfig/agollo/v4 v4.1.1-0.20220323095621-60ed86180f24/go.mod h1:SuvTjtg0p4UlSzSbik+ibLRr6oR1xRsfy65QzP3GEAs=
-github.com/arl/statsviz v0.5.1/go.mod h1:zDnjgRblGm1Dyd7J5YlbH7gM1/+HRC+SfkhZhQb5AnM=
-github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
-github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
-github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
-github.com/armon/go-metrics v0.4.1/go.mod h1:E6amYzXo6aW1tqzoZGT755KkbgrJsSdpwZ+3JqfkOG4=
-github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
-github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
-github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
-github.com/ascarter/requestid v0.0.0-20170313220838-5b76ab3d4aee/go.mod h1:u7Wtt4WATGGgae9mURNGQQqxAudPKrxfsbSDSGOso+g=
-github.com/auxten/postgresql-parser v1.0.1/go.mod h1:Nf27dtv8EU1C+xNkoLD3zEwfgJfDDVi8Zl86gznxPvI=
-github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
-github.com/aymerick/raymond v2.0.3-0.20180322193309-b565731e1464+incompatible/go.mod h1:osfaiScAUVup+UC9Nfq76eWqDhXlp+4UYaA8uhTBO6g=
-github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
-github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
-github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
-github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
-github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
-github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
-github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
-github.com/bkaradzic/go-lz4 v1.0.0/go.mod h1:0YdlkowM3VswSROI7qDxhRvJ3sLhlFrRRwjwegp5jy4=
-github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
-github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
-github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
-github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
-github.com/bradleyfalzon/ghinstallation/v2 v2.0.3/go.mod h1:tlgi+JWCXnKFx/Y4WtnDbZEINo31N5bcvnCoqieefmk=
-github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk=
-github.com/bsm/ginkgo/v2 v2.5.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
-github.com/bsm/ginkgo/v2 v2.9.5/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
-github.com/bsm/gomega v1.20.0/go.mod h1:JifAceMQ4crZIWYUKrlGcmbN3bqHogVTADMD2ATsbwk=
-github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
-github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s=
-github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
-github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
-github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50=
-github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
-github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
-github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
-github.com/cenkalti/backoff/v4 v4.1.1/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
-github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
-github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
-github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054/go.mod h1:sGbDF6GwGcLpkNXPUTkMRoywsNa/ol15pxFe6ERfguA=
-github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
-github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw=
-github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M=
-github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E=
-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/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
-github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg=
-github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc=
-github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs=
-github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
-github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs=
-github.com/cilium/ebpf v0.7.0/go.mod h1:/oI2+1shJiTGAMgl6/RgJr36Eo1jzrRcAWbcXO2usCA=
-github.com/circonus-labs/circonus-gometrics v2.3.1+incompatible/go.mod h1:nmEj6Dob7S7YxXgwXpfOuvO54S+tGdZdw9fuRZt25Ag=
-github.com/circonus-labs/circonusllhist v0.1.3/go.mod h1:kMXHVDlOchFAehlya5ePtbp5jckzBHf4XRpQvBOLI+I=
-github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
-github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
-github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58/go.mod h1:EOBUe0h4xcZ5GoxqC5SDxFQ8gwyZPKQoEzownBlhI80=
-github.com/cloudflare/tableflip v1.2.3/go.mod h1:P4gRehmV6Z2bY5ao5ml9Pd8u6kuEnlB37pUFMmv7j2E=
-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/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
-github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
-github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
-github.com/cockroachdb/apd v1.1.1-0.20181017181144-bced77f817b4/go.mod h1:mdGz2CnkJrefFtlLevmE7JpL2zB9tKofya/6w7wWzNA=
-github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8=
-github.com/cockroachdb/datadriven v1.0.0/go.mod h1:5Ib8Meh+jk1RlHIXej6Pzevx/NLlNvQB9pmSBZErGA4=
-github.com/cockroachdb/errors v1.6.1/go.mod h1:tm6FTP5G81vwJ5lC0SizQo374JNCOPrHyXGitRJoDqM=
-github.com/cockroachdb/errors v1.8.2/go.mod h1:qGwQn6JmZ+oMjuLwjWzUNqblqk0xl4CVV3SQbGwK7Ac=
-github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
-github.com/cockroachdb/redact v1.0.8/go.mod h1:BVNblN9mBWFyMyqK1k3AAiSxhvhfK2oOZZ2lK+dpvRg=
-github.com/cockroachdb/sentry-go v0.6.1-cockroachdb.2/go.mod h1:8BT+cPK6xvFOcRlk0R8eg+OTkcqI6baNH4xAkpiYVvQ=
-github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM=
-github.com/common-nighthawk/go-figure v0.0.0-20200609044655-c4b36f998cf2/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w=
-github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE=
-github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU=
-github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
-github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU=
-github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E=
-github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
-github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss=
-github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI=
-github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko=
-github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM=
-github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
-github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo=
-github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE=
-github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU=
-github.com/containerd/cgroups v1.0.3/go.mod h1:/ofk34relqNjSGyqPrmEULrO4Sc8LJhvJmWbUCUKqj8=
-github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
-github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw=
-github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE=
-github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw=
-github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ=
-github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
-github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.4.9/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA=
-github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ=
-github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU=
-github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI=
-github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s=
-github.com/containerd/containerd v1.5.18/go.mod h1:7IN9MtIzTZH4WPEmD1gNH8bbTQXVX68yd3ZXxSHYCis=
-github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
-github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
-github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y=
-github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo=
-github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y=
-github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ=
-github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM=
-github.com/containerd/continuity v0.3.0/go.mod h1:wJEAIwKOm/pBZuBd0JmeTvnLquTB1Ag8espWhkykbPM=
-github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
-github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI=
-github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
-github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0=
-github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
-github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4=
-github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU=
-github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk=
-github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
-github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0=
-github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g=
-github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
-github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok=
-github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0=
-github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA=
-github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow=
-github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms=
-github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c=
-github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
-github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY=
-github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
-github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o=
-github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8=
-github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
-github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y=
-github.com/containerd/ttrpc v1.1.0/go.mod h1:XX4ZTnoOId4HklF4edwc4DcqskFZuvXB1Evzy5KFQpQ=
-github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc=
-github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk=
-github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg=
-github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s=
-github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw=
-github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y=
-github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
-github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
-github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY=
-github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
-github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
-github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY=
-github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM=
-github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8=
-github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
-github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4=
-github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY=
-github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
-github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
-github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
-github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
-github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU=
-github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc=
-github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
-github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
-github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
-github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
-github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
-github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
-github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
-github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
-github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4=
-github.com/cyphar/filepath-securejoin v0.2.3/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
-github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ=
-github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s=
-github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8=
-github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I=
-github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
-github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
-github.com/denisenkom/go-mssqldb v0.12.0/go.mod h1:iiK0YP1ZeepvmBQk/QpLEhhTNJgfzrpArPY/aFvc9yU=
-github.com/denisenkom/go-mssqldb v0.12.2/go.mod h1:lnIw1mZukFRZDJYQ0Pb833QS2IaC3l5HkEfra2LJ+sk=
-github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0=
-github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4=
-github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
-github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
-github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
-github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
-github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
-github.com/dmarkham/enumer v1.5.5/go.mod h1:qHwULwuCxYFAFM5KCkpF1U/U0BF5sNQKLccvUzKNY2w=
-github.com/dmarkham/enumer v1.5.6/go.mod h1:eAawajOQnFBxf0NndBKgbqJImkHytg3eFEngUovqgo8=
-github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E=
-github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko=
-github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
-github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY=
-github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v20.10.6+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/docker v20.10.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
-github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=
-github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
-github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA=
-github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI=
-github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
-github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
-github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
-github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM=
-github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
-github.com/dsnet/compress v0.0.2-0.20210315054119-f66993602bf5/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
-github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
-github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
-github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
-github.com/eko/gocache/lib/v4 v4.1.5/go.mod h1:XaNfCwW8KYW1bRZ/KoHA1TugnnkMz0/gT51NDIu7LSY=
-github.com/eko/gocache/lib/v4 v4.1.6-0.20231209210640-002c243623a2/go.mod h1:XaNfCwW8KYW1bRZ/KoHA1TugnnkMz0/gT51NDIu7LSY=
-github.com/eko/gocache/store/go_cache/v4 v4.2.1/go.mod h1:rd6tUbaBOdqgi5xT3+OGeU7lQuhVJGTapNWFlZou524=
-github.com/eko/gocache/store/redis/v4 v4.2.2-0.20231209210640-002c243623a2/go.mod h1:JoLkNA5yeGNQUwINAM9529cDNQCo88WwiKlO9e/+39I=
-github.com/eko/gocache/store/ristretto/v4 v4.2.2-0.20231209210640-002c243623a2/go.mod h1:KyshDyWQqfSVrg2rH06fFQZTj6vG2fxlY7oAW9oxNHY=
-github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
-github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
-github.com/emirpasic/gods v1.18.1/go.mod h1:8tpGGwCnJ5H4r6BWwaV6OrWmMoPhUl5jm/FMNAnJvWQ=
-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/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ=
-github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
-github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
-github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
-github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
-github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
-github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
-github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
-github.com/fatih/color v1.12.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
-github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
-github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/felixge/httpsnoop v1.0.3/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
-github.com/flosch/pongo2 v0.0.0-20190707114632-bbf5a6c351f4/go.mod h1:T9YF2M40nIgbVgp3rreNmTged+9HrbNTIQf1PsaIiTA=
-github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
-github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
-github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
-github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
-github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
-github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA=
-github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY=
-github.com/gavv/httpexpect v2.0.0+incompatible/go.mod h1:x+9tiU1YnrOvnB725RkpoLv1M62hOWzwo5OXotisrKc=
-github.com/getkin/kin-openapi v0.115.0/go.mod h1:l5e9PaFUo9fyLJCPGQeXI2ML8c3P8BHOEV2VaAVf/pc=
-github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
-github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
-github.com/gin-contrib/sse v0.0.0-20190301062529-5545eab6dad3/go.mod h1:VJ0WA2NBN22VlZ2dKZQPAPnyWw5XTlK1KymzLKsr59s=
-github.com/gin-gonic/gin v1.4.0/go.mod h1:OW2EZn3DO8Ln9oIKOvM++LBO+5UPHJJDH72/q/3rZdM=
-github.com/gliderlabs/ssh v0.3.5/go.mod h1:8XB4KraRrX39qHhT6yxPsHedjA08I/uBVwj4xC+/+z4=
-github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
-github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
-github.com/go-faster/city v1.0.1/go.mod h1:jKcUJId49qdW3L1qKHH/3wPeUstCVpVSXTM6vO3VcTw=
-github.com/go-faster/errors v0.6.1/go.mod h1:5MGV2/2T9yvlrbhe9pD9LO5Z/2zCSq2T8j+Jpi2LAyY=
-github.com/go-git/gcfg v1.5.0/go.mod h1:5m20vg6GwYabIxaOonVkTdrILxQMpEShl1xiMF4ua+E=
-github.com/go-git/go-billy/v5 v5.3.1/go.mod h1:pmpqyWchKfYfrkb/UVH4otLvyi/5gJlGI4Hb3ZqZ3W0=
-github.com/go-git/go-billy/v5 v5.4.1/go.mod h1:vjbugF6Fz7JIflbVpl1hJsGjSHNltrSw45YK/ukIvQg=
-github.com/go-git/go-git-fixtures/v4 v4.3.1/go.mod h1:8LHG1a3SRW71ettAD/jW13h8c6AqjVSeL11RAdgaqpo=
-github.com/go-git/go-git/v5 v5.6.1/go.mod h1:mvyoL6Unz0PiTQrGQfSfiLFhBH1c1e84ylC2MDs4ee8=
-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-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
-github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
-github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
-github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
-github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
-github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
-github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
-github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
-github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
-github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
-github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
-github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
-github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
-github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AEU963A2AYjv4d1V5eVL1CQbEJq6aCNHDDjibzu8=
-github.com/go-ole/go-ole v1.2.4/go.mod h1:XCwSNxSkXRo4vlyPy93sltvi/qJq0jqQhjqQNIwKuxM=
-github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
-github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
-github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
-github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
-github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
-github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
-github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
-github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
-github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
-github.com/go-playground/form/v4 v4.2.0/go.mod h1:q1a2BY+AQUUzhl6xA/6hBetay6dEIhMHjgvJiGo6K7U=
-github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
-github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
-github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
-github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
-github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
-github.com/go-playground/validator/v10 v10.11.2/go.mod h1:NieE624vt4SCTJtD87arVLvdmjPAeV8BQlHtMnw9D7s=
-github.com/go-redis/redis v6.15.9+incompatible/go.mod h1:NAIEuMOZ/fxfXJIrKDQDz8wamY7mA7PouImQ2Jvg6kA=
-github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
-github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
-github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
-github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
-github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
-github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
-github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
-github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
-github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
-github.com/go-zookeeper/zk v1.0.3/go.mod h1:nOB03cncLtlp4t+UAkGSV+9beXP/akpekBwL+UX1Qcw=
-github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
-github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
-github.com/gobwas/pool v0.2.0/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
-github.com/gobwas/ws v1.0.2/go.mod h1:szmBTxLgaFppYjEmNtny/v3w89xOydFnnZMcgRRu/EM=
-github.com/goccy/go-yaml v1.11.0/go.mod h1:H+mJrWtjPTJAHvRbV09MCK9xYwODM+wRTVFFTWckfng=
-github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
-github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw=
-github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4=
-github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/godbus/dbus/v5 v5.0.6/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
-github.com/gogo/googleapis v0.0.0-20180223154316-0cd9801be74a/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s=
-github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU=
-github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c=
-github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
-github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
-github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o=
-github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
-github.com/gogo/status v1.1.0/go.mod h1:BFv9nrluPLmrS0EmGVvLaPNmRosr9KapBYd5/hpY1WM=
-github.com/goji/httpauth v0.0.0-20160601135302-2da839ab0f4d/go.mod h1:nnjvkQ9ptGaCkuDUx6wNykzzlUixGxvkme+H/lnzb+A=
-github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
-github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
-github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=
-github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
-github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
-github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
-github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+wXQnTPR4KqTKDgJukSZ6amVRtWMPEjE6sQoK8=
-github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
-github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k=
-github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
-github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-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/groupcache v0.0.0-20210331224755-41bb18bfe9da/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/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8=
-github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
-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/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
-github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM=
-github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
-github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
-github.com/gomodule/redigo v1.7.1-0.20190724094224-574c33c3df38/go.mod h1:B4C85qUVwatsJoIUNIfCRsp7qO0iAmpGFZ4EELWSbC4=
-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/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=
-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.3/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.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
-github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE=
-github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-github/v39 v39.0.0/go.mod h1:C1s8C5aCC9L+JXIYpJM5GYytdX52vC1bLvHEF1IhBrE=
-github.com/google/go-github/v42 v42.0.0/go.mod h1:jgg/jvyI0YlDOM1/ps6XYh04HNQ3vKf0CVko62/EhRg=
-github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
-github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
-github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
-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/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk=
-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-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
-github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
-github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
-github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
-github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
-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/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0=
-github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM=
-github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM=
-github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM=
-github.com/googleapis/gax-go/v2 v2.4.0/go.mod h1:XOTVJ59hdnfJLIP/dh8n5CGryZR2LxK9wbMD5+iXC6c=
-github.com/googleapis/gax-go/v2 v2.5.1/go.mod h1:h6B0KMMFNtI2ddbGJn3T3ZbwkeT6yqEF02fYlzkUCyo=
-github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg=
-github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4=
-github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
-github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
-github.com/gorilla/handlers v1.4.2/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ=
-github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
-github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
-github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
-github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
-github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
-github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ=
-github.com/gorilla/websocket v1.4.1/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
-github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
-github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=
-github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
-github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
-github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
-github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0=
-github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
-github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
-github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
-github.com/hashicorp/go-cleanhttp v0.5.0/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
-github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-immutable-radix v1.3.1/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
-github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
-github.com/hashicorp/go-msgpack v1.1.5/go.mod h1:gWVc3sv/wbDmR3rQsj1CAktEZzoz1YNK9NfGLXJ69/4=
-github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I=
-github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
-github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
-github.com/hashicorp/go-retryablehttp v0.5.3/go.mod h1:9B5zBasrRhHXnJnui7y6sL7es7NDiJgTc6Er0maI1Xs=
-github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
-github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
-github.com/hashicorp/go-sockaddr v1.0.2/go.mod h1:rB4wwRAUzs07qva3c5SdrY/NEtAUjGlgmH/UkBUC97A=
-github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
-github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
-github.com/hashicorp/go-version v1.2.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
-github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
-github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
-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/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
-github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
-github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
-github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
-github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
-github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
-github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
-github.com/hydrogen18/memlistener v0.0.0-20141126152155-54553eb933fb/go.mod h1:qEIFzExnS6016fRpRfxrExeVn2gbClQA99gQhnIcdhE=
-github.com/hyperjumptech/jiffy v1.0.0/go.mod h1:iFHHUap4onOTcvqBBU0iF33snPmqz4DSA/KgnBHG7dU=
-github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho=
-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/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA=
-github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
-github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg=
-github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
-github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
-github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
-github.com/invopop/yaml v0.1.0/go.mod h1:2XuRLgs/ouIrW3XNzuNj7J3Nvu/Dig5MXvbCEdiBN3Q=
-github.com/iris-contrib/blackfriday v2.0.0+incompatible/go.mod h1:UzZ2bDEoaSGPbkg6SAB4att1aAwTmVIx/5gCVqeyUdI=
-github.com/iris-contrib/go.uuid v2.0.0+incompatible/go.mod h1:iz2lgM/1UnEf1kP0L/+fafWORmlnuysV2EMP8MW+qe0=
-github.com/iris-contrib/i18n v0.0.0-20171121225848-987a633949d0/go.mod h1:pMCz62A0xJL6I+umB2YTlFRwWXaDFA0jy+5HzGiJjqI=
-github.com/iris-contrib/schema v0.0.1/go.mod h1:urYA3uvUNG1TIIjOSCzHr9/LmbQo8LrOcOqfqxa4hXw=
-github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA=
-github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo=
-github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
-github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk=
-github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA=
-github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE=
-github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s=
-github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o=
-github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY=
-github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
-github.com/jackc/pgconn v1.11.0/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI=
-github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI=
-github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8=
-github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE=
-github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c=
-github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak=
-github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
-github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78=
-github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA=
-github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg=
-github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
-github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM=
-github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgproto3/v2 v2.2.0/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA=
-github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E=
-github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
-github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg=
-github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc=
-github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw=
-github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM=
-github.com/jackc/pgtype v1.10.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
-github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4=
-github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y=
-github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM=
-github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc=
-github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs=
-github.com/jackc/pgx/v4 v4.15.0/go.mod h1:D/zyOyXiaM1TmVWnOM18p0xdDtdakRBa0RsVGI3U3bw=
-github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw=
-github.com/jackc/pgx/v5 v5.3.0/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
-github.com/jackc/pgx/v5 v5.3.1/go.mod h1:t3JDKnCBlYIc0ewLF0Q7B8MXmoIaBOZj/ic7iHozM/8=
-github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
-github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
-github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
-github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
-github.com/jackc/puddle v1.2.2-0.20220404125616-4e959849469a/go.mod h1:ZQuO1Un86Xpe1ShKl08ERTzYhzWq+OvrvotbpeE3XO0=
-github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
-github.com/jackc/puddle/v2 v2.2.0/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
-github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo=
-github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs=
-github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM=
-github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
-github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg=
-github.com/jcmturner/gokrb5/v8 v8.4.2/go.mod h1:sb+Xq/fTY5yktf/VxLsE3wlfPqQjp0aWNYyvBVK62bc=
-github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
-github.com/jeremywohl/flatten v1.0.1/go.mod h1:4AmD/VxjWcI5SRB0n6szE2A6s2fsNHDLO0nAlMHgfLQ=
-github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4=
-github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
-github.com/jinzhu/now v1.1.2/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
-github.com/jinzhu/now v1.1.4/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
-github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
-github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
-github.com/jmoiron/sqlx v1.2.0/go.mod h1:1FEQNm3xlJgrMD+FBdI9+xvCksHtbpVBBw5dYhBSsks=
-github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
-github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
-github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
-github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
-github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
-github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
-github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
-github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
-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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
-github.com/juju/errors v0.0.0-20181118221551-089d3ea4e4d5/go.mod h1:W54LbzXuIE0boCoNJfwqpmkKJ1O4TCTZMetAt6jGk7Q=
-github.com/juju/loggo v0.0.0-20180524022052-584905176618/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U=
-github.com/juju/testing v0.0.0-20180920084828-472a3e8b2073/go.mod h1:63prj8cnj0tU0S9OHjGJn+b1h0ZghCndfnbQolrYTwA=
-github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
-github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
-github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
-github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=
-github.com/kataras/golog v0.0.9/go.mod h1:12HJgwBIZFNGL0EJnMRhmvGA0PQGx8VFwrZtM4CqbAk=
-github.com/kataras/iris/v12 v12.0.1/go.mod h1:udK4vLQKkdDqMGJJVd/msuMtN6hpYJhg/lSzuxjhO+U=
-github.com/kataras/neffos v0.0.10/go.mod h1:ZYmJC07hQPW67eKuzlfY7SO3bC0mw83A3j6im82hfqw=
-github.com/kataras/pio v0.0.0-20190103105442-ea782b38602d/go.mod h1:NV88laa9UiiDuX9AhMbDPkGYSPugBOV6yTZB1l2K9Z0=
-github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg=
-github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM=
-github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
-github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00=
-github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
-github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
-github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.8.2/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.9.0/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
-github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
-github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
-github.com/klauspost/compress v1.15.11/go.mod h1:QPwzmACJjUTFsnSHH934V6woptycfrDDJnH7hvFVbGM=
-github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
-github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/klauspost/cpuid v1.2.1/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
-github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
-github.com/klauspost/pgzip v1.2.6/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
-github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
-github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
-github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
-github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
-github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
-github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
-github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
-github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
-github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw=
-github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
-github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
-github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
-github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvfxNnFqi74g=
-github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
-github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
-github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY=
-github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
-github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
-github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
-github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
-github.com/lithammer/shortuuid/v4 v4.0.0/go.mod h1:Zs8puNcrvf2rV9rTH51ZLLcj7ZXqQI3lv67aw4KiB1Y=
-github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
-github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
-github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
-github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
-github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
-github.com/manifoldco/promptui v0.9.0/go.mod h1:ka04sppxSGFAtxX0qhlYQjISsg9mR4GWtQEhdbn6Pgg=
-github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho=
-github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA=
-github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
-github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ=
-github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
-github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
-github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
-github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
-github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
-github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
-github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
-github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
-github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
-github.com/mattn/go-runewidth v0.0.10/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
-github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
-github.com/mattn/go-sqlite3 v1.9.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
-github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v1.14.9/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v1.14.12/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
-github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
-github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
-github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
-github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
-github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
-github.com/mediocregopher/mediocre-go-lib v0.0.0-20181029021733-cb65787f37ed/go.mod h1:dSsfyI2zABAdhcbvkXqgxOxrCsbYeHCPgrZkku60dSg=
-github.com/mediocregopher/radix/v3 v3.3.0/go.mod h1:EmfVyvspXz1uZEyPBMyGK+kjWiKQGvsUt6O3Pj+LDCQ=
-github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
-github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
-github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
-github.com/microsoft/go-mssqldb v0.19.0/go.mod h1:ukJCBnnzLzpVF0qYRT+eg1e+eSwjeQ7IvenUv8QPook=
-github.com/microsoft/go-mssqldb v0.21.0/go.mod h1:+4wZTUnz/SV6nffv+RRRB/ss8jPng5Sho2SmM1l2ts4=
-github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
-github.com/miekg/dns v1.1.54/go.mod h1:uInx36IzPl7FYnDcMeVWxj9byh7DutNykX4G9Sj60FY=
-github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
-github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4=
-github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
-github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
-github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
-github.com/mitchellh/go-wordwrap v1.0.0/go.mod h1:ZXFpozHsX6DPmq2I0TCekCxypsnAUbP2oI0UX1GXzOo=
-github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
-github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
-github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
-github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A=
-github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615/go.mod h1:Ad7oeElCZqA1Ufj0U9/liOF4BtVepxRcTvr2ey7zTvM=
-github.com/mmcloughlin/avo v0.5.0/go.mod h1:ChHFdoV7ql95Wi7vuq2YT1bwCJqiWdZrQ1im3VujLYM=
-github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc=
-github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM=
-github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
-github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A=
-github.com/moby/sys/mountinfo v0.5.0/go.mod h1:3bMD3Rg+zkqx8MRYPi7Pyb0Ie97QEBmdxbhnCLlSvSU=
-github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ=
-github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo=
-github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc=
-github.com/moby/term v0.0.0-20210610120745-9d4ed1856297/go.mod h1:vgPCkQMyxTZ7IDy8SXRufE172gr8+K/JE/7hHFxHW3A=
-github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
-github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
-github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
-github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8=
-github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
-github.com/montanaflynn/stats v0.6.6/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
-github.com/morikuni/aec v0.0.0-20170113033406-39771216ff4c/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
-github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
-github.com/morkid/gocache v1.0.0/go.mod h1:xK+hmoEMjYffIBvjn7DE8WfSd/rF5Kz/G9f20OliMJY=
-github.com/moul/http2curl v1.0.0/go.mod h1:8UbvGypXm98wA/IqH45anm5Y2Z6ep6O31QGOAZ3H0fQ=
-github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
-github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
-github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
-github.com/nats-io/nats.go v1.8.1/go.mod h1:BrFz9vVn0fU3AcH9Vn4Kd7W0NpJ651tD5omQ3M8LwxM=
-github.com/nats-io/nkeys v0.0.2/go.mod h1:dab7URMsZm6Z/jp9Z5UGa87Uutgc2mVpXLC4B7TDb/4=
-github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
-github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM=
-github.com/nfnt/resize v0.0.0-20180221191011-83c6a9932646/go.mod h1:jpp1/29i3P1S/RLdc7JQKbRpFeM1dOBd8T9ki5s+AY8=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
-github.com/nwaples/rardecode v1.1.0/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
-github.com/nwaples/rardecode v1.1.3/go.mod h1:5DzqNKiOdpKKBH87u8VlvAnPZMXcGRhxWkRpHbbfGS0=
-github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
-github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
-github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
-github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
-github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
-github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
-github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
-github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0=
-github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
-github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=
-github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
-github.com/onsi/ginkgo/v2 v2.1.3/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c=
-github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU=
-github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk=
-github.com/onsi/ginkgo/v2 v2.3.0/go.mod h1:Eew0uilEqZmIEZr8JrvYlvOM7Rr6xzTmMV8AyFNU9d0=
-github.com/onsi/ginkgo/v2 v2.4.0/go.mod h1:iHkDK1fKGcBoEHT5W7YBq4RFWaQulw+caOMkAt4OrFo=
-github.com/onsi/ginkgo/v2 v2.5.0/go.mod h1:Luc4sArBICYCS8THh8v3i3i5CuSZO+RaQRaJoeNwomw=
-github.com/onsi/ginkgo/v2 v2.7.0/go.mod h1:yjiuMwPokqY1XauOgju45q3sJt6VzQ/Fict1LFVcsAo=
-github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
-github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
-github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
-github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
-github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc=
-github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
-github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs=
-github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
-github.com/onsi/gomega v1.20.1/go.mod h1:DtrZpjmvpn2mPm4YWQa0/ALMDj9v4YxLgojwPeREyVo=
-github.com/onsi/gomega v1.21.1/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc=
-github.com/onsi/gomega v1.22.1/go.mod h1:x6n7VNe4hw0vkyYUM4mjIXx3JbLiPaBPNgB7PRQ1tuM=
-github.com/onsi/gomega v1.24.0/go.mod h1:Z/NWtiqwBrwUt4/2loMmHL63EDLnYHmVbuBpDr2vQAg=
-github.com/onsi/gomega v1.24.1/go.mod h1:3AOiACssS3/MajrniINInwbfOOtfZvplPzuRSmvt1jM=
-github.com/onsi/gomega v1.25.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM=
-github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
-github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
-github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
-github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U=
-github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0=
-github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0=
-github.com/opencontainers/runc v1.1.5/go.mod h1:1J5XiS+vdZ3wCyZybsuxXZWGrgSr8fFJHLXuG2PsnNg=
-github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0=
-github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs=
-github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE=
-github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo=
-github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8=
-github.com/opencontainers/selinux v1.10.0/go.mod h1:2i0OySw99QjzBBQByd1Gr9gSjvuho1lHsJxIJ3gGbJI=
-github.com/opentracing-contrib/go-stdlib v1.0.0/go.mod h1:qtI1ogk+2JhVPIXVc6q+NHziSmy2W5GbdQZFUHADCBU=
-github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
-github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
-github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
-github.com/pascaldekloe/name v1.0.0/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM=
-github.com/pascaldekloe/name v1.0.1/go.mod h1:Z//MfYJnH4jVpQ9wkclwu2I2MkHmXTlT9wR5UZScttM=
-github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
-github.com/paulmach/orb v0.7.1/go.mod h1:FWRlTgl88VI1RBx/MkrwWDRhQ96ctqMCh8boXhmqB/A=
-github.com/paulmach/protoscan v0.2.1/go.mod h1:SpcSwydNLrxUGSDvXvO0P7g7AuhJ7lcKfDlhJCDw2gY=
-github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
-github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc=
-github.com/perimeterx/marshmallow v1.1.4/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
-github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU=
-github.com/petermattis/goid v0.0.0-20180202154549-b0b1615b78e5/go.mod h1:jvVRKCrJTQWu0XVbaOlby/2lO20uSCHEMzzplHXte1o=
-github.com/pierrec/lz4 v2.0.5+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
-github.com/pierrec/lz4/v4 v4.1.2/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pierrec/lz4/v4 v4.1.16/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pierrec/lz4/v4 v4.1.17/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pierrec/lz4/v4 v4.1.18/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
-github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
-github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
-github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
-github.com/pkg/browser v0.0.0-20210115035449-ce105d075bb4/go.mod h1:N6UoU20jOqggOuDwUaBQpluzLNDqif3kq9z2wpdYEfQ=
-github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
-github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
-github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
-github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
-github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA=
-github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
-github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
-github.com/prometheus/client_golang v0.9.2/go.mod h1:OsXs2jCmiKlQ1lTBmv21f2mNfw4xf/QclQDMrYNZzcM=
-github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
-github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
-github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
-github.com/prometheus/client_golang v1.4.0/go.mod h1:e9GMxYsXl05ICDXkRhurwBS4Q3OK1iX/F2sw+iXX5zU=
-github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
-github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
-github.com/prometheus/client_golang v1.11.1/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
-github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
-github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
-github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk=
-github.com/prometheus/client_golang v1.17.0/go.mod h1:VeL+gMmOAxkS2IqfCq0ZmHSL+LjWfWDUmp1mBz9JgUY=
-github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
-github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
-github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
-github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
-github.com/prometheus/client_model v0.4.1-0.20230718164431-9a2bf3000d16/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU=
-github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.0.0-20181126121408-4724e9255275/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
-github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
-github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
-github.com/prometheus/common v0.9.1/go.mod h1:yhUN8i9wzaXS3w1O07YhxHEBxD+W35wd8bs7vj7HSQ4=
-github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
-github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
-github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
-github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
-github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc=
-github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY=
-github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20181204211112-1dc9a6cbc91a/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
-github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
-github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
-github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
-github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A=
-github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
-github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
-github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
-github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
-github.com/prometheus/procfs v0.11.1/go.mod h1:eesXgaPo1q7lBpVMoMy0ZOFTth9hBn4W/y0/p/ScXhY=
-github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
-github.com/radovskyb/watcher v1.0.7/go.mod h1:78okwvY5wPdzcb1UYnip1pvrZNIVEIh/Cm+ZuvsUYIg=
-github.com/rbretecher/go-postman-collection v0.9.0/go.mod h1:pptkyjdB/sqPycH+CCa1zrA6Wpj2Kc8Nz846qRstVVs=
-github.com/redis/go-redis/v9 v9.0.2/go.mod h1:/xDTe9EF1LM61hek62Poq2nzQSGj0xSrEtEHbBQevps=
-github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
-github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
-github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
-github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
-github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
-github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
-github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
-github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o=
-github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
-github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog=
-github.com/rs/cors v1.9.0/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
-github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
-github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
-github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
-github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
-github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
-github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
-github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
-github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4=
-github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=
-github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
-github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
-github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
-github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
-github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg=
-github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
-github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/sergi/go-diff v1.2.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
-github.com/shirou/gopsutil v2.19.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
-github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
-github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4/go.mod h1:qsXQc7+bwAM3Q1u/4XEfrquwF8Lw7D7y5cD8CuHnfIc=
-github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4=
-github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
-github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
-github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
-github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
-github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc=
-github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
-github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
-github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
-github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
-github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
-github.com/skeema/knownhosts v1.1.0/go.mod h1:sKFq3RD6/TKZkSWn8boUbDC7Qkgcv+8XXijpFO6roag=
-github.com/slok/goresilience v0.2.0/go.mod h1:L6IqqHlxWGTrTyq8WwF8kUY8kOIESZAMWr1xkV0zdZA=
-github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
-github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo=
-github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
-github.com/smartystreets/goconvey v1.7.2/go.mod h1:Vw0tHAZW6lzCRk3xgdin6fKYcG+G3Pg9vgXWeJpQFMM=
-github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
-github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
-github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
-github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
-github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
-github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
-github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
-github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE=
-github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
-github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
-github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
-github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
-github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
-github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE=
-github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
-github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8=
-github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
-github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
-github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
-github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
-github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
-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/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
-github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
-github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
-github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
-github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
-github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
-github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww=
-github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
-github.com/testcontainers/testcontainers-go v0.11.0/go.mod h1:HztBCODzuA+YpMXGK8amjO8j50jz2gcT0BOzSKUiYIs=
-github.com/tevid/gohamcrest v1.1.1/go.mod h1:3UvtWlqm8j5JbwYZh80D/PVBt0mJ1eJiYgZMibh0H/k=
-github.com/tklauser/go-sysconf v0.3.10/go.mod h1:C8XykCvCb+Gn0oNCWPIlcb0RuglQTYaQ2hGm7jmxEFk=
-github.com/tklauser/numcpus v0.4.0/go.mod h1:1+UI3pD8NW14VMwdgJNJ1ESk2UnwhAnz5hMwiKKqXCQ=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
-github.com/tv42/httpunix v0.0.0-20150427012821-b75d8614f926/go.mod h1:9ESjWnEqriFuLhtthL60Sar/7RFoluCcXsuvEwTV5KM=
-github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
-github.com/uber/jaeger-lib v2.4.1+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
-github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM=
-github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc=
-github.com/ugorji/go v1.2.7/go.mod h1:nF9osbDWLy6bDVv/Rtoh6QgnvNDpmCalQV5urGCCS6M=
-github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
-github.com/ugorji/go/codec v1.2.7/go.mod h1:WGN1fab3R1fzQlVQTkfxVtIBhWDRqOviHU95kRgeqEY=
-github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
-github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
-github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
-github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
-github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
-github.com/urfave/negroni v1.0.0/go.mod h1:Meg73S6kFm/4PpbYdq35yYWoCZ9mS/YSx+lKnmiohz4=
-github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
-github.com/valyala/fasthttp v1.6.0/go.mod h1:FstJa9V+Pj9vQ7OJie2qMHdwemEDaDiSdBnvPM1Su9w=
-github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
-github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
-github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
-github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
-github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
-github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI=
-github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=
-github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=
-github.com/vmihailenco/go-tinylfu v0.2.2/go.mod h1:CutYi2Q9puTxfcolkliPq4npPuofg9N9t8JVrjzwa3Q=
-github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
-github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
-github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4=
-github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI=
-github.com/wubin1989/nacos-sdk-go/v2 v2.1.2-0.20221024120645-0288f53fdaa8/go.mod h1:Z30xHaEyVwGKmbXHGM5uuh7pQkV66p/wiC3mGHCyhWQ=
-github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
-github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
-github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
-github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs=
-github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
-github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1v2SRTV4cUmp4=
-github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU=
-github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
-github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
-github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
-github.com/xwb1989/sqlparser v0.0.0-20180606152119-120387863bf2/go.mod h1:hzfGeIUDq/j97IG+FhNqkowIyEcD88LrW6fyU3K3WqY=
-github.com/yalp/jsonpath v0.0.0-20180802001716-5cc68e5049a0/go.mod h1:/LWChgwKmvncFJFHJ7Gvn9wZArjbV5/FppcK2fKk/tI=
-github.com/yudai/gojsondiff v1.0.0/go.mod h1:AY32+k2cwILAkW1fbgxQ5mUmMiZFgLIV+FBNExI05xg=
-github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
-github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
-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=
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
-github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
-github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
-github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs=
-github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA=
-github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg=
-github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
-go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
-go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
-go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
-go.etcd.io/etcd/api/v3 v3.5.7/go.mod h1:9qew1gCdDDLu+VwmeG+iFpL+QlpHTo7iubavdVDgCAA=
-go.etcd.io/etcd/client/pkg/v3 v3.5.7/go.mod h1:o0Abi1MK86iad3YrWhgUsbGx1pmTS+hrORWc2CamuhY=
-go.etcd.io/etcd/client/v3 v3.5.7/go.mod h1:sOWmj9DZUMyAngS7QQwCyAXXAL6WhgTOPLNS/NabQgw=
-go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk=
-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=
-go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
-go.opentelemetry.io/otel v1.8.0/go.mod h1:2pkj+iMj0o03Y+cW6/m8Y4WkRdYN3AvCXCnzRMp9yvM=
-go.opentelemetry.io/otel v1.9.0/go.mod h1:np4EoPGzoPs3O67xUVNoPPcmSvsfOxNlNA4F4AC+0Eo=
-go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ=
-go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A=
-go.opentelemetry.io/otel/sdk v1.9.0/go.mod h1:AEZc8nt5bd2F7BC24J5R0mrjYnpEgYHyTcM/vrSple4=
-go.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE=
-go.opentelemetry.io/otel/trace v1.8.0/go.mod h1:0Bt3PXY8w+3pheS3hQUt+wow8b1ojPaTBoTCh2zIFI4=
-go.opentelemetry.io/otel/trace v1.9.0/go.mod h1:2737Q0MuG8q1uILYm2YYVkAyLtOofiTNGg6VODnOiPo=
-go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM=
-go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
-go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
-go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
-go.uber.org/automaxprocs v1.5.2/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
-go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
-go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
-go.uber.org/goleak v1.1.12/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
-go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
-go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
-go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
-go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
-go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
-go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
-go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
-go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo=
-go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
-go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
-go.uber.org/zap v1.22.0/go.mod h1:H4siCOZOrAolnUPJEkfaSjDqyP+BDS0DdDWzwcgt3+U=
-go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
-golang.org/x/arch v0.1.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
-golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
-golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
-golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
-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-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
-golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/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-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201112155050-0c6587e931a9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
-golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
-golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
-golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20210817164053-32db794688a5/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
-golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220511200225-c6db032c6c88/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20220826181053-bd7e27e6170d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.0.0-20221005025214-4161e89ecf1b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
-golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
-golang.org/x/crypto v0.5.0/go.mod h1:NK/OQwhpMQP3MwtdjgLlYHnH9ebylxKWv3e0fK+mkQU=
-golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
-golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE=
-golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20180807140117-3d87b88a115f/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
-golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/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-20220303212507-bbda1eaf7a17/go.mod h1:lgLbSvA5ygNOMpwM/9anMpWVlVJ7Z+cHWq/eFuinpGE=
-golang.org/x/exp v0.0.0-20221126150942-6ab00d035af9/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
-golang.org/x/exp v0.0.0-20230315142452-642cacee5cc0/go.mod h1:CxIveKay+FTh1D0yPZemJVgC/95VzuuOLq5Qi4xnoYc=
-golang.org/x/image v0.0.0-20180708004352-c73c2afc3b81/go.mod h1:ux5Hcp/YLpHSI86hEcLt0YII63i6oz57MZXIpbrjZUs=
-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/lint v0.0.0-20210508222113-6edffad5e616/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/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
-golang.org/x/mod v0.6.0-dev.0.20211013180041-c96bc1413d57/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
-golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
-golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
-golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
-golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
-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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
-golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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-20190327091125-710a502c58a2/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-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
-golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-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-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
-golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/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-20200520004742-59133d7f0dd7/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-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-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-20201110031124-69a78807bb2b/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-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
-golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc=
-golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
-golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
-golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20210610132358-84b48f89b13b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
-golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220412020605-290c469a71a5/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
-golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220617184016-355a448f1bc9/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220826154423-83b083e8dc8b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
-golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
-golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
-golang.org/x/net v0.3.0/go.mod h1:MBQ8lrhLObU/6UmLb4fmbmk5OcyYmqtbGd/9yIeKjEE=
-golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
-golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
-golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
-golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
-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/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
-golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
-golang.org/x/oauth2 v0.0.0-20220608161450-d0670ef3b1eb/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
-golang.org/x/oauth2 v0.0.0-20220622183110-fd043fe589d2/go.mod h1:jaDAt6Dkxork7LmZnYtzbRWj0W47D86a3TGe0YHBvmE=
-golang.org/x/oauth2 v0.0.0-20220822191816-0ebed06d0094/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg=
-golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
-golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE=
-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.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
-golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
-golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190422165155-953cdadca894/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-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191220220014-0732a990476f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/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-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/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-20200217220822-9197077df867/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-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/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-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20201214210602-f9fddec55a1e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210112080510-489259a85091/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-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210403161142-5e06dd20ab57/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
-golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/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-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/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-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220224120231-95c6836cb0e7/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220405210540-1e041c57c461/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-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220502124256-b6088ccd6cba/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220610221304-9f5ed59c137d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220624220833-87e55d714810/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220825204002-c680a09ffe64/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20221006211917-84dc82d7e875/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20221010170243-090e33056c14/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
-golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
-golang.org/x/term v0.3.0/go.mod h1:q750SLmJuPmVoN1blW3UFBPREJfb1KmY3vwxfr+nFDA=
-golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
-golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
-golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
-golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
-golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
-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.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
-golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
-golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.5.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
-golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
-golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
-golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-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/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
-golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180525024113-a5b4c53f6e8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
-golang.org/x/tools v0.0.0-20181221001348-537d06c36207/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-20190206041539-40960b6deb8e/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-20190327201419-c70d86f8b7cf/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
-golang.org/x/tools v0.0.0-20190424220101-1e8e1cfdf96b/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
-golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/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-20190614205625-5aca471b1d59/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-20190624222133-a101b041ded4/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-20190823170909-c4a336ef6a2f/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-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
-golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/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-20200103221440-774c71fcf114/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-20200619180055-7c47624df98f/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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
-golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.8-0.20211029000441-d6a9af8af023/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
-golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
-golang.org/x/tools v0.1.11/go.mod h1:SgwaegtQh8clINPpECJMqnxLv9I09HLqnW3RMqW0CA4=
-golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
-golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
-golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
-golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ=
-golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
-golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-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=
-golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-golang.org/x/xerrors v0.0.0-20220609144429-65e65417b02f/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
-gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo=
-gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0=
-gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw=
-gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc=
-google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0=
-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/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU=
-google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94=
-google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo=
-google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4=
-google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw=
-google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU=
-google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k=
-google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE=
-google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI=
-google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I=
-google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo=
-google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g=
-google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA=
-google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8=
-google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs=
-google.golang.org/api v0.75.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
-google.golang.org/api v0.77.0/go.mod h1:pU9QmyHLnzlpar1Mjt4IbapUCy8J+6HD6GeELN69ljA=
-google.golang.org/api v0.78.0/go.mod h1:1Sg78yoMLOhlQTeF+ARBoytAcH1NNyyl390YMy6rKmw=
-google.golang.org/api v0.80.0/go.mod h1:xY3nI94gbvBrE0J6NHXhxOmW97HG7Khjkku6AFB3Hyg=
-google.golang.org/api v0.84.0/go.mod h1:NTsGnUFJMYROtiquksZHBWtHfeMC7iYthki7Eq3pa8o=
-google.golang.org/api v0.85.0/go.mod h1:AqZf8Ep9uZ2pyTvgL+x0D3Zt0eoT9b5E8fmzfu6FO2g=
-google.golang.org/api v0.90.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
-google.golang.org/api v0.93.0/go.mod h1:+Sem1dnrKlrXMR/X0bPnMWyluQe4RsNoYfmNLhOIkzw=
-google.golang.org/api v0.95.0/go.mod h1:eADj+UBuxkh5zlrSntJghuNeg8HwQ1w5lTKkuqaETEI=
-google.golang.org/api v0.96.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
-google.golang.org/api v0.97.0/go.mod h1:w7wJQLTM+wvQpNf5JyEcBoxK0RH7EDrh/L4qfsuJ13s=
-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/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk=
-google.golang.org/genproto v0.0.0-20180518175338-11a468237815/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
-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-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s=
-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-20200117163144-32f20d992d24/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-20200423170343-7949de9c1215/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-20200513103714-09dca8ec2884/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-20200911024640-645f7a48b24f/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-20201110150050-8816d57aaa9a/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-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
-google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A=
-google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A=
-google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
-google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24=
-google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k=
-google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48=
-google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w=
-google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY=
-google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
-google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
-google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E=
-google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220414192740-2d67ff6cf2b4/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220421151946-72621c1f0bd3/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220429170224-98d788798c3e/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
-google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220505152158-f39f71e6c8f3/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220523171625-347a074981d8/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
-google.golang.org/genproto v0.0.0-20220608133413-ed9918b62aac/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220616135557-88e70c0c3a90/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220617124728-180714bec0ad/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220624142145-8cd45d7dbd1f/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220628213854-d9e0b6570c03/go.mod h1:KEWEmljWE5zPzLBa/oHl6DaEt9LmfH6WtH1OHIvleBA=
-google.golang.org/genproto v0.0.0-20220722212130-b98a9ff5e252/go.mod h1:GkXuJDJ6aQ7lnJcRF+SJVgFdQhypqgl3LB1C9vabdRE=
-google.golang.org/genproto v0.0.0-20220801145646-83ce21fca29f/go.mod h1:iHe1svFLAZg9VWz891+QbRMwUv9O/1Ww+/mngYeThbc=
-google.golang.org/genproto v0.0.0-20220815135757-37a418bb8959/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220817144833-d7fd3f11b9b1/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220822174746-9e6da59bd2fc/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220829144015-23454907ede3/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220829175752-36a9c930ecbf/go.mod h1:dbqgFATTzChvnt+ujMdZwITVAJHFtfyN1qUhDqEiIlk=
-google.golang.org/genproto v0.0.0-20220913154956-18f8339a66a5/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220914142337-ca0e39ece12f/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220915135415-7fd63a7952de/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220916172020-2692e8806bfa/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220919141832-68c03719ef51/go.mod h1:0Nb8Qy+Sk5eDzHnzlStwW3itdNaWoZA5XeSG+R3JHSo=
-google.golang.org/genproto v0.0.0-20220920201722-2b89144ce006/go.mod h1:ht8XFiar2npT/g4vkk7O0WYS1sHOHbdujxbEp7CJWbw=
-google.golang.org/genproto v0.0.0-20220926165614-551eb538f295/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
-google.golang.org/genproto v0.0.0-20220926220553-6981cbe3cfce/go.mod h1:woMGP53BroOrRY3xTxlbr8Y3eB/nzAvvFM83q7kG2OI=
-google.golang.org/genproto v0.0.0-20221010155953-15ba04fc1c0e/go.mod h1:3526vdqwhZAwq4wsRUaVG555sVgsNmIjRtO7t/JH29U=
-google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
-google.golang.org/grpc v1.12.0/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
-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.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
-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.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
-google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA=
-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.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
-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/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
-google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE=
-google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
-google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k=
-google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
-google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
-google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.46.2/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
-google.golang.org/grpc v1.49.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
-google.golang.org/grpc v1.50.0/go.mod h1:ZgQEeidpAuNRZ8iRrlBKXZQP1ghovWIVhdJRyCDK+GI=
-google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw=
-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=
-google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
-google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
-gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U=
-gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
-gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
-gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw=
-gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
-gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
-gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo=
-gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE=
-gopkg.in/go-playground/validator.v8 v8.18.2/go.mod h1:RX2a/7Ha8BgOhfk7j780h4/u/RRjR0eouCJSH80/M2Y=
-gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s=
-gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
-gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
-gopkg.in/mgo.v2 v2.0.0-20180705113604-9856a29383ce/go.mod h1:yeKp02qBN3iKW1OzL3MGk2IdtZzaj7SFntXj72NppTA=
-gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
-gopkg.in/natefinch/npipe.v2 v2.0.0-20160621034901-c1b8fa8bdcce/go.mod h1:5AcXVHNjg+BDxry382+8OKon8SEWiKktQR07RKPsv1c=
-gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
-gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI=
-gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
-gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI=
-gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
-gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
-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.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
-gorm.io/datatypes v1.0.7/go.mod h1:l9qkCuy0CdzDEop9HKUdcnC9gHC2sRlaFtHkTzsZRqg=
-gorm.io/datatypes v1.1.1-0.20230130040222-c43177d3cf8c/go.mod h1:SH2K9R+2RMjuX1CkCONrPwoe9JzVv2hkQvEu4bXGojE=
-gorm.io/driver/clickhouse v0.5.0/go.mod h1:cIKAlFw+IVK75g0bDcm0M9qRA4EAgsn23Si+zCXQ1Lc=
-gorm.io/driver/mysql v1.3.2/go.mod h1:ChK6AHbHgDCFZyJp0F+BmVGb06PSIoh9uVYKAlRbb2U=
-gorm.io/driver/mysql v1.4.0/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
-gorm.io/driver/mysql v1.4.3/go.mod h1:sSIebwZAVPiT+27jK9HIwvsqOGKx3YMPmrA3mBJR10c=
-gorm.io/driver/mysql v1.4.4/go.mod h1:BCg8cKI+R0j/rZRQxeKis/forqRwRSYOR8OM3Wo6hOM=
-gorm.io/driver/mysql v1.5.1-0.20230509030346-3715c134c25b/go.mod h1:RpAr+f2lUtJUm0e2FAbttXiUKgAqKSUtzI1ulJfz9xU=
-gorm.io/driver/postgres v1.3.4/go.mod h1:y0vEuInFKJtijuSGu9e5bs5hzzSzPK+LancpKpvbRBw=
-gorm.io/driver/postgres v1.4.1/go.mod h1:whNfh5WhhHs96honoLjBAMwJGYEuA3m1hvgUbNXhPCw=
-gorm.io/driver/postgres v1.4.5/go.mod h1:GKNQYSJ14qvWkvPwXljMGehpKrhlDNsqYRr5HnYGncg=
-gorm.io/driver/postgres v1.5.0/go.mod h1:FUZXzO+5Uqg5zzwzv4KK49R8lvGIyscBOqYrtI1Ce9A=
-gorm.io/driver/sqlite v1.1.6/go.mod h1:W8LmC/6UvVbHKah0+QOC7Ja66EaZXHwUTjgXY8YNWX8=
-gorm.io/driver/sqlite v1.3.1/go.mod h1:wJx0hJspfycZ6myN38x1O/AqLtNS6c5o9TndewFbELg=
-gorm.io/driver/sqlite v1.4.1/go.mod h1:AKZZCAoFfOWHF7Nd685Iq8Uywc0i9sWJlzpoE/INzsw=
-gorm.io/driver/sqlite v1.4.2/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
-gorm.io/driver/sqlite v1.4.3/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
-gorm.io/driver/sqlite v1.4.4/go.mod h1:0Aq3iPO+v9ZKbcdiz8gLWRw5VOPcBOPUQJFLq5e2ecI=
-gorm.io/driver/sqlserver v1.3.1/go.mod h1:w25Vrx2BG+CJNUu/xKbFhaKlGxT/nzRkhWCCoptX8tQ=
-gorm.io/driver/sqlserver v1.4.0/go.mod h1:P8BSbBwkdzXURYx3pWUSEAABRQU0vxbd6xk5+53pg7g=
-gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHDnJLig=
-gorm.io/driver/sqlserver v1.4.2/go.mod h1:XHwBuB4Tlh7DqO0x7Ema8dmyWsQW7wi38VQOAFkrbXY=
-gorm.io/gen v0.3.19/go.mod h1:aWgvoKdG9f8Des4TegSa0N5a+gwhGsFo0JJMaLwokvk=
-gorm.io/gorm v1.21.15/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
-gorm.io/gorm v1.22.2/go.mod h1:F+OptMscr0P2F2qU97WT1WimdH9GaQPoDW7AYd5i2Y0=
-gorm.io/gorm v1.23.1/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
-gorm.io/gorm v1.23.4/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
-gorm.io/gorm v1.23.6/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
-gorm.io/gorm v1.23.7/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
-gorm.io/gorm v1.23.8/go.mod h1:l2lP/RyAtc1ynaTjFksBde/O8v9oOGIApu2/xRitmZk=
-gorm.io/gorm v1.23.10/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
-gorm.io/gorm v1.24.0/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
-gorm.io/gorm v1.24.1-0.20221019064659-5dd2bb482755/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
-gorm.io/gorm v1.24.2/go.mod h1:DVrVomtaYTbqs7gB/x2uVvqnXzv0nqjB396B8cG4dBA=
-gorm.io/gorm v1.24.7-0.20230306060331-85eaf9eeda11/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
-gorm.io/gorm v1.25.0/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
-gorm.io/gorm v1.25.1-0.20230505075827-e61b98d69677/go.mod h1:L4uxeKpfBml98NYqVqwAdmV1a2nBtAec/cf3fpucW/k=
-gorm.io/hints v1.1.0/go.mod h1:lKQ0JjySsPBj3uslFzY3JhYDtqEwzm+G1hv8rWujB6Y=
-gorm.io/hints v1.1.1/go.mod h1:zdwzfFqvBWGbpuKiAhLFOSGSpeD3/VsRgkXR9Y7Z3cs=
-gorm.io/plugin/dbresolver v1.3.0/go.mod h1:Pr7p5+JFlgDaiM6sOrli5olekJD16YRunMyA2S7ZfKk=
-gorm.io/plugin/dbresolver v1.4.0/go.mod h1:w0DKqg02frWKwbBMTQkJ7aVxeKnap2cShQcroOQaq8k=
-gorm.io/plugin/prometheus v0.1.0/go.mod h1:5nrc/JrWCUNoDXCY4eOae/FK/J5WjQ0axXuFusCzdTc=
-gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw=
-gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk=
-gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8=
-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=
-k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo=
-k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ=
-k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8=
-k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
-k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU=
-k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc=
-k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU=
-k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM=
-k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q=
-k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y=
-k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k=
-k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0=
-k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk=
-k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI=
-k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM=
-k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM=
-k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
-k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
-k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
-k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
-k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
-k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
-k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM=
-k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk=
-k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA=
-rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
-rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=
-rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
-rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
-sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
-sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
-sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
-sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
diff --git a/toolkit/gormgen/tools/gentool/README.ZH_CN.md b/toolkit/gormgen/tools/gentool/README.ZH_CN.md
deleted file mode 100644
index 587d4915..00000000
--- a/toolkit/gormgen/tools/gentool/README.ZH_CN.md
+++ /dev/null
@@ -1,128 +0,0 @@
-# GenTool
-
-将Gen作为二进制的方式进行安装
-
-
-
-## 安装
-
-```shell
- go install github.com/wubin1989/gen/tools/gentool@latest
-```
-
-## 使用方式
-
-```shell
-
- gentool -h
-
- Usage of gentool:
- -db string
- input mysql or postgres or sqlite or sqlserver. consult[https://github.com/wubin1989/docs/connecting_to_the_database.html] (default "mysql")
- -dsn string
- consult[https://github.com/wubin1989/docs/connecting_to_the_database.html]
- -fieldNullable
- generate with pointer when field is nullable
- -fieldWithIndexTag
- generate field with gorm index tag
- -fieldWithTypeTag
- generate field with gorm column type tag
- -modelPkgName string
- generated model code's package name
- -outFile string
- query code file name, default: gen.go
- -outPath string
- specify a directory for output (default "./dao/query")
- -tables string
- enter the required data table or leave it blank
- -onlyModel
- only generate models (without query file)
- -withUnitTest
- generate unit test for query code
- -fieldSignable
- detect integer field's unsigned type, adjust generated data type
-
-```
-
-#### c
-default ""
-可以指定配置文件gen.yml的路径。
-用配置文件来代替命令行。
-命令行是最高优先级。
-
-#### db
-
-默认值:mysql
-
-可以输入: mysql、 postgres、 sqlite 、 sqlserve
-
-参考:https://github.com/wubin1989/docs/connecting_to_the_database.html
-
-#### dsn
-
-你可以使用GORM所有的连接。
-
- 参考:https://github.com/wubin1989/docs/connecting_to_the_database.html
-
-#### fieldNullable
-
-字段可为空时使用指针生成
-
-#### fieldWithIndexTag
-
-使用GROM索引标记生成字段
-
-#### fieldWithTypeTag
-
-使用gorm列类型标记生成字段
-
-#### modelPkgName
-
-默认值是数据表名称。
-
- 生成的model代码的包名称。
-
-#### outFile
-
-默认为:gen.go
-
-查询代码文件名。
-
-#### outPath
-
-默认为:/dao/query
-
-指定输出目录
-
-#### tables
-
-值为 : 输入所需的数据表或将其留空
-
-eg :
-
- --tables="orders" #orders 数据表
-
- --tables="orders,users" #orders 数据表和 users数据表
-
- --tables="" # 数据库中所有的数据表
-
-基于数据表生成对应的代码。
-
-#### withUnitTest
-
-值为 : False / True
-
-生成单元测试。
-
-#### fieldSignable
-
-Value : False / True
-
-基于数据表定义的数据类型,生成对应的数据类型
-
-
-### 使用示例
-
-```shell
-gentool -dsn "user:pwd@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local" -tables "orders,doctor"
-```
\ No newline at end of file
diff --git a/toolkit/gormgen/tools/gentool/README.md b/toolkit/gormgen/tools/gentool/README.md
deleted file mode 100644
index 1cf70601..00000000
--- a/toolkit/gormgen/tools/gentool/README.md
+++ /dev/null
@@ -1,123 +0,0 @@
-# GenTool
-
-Install GEN as a binary tool
-
-## install
-
-```shell
- go install github.com/wubin1989/gen/tools/gentool@latest
-```
-
-## usage
-
-```shell
-
- gentool -h
-
- Usage of gentool:
- -db string
- input mysql|postgres|sqlite|sqlserver|clickhouse. consult[https://github.com/wubin1989/docs/connecting_to_the_database.html] (default "mysql")
- -dsn string
- consult[https://github.com/wubin1989/docs/connecting_to_the_database.html]
- -fieldNullable
- generate with pointer when field is nullable
- -fieldWithIndexTag
- generate field with gorm index tag
- -fieldWithTypeTag
- generate field with gorm column type tag
- -modelPkgName string
- generated model code's package name
- -outFile string
- query code file name, default: gen.go
- -outPath string
- specify a directory for output (default "./dao/query")
- -tables string
- enter the required data table or leave it blank
- -onlyModel
- only generate models (without query file)
- -withUnitTest
- generate unit test for query code
- -fieldSignable
- detect integer field's unsigned type, adjust generated data type
-
-```
-#### c
-default ""
-Is path for gen.yml
-Replace the command line with a configuration file
-The command line is the highest priority
-
-
-#### db
-
-default:mysql
-
-input mysql or postgres or sqlite or sqlserver.
-
-consult : https://github.com/wubin1989/docs/connecting_to_the_database.html
-
-#### dsn
-
-You can use all gorm's dsn.
-
- consult : https://github.com/wubin1989/docs/connecting_to_the_database.html
-
-#### fieldNullable
-
-generate with pointer when field is nullable
-
-#### fieldWithIndexTag
-
-generate field with gorm index tag
-
-#### fieldWithTypeTag
-
-generate field with gorm column type tag
-
-#### modelPkgName
-
-defalut table name.
-
- generated model code's package name.
-
-#### outFile
-
- query code file name, default: gen.go
-
-#### outPath
-
-specify a directory for output (default "./dao/query")
-
-#### tables
-
-Value : enter the required data table or leave it blank.
-
-eg :
-
- --tables="orders" #orders table
-
- --tables="orders,users" #orders table and users table
-
- --tables="" # All data tables in the database.
-
-Generate some tables code.
-
-#### withUnitTest
-
-Value : False / True
-
-Generate unit test.
-
-#### fieldSignable
-
-Value : False / True
-
-detect integer field's unsigned type, adjust generated data type
-
-
-
-### example
-
-```shell
-gentool -dsn "user:pwd@tcp(127.0.0.1:3306)/database?charset=utf8mb4&parseTime=True&loc=Local" -tables "orders,doctor"
-```
\ No newline at end of file
diff --git a/toolkit/gormgen/tools/gentool/gen.yml b/toolkit/gormgen/tools/gentool/gen.yml
deleted file mode 100644
index 42c8dcfe..00000000
--- a/toolkit/gormgen/tools/gentool/gen.yml
+++ /dev/null
@@ -1,28 +0,0 @@
-version: "0.1"
-database:
- # consult[https://github.com/wubin1989/docs/connecting_to_the_database.html]"
- dsn : "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local"
- # input mysql or postgres or sqlite or sqlserver. consult[https://github.com/wubin1989/docs/connecting_to_the_database.html]
- db : "mysql"
- # enter the required data table or leave it blank.You can input :
- # tables :
- # - orders
- # - users
- # - goods
- tables :
- # specify a directory for output
- outPath : "./dao/query"
- # query code file name, default: gen.go
- outFile : ""
- # generate unit test for query code
- withUnitTest : false
- # generated model code's package name
- modelPkgName : ""
- # generate with pointer when field is nullable
- fieldNullable : false
- # generate field with gorm index tag
- fieldWithIndexTag : false
- # generate field with gorm column type tag
- fieldWithTypeTag : false
- # detect integer field's unsigned type, adjust generated data type
- fieldSignable : false
diff --git a/toolkit/gormgen/tools/gentool/gentool.go b/toolkit/gormgen/tools/gentool/gentool.go
deleted file mode 100644
index bca36760..00000000
--- a/toolkit/gormgen/tools/gentool/gentool.go
+++ /dev/null
@@ -1,210 +0,0 @@
-package main
-
-import (
- "flag"
- "fmt"
- "log"
- "os"
- "strings"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/gormgen"
- "github.com/wubin1989/clickhouse"
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/mysql"
- "github.com/wubin1989/postgres"
- "github.com/wubin1989/sqlite"
- "github.com/wubin1989/sqlserver"
- "gopkg.in/yaml.v3"
-)
-
-// DBType database type
-type DBType string
-
-const (
- // dbMySQL Gorm Drivers mysql || postgres || sqlite || sqlserver
- dbMySQL DBType = "mysql"
- dbPostgres DBType = "postgres"
- dbSQLite DBType = "sqlite"
- dbSQLServer DBType = "sqlserver"
- dbClickHouse DBType = "clickhouse"
-)
-
-// CmdParams is command line parameters
-type CmdParams struct {
- DSN string `yaml:"dsn"` // consult[https://github.com/wubin1989/docs/connecting_to_the_database.html]"
- DB string `yaml:"db"` // input mysql or postgres or sqlite or sqlserver. consult[https://github.com/wubin1989/docs/connecting_to_the_database.html]
- Tables []string `yaml:"tables"` // enter the required data table or leave it blank
- OnlyModel bool `yaml:"onlyModel"` // only generate model
- OutPath string `yaml:"outPath"` // specify a directory for output
- OutFile string `yaml:"outFile"` // query code file name, default: gen.go
- WithUnitTest bool `yaml:"withUnitTest"` // generate unit test for query code
- ModelPkgName string `yaml:"modelPkgName"` // generated model code's package name
- FieldNullable bool `yaml:"fieldNullable"` // generate with pointer when field is nullable
- FieldWithIndexTag bool `yaml:"fieldWithIndexTag"` // generate field with gorm index tag
- FieldWithTypeTag bool `yaml:"fieldWithTypeTag"` // generate field with gorm column type tag
- FieldSignable bool `yaml:"fieldSignable"` // detect integer field's unsigned type, adjust generated data type
-}
-
-// YamlConfig is yaml config struct
-type YamlConfig struct {
- Version string `yaml:"version"` //
- Database *CmdParams `yaml:"database"` //
-}
-
-// connectDB choose db type for connection to database
-func connectDB(t DBType, dsn string) (*gorm.DB, error) {
- if dsn == "" {
- return nil, fmt.Errorf("dsn cannot be empty")
- }
-
- switch t {
- case dbMySQL:
- return gorm.Open(mysql.Open(dsn))
- case dbPostgres:
- return gorm.Open(postgres.Open(dsn))
- case dbSQLite:
- return gorm.Open(sqlite.Open(dsn))
- case dbSQLServer:
- return gorm.Open(sqlserver.Open(dsn))
- case dbClickHouse:
- return gorm.Open(clickhouse.Open(dsn))
- default:
- return nil, fmt.Errorf("unknow db %q (support mysql || postgres || sqlite || sqlserver for now)", t)
- }
-}
-
-// genModels is gorm/gen generated models
-func genModels(g *gormgen.Generator, db *gorm.DB, tables []string) (models []interface{}, err error) {
- var tablesList []string
- if len(tables) == 0 {
- // Execute tasks for all tables in the database
- tablesList, err = db.Migrator().GetTables()
- if err != nil {
- return nil, fmt.Errorf("GORM migrator get all tables fail: %w", err)
- }
- } else {
- tablesList = tables
- }
-
- // Execute some data table tasks
- models = make([]interface{}, len(tablesList))
- for i, tableName := range tablesList {
- models[i] = g.GenerateModel(tableName)
- }
- return models, nil
-}
-
-// loadConfigFile load config file from path
-func loadConfigFile(path string) (*CmdParams, error) {
- file, err := os.Open(path)
- if err != nil {
- return nil, err
- }
- defer file.Close() // nolint
- var yamlConfig YamlConfig
- if cmdErr := yaml.NewDecoder(file).Decode(&yamlConfig); cmdErr != nil {
- return nil, cmdErr
- }
- return yamlConfig.Database, nil
-}
-
-// argParse is parser for cmd
-func argParse() *CmdParams {
- // choose is file or flag
- genPath := flag.String("c", "", "is path for gen.yml")
- dsn := flag.String("dsn", "", "consult[https://github.com/wubin1989/docs/connecting_to_the_database.html]")
- db := flag.String("db", "mysql", "input mysql|postgres|sqlite|sqlserver|clickhouse. consult[https://github.com/wubin1989/docs/connecting_to_the_database.html]")
- tableList := flag.String("tables", "", "enter the required data table or leave it blank")
- onlyModel := flag.Bool("onlyModel", false, "only generate models (without query file)")
- outPath := flag.String("outPath", "./dao/query", "specify a directory for output")
- outFile := flag.String("outFile", "", "query code file name, default: gen.go")
- withUnitTest := flag.Bool("withUnitTest", false, "generate unit test for query code")
- modelPkgName := flag.String("modelPkgName", "", "generated model code's package name")
- fieldNullable := flag.Bool("fieldNullable", false, "generate with pointer when field is nullable")
- fieldWithIndexTag := flag.Bool("fieldWithIndexTag", false, "generate field with gorm index tag")
- fieldWithTypeTag := flag.Bool("fieldWithTypeTag", false, "generate field with gorm column type tag")
- fieldSignable := flag.Bool("fieldSignable", false, "detect integer field's unsigned type, adjust generated data type")
- flag.Parse()
- var cmdParse CmdParams
- if *genPath != "" {
- if configFileParams, err := loadConfigFile(*genPath); err == nil && configFileParams != nil {
- cmdParse = *configFileParams
- } else if err != nil {
- log.Fatalf("loadConfigFile fail %s", err.Error())
- }
- }
- // cmd first
- if *dsn != "" {
- cmdParse.DSN = *dsn
- }
- if *db != "" {
- cmdParse.DB = *db
- }
- if *tableList != "" {
- cmdParse.Tables = strings.Split(*tableList, ",")
- }
- if *onlyModel {
- cmdParse.OnlyModel = true
- }
- if *outPath != "" {
- cmdParse.OutPath = *outPath
- }
- if *outFile != "" {
- cmdParse.OutFile = *outFile
- }
- if *withUnitTest {
- cmdParse.WithUnitTest = *withUnitTest
- }
- if *modelPkgName != "" {
- cmdParse.ModelPkgName = *modelPkgName
- }
- if *fieldNullable {
- cmdParse.FieldNullable = *fieldNullable
- }
- if *fieldWithIndexTag {
- cmdParse.FieldWithIndexTag = *fieldWithIndexTag
- }
- if *fieldWithTypeTag {
- cmdParse.FieldWithTypeTag = *fieldWithTypeTag
- }
- if *fieldSignable {
- cmdParse.FieldSignable = *fieldSignable
- }
- return &cmdParse
-}
-
-func main() {
- // cmdParse
- config := argParse()
- if config == nil {
- log.Fatalln("parse config fail")
- }
- db, err := connectDB(DBType(config.DB), config.DSN)
- if err != nil {
- log.Fatalln("connect db server fail:", err)
- }
-
- g := gormgen.NewGenerator(gormgen.Config{
- OutPath: config.OutPath,
- OutFile: config.OutFile,
- ModelPkgPath: config.ModelPkgName,
- WithUnitTest: config.WithUnitTest,
- FieldNullable: config.FieldNullable,
- FieldWithIndexTag: config.FieldWithIndexTag,
- FieldWithTypeTag: config.FieldWithTypeTag,
- FieldSignable: config.FieldSignable,
- })
-
- g.UseDB(db)
-
- models, err := genModels(g, db, config.Tables)
- if err != nil {
- log.Fatalln("get tables info fail:", err)
- }
-
- if !config.OnlyModel {
- g.ApplyBasic(models...)
- }
-
- g.Execute()
-}
diff --git a/toolkit/hashutils/hashutils.go b/toolkit/hashutils/hashutils.go
deleted file mode 100644
index a023585a..00000000
--- a/toolkit/hashutils/hashutils.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package hashutils
-
-import (
- "crypto/sha1"
- "encoding/base64"
- "encoding/hex"
- "fmt"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-)
-
-// Sha1 return sha1 string
-func Sha1(input string) string {
- if input == "" {
- return "adc83b19e793491b1c6ea0fd8b46cd9f32e592fc"
- }
- return fmt.Sprintf("%x", sha1.Sum([]byte(input)))
-}
-
-// Secret2Password return password string
-func Secret2Password(username, secret string) string {
- if stringutils.IsEmpty(secret) {
- return Sha1(Sha1(secret) + Sha1(username) + Sha1(secret))
- }
- return Sha1(Sha1(secret[:8]) + Sha1(username) + Sha1(secret[8:]))
-}
-
-// Base64 returns base64 string
-func Base64(input string) string {
- return base64.StdEncoding.EncodeToString([]byte(input))
-}
-
-// encodeHex is borrowed from https://github.com/google/uuid/blob/44b5fee7c49cf3bcdf723f106b36d56ef13ccc88/uuid.go#L200
-func encodeHex(dst []byte, uuid [16]byte) {
- hex.Encode(dst, uuid[:4])
- dst[8] = '-'
- hex.Encode(dst[9:13], uuid[4:6])
- dst[13] = '-'
- hex.Encode(dst[14:18], uuid[6:8])
- dst[18] = '-'
- hex.Encode(dst[19:23], uuid[8:10])
- dst[23] = '-'
- hex.Encode(dst[24:], uuid[10:])
-}
-
-// UUIDByString generates uuid hex string from an arbitrary string
-func UUIDByString(input string) string {
- sum := sha1.Sum([]byte(input))
- var uuid [16]byte
- copy(uuid[:], sum[:])
- uuid[6] = (uuid[6] & 0x0f) | uint8((5&0xf)<<4)
- uuid[8] = (uuid[8] & 0x3f) | 0x80
- var buf [36]byte
- encodeHex(buf[:], uuid)
- return string(buf[:])
-}
diff --git a/toolkit/hashutils/hashutils_test.go b/toolkit/hashutils/hashutils_test.go
deleted file mode 100644
index 103bf87c..00000000
--- a/toolkit/hashutils/hashutils_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package hashutils
-
-import "testing"
-
-func TestBase64(t *testing.T) {
- type args struct {
- input string
- }
- tests := []struct {
- name string
- args args
- want string
- }{
- {
- name: "",
- args: args{
- input: "my secret key",
- },
- want: "bXkgc2VjcmV0IGtleQ==",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := Base64(tt.args.input); got != tt.want {
- t.Errorf("Base64() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestSecret2Password(t *testing.T) {
- type args struct {
- username string
- secret string
- }
- tests := []struct {
- name string
- args args
- want string
- }{
- {
- name: "",
- args: args{
- username: "wubin",
- secret: "my secret",
- },
- want: "cff23c519b29a0e0c0304ff1a3d795f171b9c919",
- },
- {
- name: "",
- args: args{
- username: "wubin",
- secret: "",
- },
- want: "f85610573ac9cda1a0e27e27406e9125e0e2403d",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := Secret2Password(tt.args.username, tt.args.secret); got != tt.want {
- t.Errorf("Secret2Password() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestUUIDByString(t *testing.T) {
- type args struct {
- input string
- }
- tests := []struct {
- name string
- args args
- want string
- }{
- {
- name: "",
- args: args{
- input: "http://www.qingdao.gov.cn/zwgk/xxgk/fgw/gkml/gwfg/bmgw/",
- },
- want: "f64e7a2c-7c3c-574b-8353-e10efee0efc5",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := UUIDByString(tt.args.input); got != tt.want {
- t.Errorf("UUIDByString() = %v, want %v", got, tt.want)
- }
- })
- }
-}
diff --git a/toolkit/imgutils/helper.go b/toolkit/imgutils/helper.go
deleted file mode 100644
index 1d94d415..00000000
--- a/toolkit/imgutils/helper.go
+++ /dev/null
@@ -1,285 +0,0 @@
-package imgutils
-
-import (
- "container/heap"
- "image"
- "image/color"
- "image/color/palette"
- "image/draw"
- "sort"
-)
-
-const (
- numDimensions = 3
-)
-
-func min(x, y int) int {
- if x < y {
- return x
- }
- return y
-}
-
-func max(x, y int) int {
- if x > y {
- return x
- }
- return y
-}
-
-type point [numDimensions]int
-
-type block struct {
- minCorner, maxCorner point
- points []point
- // The index is needed by update and is maintained by the heap.Interface methods.
- index int // The index of the item in the heap.
-}
-
-func newBlock(p []point) *block {
- return &block{
- minCorner: point{0x00, 0x00, 0x00},
- maxCorner: point{0xFF, 0xFF, 0xFF},
- points: p,
- }
-}
-
-func (b *block) longestSideIndex() int {
- m := b.maxCorner[0] - b.minCorner[0]
- maxIndex := 0
- for i := 1; i < numDimensions; i++ {
- diff := b.maxCorner[i] - b.minCorner[i]
- if diff > m {
- m = diff
- maxIndex = i
- }
- }
- return maxIndex
-}
-
-func (b *block) longestSideLength() int {
- i := b.longestSideIndex()
- return b.maxCorner[i] - b.minCorner[i]
-}
-
-func (b *block) shrink() {
- for j := 0; j < numDimensions; j++ {
- b.minCorner[j] = b.points[0][j]
- b.maxCorner[j] = b.points[0][j]
- }
- for i := 1; i < len(b.points); i++ {
- for j := 0; j < numDimensions; j++ {
- b.minCorner[j] = min(b.minCorner[j], b.points[i][j])
- b.maxCorner[j] = max(b.maxCorner[j], b.points[i][j])
- }
- }
-}
-
-type pointSorter struct {
- points []point
- by func(p1, p2 *point) bool
-}
-
-func (p *pointSorter) Len() int {
- return len(p.points)
-}
-
-func (p *pointSorter) Swap(i, j int) {
- p.points[i], p.points[j] = p.points[j], p.points[i]
-}
-
-func (p *pointSorter) Less(i, j int) bool {
- return p.by(&p.points[i], &p.points[j])
-}
-
-// A priorityQueue implements heap.Interface and holds blocks.
-type priorityQueue []*block
-
-func (pq priorityQueue) Len() int { return len(pq) }
-
-func (pq priorityQueue) Less(i, j int) bool {
- return pq[i].longestSideLength() > pq[j].longestSideLength()
-}
-
-func (pq priorityQueue) Swap(i, j int) {
- pq[i], pq[j] = pq[j], pq[i]
- pq[i].index = i
- pq[j].index = j
-}
-
-func (pq *priorityQueue) Push(x interface{}) {
- n := len(*pq)
- item := x.(*block)
- item.index = n
- *pq = append(*pq, item)
-}
-
-func (pq *priorityQueue) Pop() interface{} {
- old := *pq
- n := len(old)
- item := old[n-1]
- item.index = -1 // for safety
- *pq = old[:n-1]
- return item
-}
-
-func (pq *priorityQueue) top() interface{} {
- n := len(*pq)
- if n == 0 {
- return nil
- }
- return (*pq)[n-1]
-}
-
-// clip clips r against each image's bounds (after translating into
-// the destination image's co-ordinate space) and shifts the point
-// sp by the same amount as the change in r.Min.
-func clip(dst draw.Image, r *image.Rectangle, src image.Image, sp *image.Point) {
- orig := r.Min
- *r = r.Intersect(dst.Bounds())
- *r = r.Intersect(src.Bounds().Add(orig.Sub(*sp)))
- dx := r.Min.X - orig.X
- dy := r.Min.Y - orig.Y
- if dx == 0 && dy == 0 {
- return
- }
- (*sp).X += dx
- (*sp).Y += dy
-}
-
-// MedianCutQuantizer constructs a palette with a maximum of
-// NumColor colors by iteratively splitting clusters of color
-// points mapped on a three-dimensional (RGB) Euclidian space.
-// Once the number of clusters is within the specified bounds,
-// the resulting color is computed by averaging those within
-// each grouping.
-type MedianCutQuantizer struct {
- NumColor int
-}
-
-func (q *MedianCutQuantizer) medianCut(points []point) color.Palette {
- if q.NumColor == 0 {
- return color.Palette{}
- }
-
- initialBlock := newBlock(points)
- initialBlock.shrink()
- pq := &priorityQueue{}
- heap.Init(pq)
- heap.Push(pq, initialBlock)
-
- for pq.Len() < q.NumColor && len(pq.top().(*block).points) > 1 {
- longestBlock := heap.Pop(pq).(*block)
- points := longestBlock.points
- li := longestBlock.longestSideIndex()
- // TODO: Instead of sorting the entire slice, finding the median using an
- // algorithm like introselect would give much better performance.
- sort.Sort(&pointSorter{
- points: points,
- by: func(p1, p2 *point) bool { return p1[li] < p2[li] },
- })
- median := len(points) / 2
- block1 := newBlock(points[:median])
- block2 := newBlock(points[median:])
- block1.shrink()
- block2.shrink()
- heap.Push(pq, block1)
- heap.Push(pq, block2)
- }
-
- palette := make(color.Palette, q.NumColor)
- var n int
- for n = 0; pq.Len() > 0; n++ {
- block := heap.Pop(pq).(*block)
- var sum [numDimensions]int
- for i := 0; i < len(block.points); i++ {
- for j := 0; j < numDimensions; j++ {
- sum[j] += block.points[i][j]
- }
- }
- palette[n] = color.RGBA64{
- R: uint16(sum[0] / len(block.points)),
- G: uint16(sum[1] / len(block.points)),
- B: uint16(sum[2] / len(block.points)),
- A: 0xFFFF,
- }
- }
- // Trim to only the colors present in the image, which
- // could be less than NumColor.
- return palette[:n]
-}
-
-func (q *MedianCutQuantizer) Quantize(dst *image.Paletted, r image.Rectangle, src image.Image, sp image.Point) {
- clip(dst, &r, src, &sp)
- if r.Empty() {
- return
- }
-
- points := make([]point, r.Dx()*r.Dy())
- colorSet := make(map[uint32]color.Color, q.NumColor)
- i := 0
- for y := r.Min.Y; y < r.Max.Y; y++ {
- for x := r.Min.X; x < r.Max.X; x++ {
- c := src.At(x, y)
- r, g, b, _ := c.RGBA()
- colorSet[(r>>8)<<16|(g>>8)<<8|b>>8] = c
- points[i][0] = int(r)
- points[i][1] = int(g)
- points[i][2] = int(b)
- i++
- }
- }
- if len(colorSet) <= q.NumColor {
- // No need to quantize since the total number of colors
- // fits within the palette.
- dst.Palette = make(color.Palette, len(colorSet))
- i := 0
- for _, c := range colorSet {
- dst.Palette[i] = c
- i++
- }
- } else {
- dst.Palette = q.medianCut(points)
- }
-
- for y := 0; y < r.Dy(); y++ {
- for x := 0; x < r.Dx(); x++ {
- // TODO: this should be done more efficiently.
- dst.Set(sp.X+x, sp.Y+y, src.At(r.Min.X+x, r.Min.Y+y))
- }
- }
-}
-
-func inPalette(p color.Palette, c color.Color) int {
- ret := -1
- for i, v := range p {
- if v == c {
- return i
- }
- }
- return ret
-}
-
-func getSubPalette(m image.Image) color.Palette {
- p := color.Palette{color.RGBA{0x00, 0x00, 0x00, 0x00}}
- p9 := color.Palette(palette.Plan9)
- b := m.Bounds()
- black := false
- for y := b.Min.Y; y < b.Max.Y; y++ {
- for x := b.Min.X; x < b.Max.X; x++ {
- c := m.At(x, y)
- cc := p9.Convert(c)
- if cc == p9[0] {
- black = true
- }
- if inPalette(p, cc) == -1 {
- p = append(p, cc)
- }
- }
- }
- if len(p) < 256 && black == true {
- p[0] = color.RGBA{0x00, 0x00, 0x00, 0x00} // transparent
- p = append(p, p9[0])
- }
- return p
-}
diff --git a/toolkit/imgutils/imgutils.go b/toolkit/imgutils/imgutils.go
deleted file mode 100644
index 99f6b3b2..00000000
--- a/toolkit/imgutils/imgutils.go
+++ /dev/null
@@ -1,86 +0,0 @@
-package imgutils
-
-import (
- "bytes"
- "fmt"
- "image"
- "image/draw"
- "image/gif"
- "image/jpeg"
- "image/png"
- "io"
- "os"
- "path/filepath"
-
- "github.com/nfnt/resize"
- "github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-)
-
-func ResizeKeepAspectRatio(input io.Reader, multiplier float64, output string) (string, error) {
- by, err := io.ReadAll(input)
- if err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- imgConf, imgType, err := image.DecodeConfig(bytes.NewReader(by))
- if err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- switch imgType {
- case "jpeg":
- img, err := jpeg.Decode(bytes.NewReader(by))
- if err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- m := resize.Resize(uint(float64(imgConf.Width)*multiplier), 0, img, resize.Lanczos3)
- if stringutils.IsEmpty(filepath.Ext(output)) {
- output += fmt.Sprintf(".%s", imgType)
- }
- out, err := os.Create(output)
- if err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- defer out.Close()
- jpeg.Encode(out, m, nil)
- case "png":
- img, err := png.Decode(bytes.NewReader(by))
- if err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- m := resize.Resize(uint(float64(imgConf.Width)*multiplier), 0, img, resize.Lanczos3)
- if stringutils.IsEmpty(filepath.Ext(output)) {
- output += fmt.Sprintf(".%s", imgType)
- }
- out, err := os.Create(output)
- if err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- defer out.Close()
- png.Encode(out, m)
- case "gif":
- img, err := gif.DecodeAll(bytes.NewReader(by))
- if err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- if multiplier != 1 {
- for i, item := range img.Image {
- resized := resize.Resize(uint(float64(imgConf.Width)*multiplier), 0, item, resize.Lanczos3)
- rgba64 := resized.(*image.RGBA64)
- palettedImage := image.NewPaletted(rgba64.Bounds(), getSubPalette(rgba64))
- draw.Draw(palettedImage, palettedImage.Rect, rgba64, rgba64.Bounds().Min, draw.Src)
- img.Image[i] = palettedImage
- }
- }
- if stringutils.IsEmpty(filepath.Ext(output)) {
- output += fmt.Sprintf(".%s", imgType)
- }
- out, err := os.Create(output)
- if err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- defer out.Close()
- gif.EncodeAll(out, img)
- }
- return output, nil
-}
diff --git a/toolkit/imgutils/imgutils_test.go b/toolkit/imgutils/imgutils_test.go
deleted file mode 100644
index 31d3b8d5..00000000
--- a/toolkit/imgutils/imgutils_test.go
+++ /dev/null
@@ -1,71 +0,0 @@
-package imgutils_test
-
-import (
- "os"
- "path/filepath"
- "testing"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/imgutils"
-)
-
-const testDir = "testdata"
-
-func TestResizeKeepAspectRatioPng(t *testing.T) {
- file, err := os.Open(filepath.Join(testDir, "test.png"))
- if err != nil {
- panic(err)
- }
- defer file.Close()
- _, err = imgutils.ResizeKeepAspectRatio(file, 0.5, filepath.Join(testDir, "test_result"))
- if err != nil {
- panic(err)
- }
-}
-
-func TestResizeKeepAspectRatioJpeg(t *testing.T) {
- file, err := os.Open(filepath.Join(testDir, "test.jpg"))
- if err != nil {
- panic(err)
- }
- defer file.Close()
- _, err = imgutils.ResizeKeepAspectRatio(file, 0.5, filepath.Join(testDir, "test_result.jpg"))
- if err != nil {
- panic(err)
- }
-}
-
-func TestResizeKeepAspectRatioGif1(t *testing.T) {
- file, err := os.Open(filepath.Join(testDir, "rgb.gif"))
- if err != nil {
- panic(err)
- }
- defer file.Close()
- _, err = imgutils.ResizeKeepAspectRatio(file, 0.5, filepath.Join(testDir, "rgb_result"))
- if err != nil {
- panic(err)
- }
-}
-
-func TestResizeKeepAspectRatioGif2(t *testing.T) {
- file, err := os.Open(filepath.Join(testDir, "test.gif"))
- if err != nil {
- panic(err)
- }
- defer file.Close()
- _, err = imgutils.ResizeKeepAspectRatio(file, 1, filepath.Join(testDir, "test_result"))
- if err != nil {
- panic(err)
- }
-}
-
-func TestResizeKeepAspectRatioGif3(t *testing.T) {
- file, err := os.Open(filepath.Join(testDir, "test.gif"))
- if err != nil {
- panic(err)
- }
- defer file.Close()
- _, err = imgutils.ResizeKeepAspectRatio(file, 0.5, filepath.Join(testDir, "test_result"))
- if err != nil {
- panic(err)
- }
-}
diff --git a/toolkit/imgutils/testdata/rgb.gif b/toolkit/imgutils/testdata/rgb.gif
deleted file mode 100644
index c5f1d3f3..00000000
Binary files a/toolkit/imgutils/testdata/rgb.gif and /dev/null differ
diff --git a/toolkit/imgutils/testdata/rgb_result.gif b/toolkit/imgutils/testdata/rgb_result.gif
deleted file mode 100644
index 7596f3ea..00000000
Binary files a/toolkit/imgutils/testdata/rgb_result.gif and /dev/null differ
diff --git a/toolkit/imgutils/testdata/test.gif b/toolkit/imgutils/testdata/test.gif
deleted file mode 100644
index 6b741994..00000000
Binary files a/toolkit/imgutils/testdata/test.gif and /dev/null differ
diff --git a/toolkit/imgutils/testdata/test.jpg b/toolkit/imgutils/testdata/test.jpg
deleted file mode 100644
index 0411e81f..00000000
Binary files a/toolkit/imgutils/testdata/test.jpg and /dev/null differ
diff --git a/toolkit/imgutils/testdata/test.png b/toolkit/imgutils/testdata/test.png
deleted file mode 100644
index 22eeb378..00000000
Binary files a/toolkit/imgutils/testdata/test.png and /dev/null differ
diff --git a/toolkit/imgutils/testdata/test_result.gif b/toolkit/imgutils/testdata/test_result.gif
deleted file mode 100644
index b5269b1e..00000000
Binary files a/toolkit/imgutils/testdata/test_result.gif and /dev/null differ
diff --git a/toolkit/imgutils/testdata/test_result.jpeg b/toolkit/imgutils/testdata/test_result.jpeg
deleted file mode 100644
index 8abf4899..00000000
Binary files a/toolkit/imgutils/testdata/test_result.jpeg and /dev/null differ
diff --git a/toolkit/imgutils/testdata/test_result.jpg b/toolkit/imgutils/testdata/test_result.jpg
deleted file mode 100644
index 8abf4899..00000000
Binary files a/toolkit/imgutils/testdata/test_result.jpg and /dev/null differ
diff --git a/toolkit/imgutils/testdata/test_result.png b/toolkit/imgutils/testdata/test_result.png
deleted file mode 100644
index 9d7b5793..00000000
Binary files a/toolkit/imgutils/testdata/test_result.png and /dev/null differ
diff --git a/toolkit/ip/ip.go b/toolkit/ip/ip.go
deleted file mode 100644
index 0c61e943..00000000
--- a/toolkit/ip/ip.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package ip
-
-import (
- "net"
-)
-
-// GetOutboundIP return local public net.IP
-func GetOutboundIP() net.IP {
- conn, _ := net.Dial("udp", "8.8.8.8:80")
- defer conn.Close()
- localAddr := conn.LocalAddr().(*net.UDPAddr)
- return localAddr.IP
-}
diff --git a/toolkit/ip/ip_test.go b/toolkit/ip/ip_test.go
deleted file mode 100644
index fd3a1e09..00000000
--- a/toolkit/ip/ip_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package ip
-
-import (
- "net"
- "testing"
-)
-
-func TestGetOutboundIP(t *testing.T) {
- tests := []struct {
- name string
- want net.IP
- }{
- {
- name: "",
- want: nil,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := GetOutboundIP(); len(got) == 0 {
- t.Errorf("GetOutboundIP() got nothing")
- }
- })
- }
-}
diff --git a/toolkit/loadbalance/subset.go b/toolkit/loadbalance/subset.go
deleted file mode 100644
index 726694f8..00000000
--- a/toolkit/loadbalance/subset.go
+++ /dev/null
@@ -1,18 +0,0 @@
-package loadbalance
-
-import (
- "math"
- "math/rand"
-)
-
-func Subset(backends []string, clientId int, subsetSize int) []string {
- subsetCount := int(math.Ceil(float64(len(backends)) / float64(subsetSize)))
- round := int64(clientId / subsetCount)
- rand.Seed(round)
- rand.Shuffle(len(backends), func(i, j int) {
- backends[i], backends[j] = backends[j], backends[i]
- })
- subsetId := clientId % subsetCount
- start := subsetId * subsetSize
- return backends[start : start+subsetSize]
-}
diff --git a/toolkit/loadbalance/subset_test.go b/toolkit/loadbalance/subset_test.go
deleted file mode 100644
index 42c073ef..00000000
--- a/toolkit/loadbalance/subset_test.go
+++ /dev/null
@@ -1,115 +0,0 @@
-package loadbalance
-
-import (
- "github.com/goccy/go-reflect"
- "testing"
-)
-
-func TestSubset(t *testing.T) {
- type args struct {
- backends []string
- clientId int
- subsetSize int
- }
- tests := []struct {
- name string
- args args
- want []string
- }{
- {
- name: "",
- args: args{
- backends: []string{
- "192.168.0.0",
- "192.168.0.1",
- "192.168.0.2",
- "192.168.0.3",
- "192.168.0.4",
- "192.168.0.5",
- "192.168.0.6",
- "192.168.0.7",
- "192.168.0.8",
- "192.168.0.9",
- "192.168.0.10",
- "192.168.0.11",
- },
- clientId: 8,
- subsetSize: 3,
- },
- want: []string{"192.168.0.5", "192.168.0.7", "192.168.0.3"},
- },
- {
- name: "",
- args: args{
- backends: []string{
- "192.168.0.0",
- "192.168.0.1",
- "192.168.0.2",
- "192.168.0.3",
- "192.168.0.4",
- "192.168.0.5",
- "192.168.0.6",
- "192.168.0.7",
- "192.168.0.8",
- "192.168.0.9",
- "192.168.0.10",
- "192.168.0.11",
- },
- clientId: 9,
- subsetSize: 3,
- },
- want: []string{"192.168.0.9", "192.168.0.8", "192.168.0.10"},
- },
- {
- name: "",
- args: args{
- backends: []string{
- "192.168.0.0",
- "192.168.0.1",
- "192.168.0.2",
- "192.168.0.3",
- "192.168.0.4",
- "192.168.0.5",
- "192.168.0.6",
- "192.168.0.7",
- "192.168.0.8",
- "192.168.0.9",
- "192.168.0.10",
- "192.168.0.11",
- },
- clientId: 10,
- subsetSize: 3,
- },
- want: []string{"192.168.0.6", "192.168.0.4", "192.168.0.1"},
- },
- {
- name: "",
- args: args{
- backends: []string{
- "192.168.0.0",
- "192.168.0.1",
- "192.168.0.2",
- "192.168.0.3",
- "192.168.0.4",
- "192.168.0.5",
- "192.168.0.6",
- "192.168.0.7",
- "192.168.0.8",
- "192.168.0.9",
- "192.168.0.10",
- "192.168.0.11",
- },
- clientId: 11,
- subsetSize: 3,
- },
- want: []string{"192.168.0.0", "192.168.0.11", "192.168.0.2"},
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := Subset(tt.args.backends, tt.args.clientId, tt.args.subsetSize); !reflect.DeepEqual(got, tt.want) {
- t.Errorf("Subset() = %v, want %v", got, tt.want)
- }
- })
- }
-}
diff --git a/toolkit/maputils/maputils.go b/toolkit/maputils/maputils.go
deleted file mode 100644
index 1dfbb353..00000000
--- a/toolkit/maputils/maputils.go
+++ /dev/null
@@ -1,193 +0,0 @@
-package maputils
-
-import (
- "github.com/goccy/go-reflect"
- "github.com/samber/lo"
- "strconv"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
-)
-
-type ChangeType int
-
-const (
- ADDED ChangeType = iota
- MODIFIED
- DELETED
-)
-
-type Change struct {
- OldValue interface{}
- NewValue interface{}
- ChangeType ChangeType
-}
-
-func Diff(new, old map[string]interface{}) map[string]Change {
- mp := map[string]bool{}
- for k, _ := range old {
- mp[k] = true
- }
- changes := make(map[string]Change)
-
- if new != nil {
- for key, value := range new {
- //key state insert or update
- //insert
- if !mp[key] {
- changes[key] = createAddChange(value)
- } else {
- //update
- oldValue := old[key]
- if !reflect.DeepEqual(oldValue, value) {
- changes[key] = createModifyChange(oldValue, value)
- }
- }
- delete(mp, key)
- }
- }
-
- // remove del keys
- for key := range mp {
- //get old value and del
- oldValue := old[key]
- changes[key] = createDeletedChange(oldValue)
- }
-
- return changes
-}
-
-func createModifyChange(oldValue interface{}, newValue interface{}) Change {
- return Change{
- OldValue: oldValue,
- NewValue: newValue,
- ChangeType: MODIFIED,
- }
-}
-
-func createAddChange(newValue interface{}) Change {
- return Change{
- NewValue: newValue,
- ChangeType: ADDED,
- }
-}
-
-func createDeletedChange(oldValue interface{}) Change {
- return Change{
- OldValue: oldValue,
- ChangeType: DELETED,
- }
-}
-
-var (
- MaxDepth = 32
-)
-
-// Merge recursively merges the src and dst maps. Key conflicts are resolved by
-// preferring src, or recursively descending, if both src and dst are maps.
-// borrow code from https://github.com/peterbourgon/mergemap
-func Merge(dst, src map[string]interface{}) map[string]interface{} {
- return merge(dst, src, 0, false)
-}
-
-func MergeOverwriteSlice(dst, src map[string]interface{}) map[string]interface{} {
- return merge(dst, src, 0, true)
-}
-
-// overwrite means overwrite slice value
-func merge(dst, src map[string]interface{}, depth int, overwrite bool) map[string]interface{} {
- if depth > MaxDepth {
- panic("too deep!")
- }
- for key, srcVal := range src {
- if dstVal, ok := dst[key]; ok {
- srcMap, srcMapOk := mapify(srcVal)
- dstMap, dstMapOk := mapify(dstVal)
- if srcMapOk && dstMapOk {
- srcVal = merge(dstMap, srcMap, depth+1, overwrite)
- goto REWRITE
- }
- if overwrite {
- goto REWRITE
- }
- srcSlice, srcSliceOk := sliceutils.TakeSliceArg(srcVal)
- dstSlice, dstSliceOk := sliceutils.TakeSliceArg(dstVal)
- if srcSliceOk && dstSliceOk {
- merged := make([]interface{}, 0)
- kv := make(map[interface{}]struct{})
- for _, item := range dstSlice {
- if !reflect.ValueOf(item).Type().Comparable() {
- merged = append(merged, item)
- continue
- }
- if _, exists := kv[item]; !exists {
- merged = append(merged, item)
- kv[item] = struct{}{}
- }
- }
- for _, item := range srcSlice {
- if !reflect.ValueOf(item).Type().Comparable() {
- merged = append(merged, item)
- continue
- }
- if _, exists := kv[item]; !exists {
- merged = append(merged, item)
- kv[item] = struct{}{}
- }
- }
- srcVal = merged
- }
- }
- REWRITE:
- dst[key] = srcVal
- }
- return dst
-}
-
-func mapify(i interface{}) (map[string]interface{}, bool) {
- value := reflect.ValueOf(i)
- if value.Kind() == reflect.Map {
- m := map[string]interface{}{}
- for _, k := range value.MapKeys() {
- m[k.String()] = value.MapIndex(k).Interface()
- }
- return m, true
- }
- return map[string]interface{}{}, false
-}
-
-func ConvertInt642String(data map[string]interface{}) {
- lo.ForEach(lo.Entries(data), func(item lo.Entry[string, interface{}], index int) {
- if item.Value != nil {
- data[item.Key] = convertInt642String(item.Value)
- }
- })
-}
-
-func convertInt642String(data interface{}) interface{} {
- switch v := data.(type) {
- case int64:
- return strconv.FormatInt(v, 10)
- case *int64:
- if v != nil {
- return lo.ToPtr(strconv.FormatInt(*v, 10))
- } else {
- return (*string)(nil)
- }
- case []int64:
- return lo.Map[int64, string](v, func(item int64, index int) string {
- return strconv.FormatInt(item, 10)
- })
- case []*int64:
- return lo.Map[*int64, *string](v, func(item *int64, index int) *string {
- if item != nil {
- return lo.ToPtr(strconv.FormatInt(*item, 10))
- }
- return (*string)(nil)
- })
- case []interface{}:
- return lo.Map[interface{}, interface{}](v, func(item interface{}, index int) interface{} {
- return convertInt642String(item)
- })
- }
- return data
-}
diff --git a/toolkit/maputils/maputils_test.go b/toolkit/maputils/maputils_test.go
deleted file mode 100644
index cecce30e..00000000
--- a/toolkit/maputils/maputils_test.go
+++ /dev/null
@@ -1,265 +0,0 @@
-package maputils_test
-
-import (
- "bytes"
- "encoding/json"
- "github.com/goccy/go-reflect"
- "testing"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/maputils"
-)
-
-func TestDiff(t *testing.T) {
- type args struct {
- new map[string]interface{}
- old map[string]interface{}
- }
- tests := []struct {
- name string
- args args
- want map[string]maputils.Change
- }{
- {
- name: "",
- args: args{
- new: map[string]interface{}{
- "gdd.port": 6060,
- "gdd.read.timeout": "30s",
- },
- old: map[string]interface{}{
- "gdd.port": 3000,
- "gdd.read.timeout": "30s",
- },
- },
- want: map[string]maputils.Change{
- "gdd.port": {
- OldValue: 3000,
- NewValue: 6060,
- ChangeType: maputils.MODIFIED,
- },
- },
- },
- {
- name: "",
- args: args{
- new: map[string]interface{}{
- "gdd.port": 6060,
- "gdd.read.timeout": "30s",
- },
- old: map[string]interface{}{
- "gdd.port": 6060,
- },
- },
- want: map[string]maputils.Change{
- "gdd.read.timeout": {
- OldValue: nil,
- NewValue: "30s",
- ChangeType: maputils.ADDED,
- },
- },
- },
- {
- name: "",
- args: args{
- new: map[string]interface{}{
- "gdd.port": 6060,
- },
- old: map[string]interface{}{
- "gdd.port": 6060,
- "gdd.read.timeout": "30s",
- },
- },
- want: map[string]maputils.Change{
- "gdd.read.timeout": {
- OldValue: "30s",
- NewValue: nil,
- ChangeType: maputils.DELETED,
- },
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := maputils.Diff(tt.args.new, tt.args.old); !reflect.DeepEqual(got, tt.want) {
- t.Errorf("Diff() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestMerge(t *testing.T) {
- for _, tuple := range []struct {
- src string
- dst string
- expected string
- }{
- {
- src: `{}`,
- dst: `{}`,
- expected: `{}`,
- },
- {
- src: `{"b":2}`,
- dst: `{"a":1}`,
- expected: `{"a":1,"b":2}`,
- },
- {
- src: `{"a":0}`,
- dst: `{"a":1}`,
- expected: `{"a":0}`,
- },
- {
- src: `{"a":{ "y":2}}`,
- dst: `{"a":{"x":1 }}`,
- expected: `{"a":{"x":1, "y":2}}`,
- },
- {
- src: `{"a":{"x":2}}`,
- dst: `{"a":{"x":1}}`,
- expected: `{"a":{"x":2}}`,
- },
- {
- src: `{"a":{ "y":7, "z":8}}`,
- dst: `{"a":{"x":1, "y":2 }}`,
- expected: `{"a":{"x":1, "y":7, "z":8}}`,
- },
- {
- src: `{"1": { "b":1, "2": { "3": { "b":3, "n":[1,2]} } }}`,
- dst: `{"1": { "2": { "3": {"a":"A", "n":"xxx"} }, "a":3 }}`,
- expected: `{"1": { "b":1, "2": { "3": {"a":"A", "b":3, "n":[1,2]} }, "a":3 }}`,
- },
- {
- src: `{"1": { "b":1, "2": { "3": { "b":3, "n":[1,2]} } }}`,
- dst: `{"1": { "2": { "3": {"a":"A", "n":[3]} }, "a":3 }}`,
- expected: `{"1": { "b":1, "2": { "3": {"a":"A", "b":3, "n":[3,1,2]} }, "a":3 }}`,
- },
- {
- src: `{"1": { "b":1, "2": { "3": { "b":3, "n":[1,2]} } }}`,
- dst: `{"1": { "2": { "3": {"a":"A", "n":[3,1]} }, "a":3 }}`,
- expected: `{"1": { "b":1, "2": { "3": {"a":"A", "b":3, "n":[3,1,2]} }, "a":3 }}`,
- },
- {
- src: `{"1": { "b":1, "2": { "3": { "b":3, "n":[1,{"c":4,"d":5}]} } }}`,
- dst: `{"1": { "2": { "3": {"a":"A", "n":[3,1,{"c":2}]} }, "a":3 }}`,
- expected: `{"1":{"2":{"3":{"a":"A","b":3,"n":[3,1,{"c":2},{"c":4,"d":5}]}},"a":3,"b":1}}`,
- },
- } {
- var dst map[string]interface{}
- if err := json.Unmarshal([]byte(tuple.dst), &dst); err != nil {
- t.Error(err)
- continue
- }
-
- var src map[string]interface{}
- if err := json.Unmarshal([]byte(tuple.src), &src); err != nil {
- t.Error(err)
- continue
- }
-
- var expected map[string]interface{}
- if err := json.Unmarshal([]byte(tuple.expected), &expected); err != nil {
- t.Error(err)
- continue
- }
-
- got := maputils.Merge(dst, src)
- assert(t, expected, got)
- }
-}
-
-func assert(t *testing.T, expected, got map[string]interface{}) {
- expectedBuf, err := json.Marshal(expected)
- if err != nil {
- t.Error(err)
- return
- }
- gotBuf, err := json.Marshal(got)
- if err != nil {
- t.Error(err)
- return
- }
- if bytes.Compare(expectedBuf, gotBuf) != 0 {
- t.Errorf("expected %s, got %s", string(expectedBuf), string(gotBuf))
- return
- }
-}
-
-func TestMergeOverwriteSlice(t *testing.T) {
- for _, tuple := range []struct {
- src string
- dst string
- expected string
- }{
- {
- src: `{}`,
- dst: `{}`,
- expected: `{}`,
- },
- {
- src: `{"b":2}`,
- dst: `{"a":1}`,
- expected: `{"a":1,"b":2}`,
- },
- {
- src: `{"a":0}`,
- dst: `{"a":1}`,
- expected: `{"a":0}`,
- },
- {
- src: `{"a":{ "y":2}}`,
- dst: `{"a":{"x":1 }}`,
- expected: `{"a":{"x":1, "y":2}}`,
- },
- {
- src: `{"a":{"x":2}}`,
- dst: `{"a":{"x":1}}`,
- expected: `{"a":{"x":2}}`,
- },
- {
- src: `{"a":{ "y":7, "z":8}}`,
- dst: `{"a":{"x":1, "y":2 }}`,
- expected: `{"a":{"x":1, "y":7, "z":8}}`,
- },
- {
- src: `{"1": { "b":1, "2": { "3": { "b":3, "n":[1,2]} } }}`,
- dst: `{"1": { "2": { "3": {"a":"A", "n":"xxx"} }, "a":3 }}`,
- expected: `{"1": { "b":1, "2": { "3": {"a":"A", "b":3, "n":[1,2]} }, "a":3 }}`,
- },
- {
- src: `{"1": { "b":1, "2": { "3": { "b":3, "n":[1,2]} } }}`,
- dst: `{"1": { "2": { "3": {"a":"A", "n":[3]} }, "a":3 }}`,
- expected: `{"1": { "b":1, "2": { "3": {"a":"A", "b":3, "n":[1,2]} }, "a":3 }}`,
- },
- {
- src: `{"1": { "b":1, "2": { "3": { "b":3, "n":[1,2]} } }}`,
- dst: `{"1": { "2": { "3": {"a":"A", "n":[3,1]} }, "a":3 }}`,
- expected: `{"1": { "b":1, "2": { "3": {"a":"A", "b":3, "n":[1,2]} }, "a":3 }}`,
- },
- {
- src: `{"1": { "b":1, "2": { "3": { "b":3, "n":[1,{"c":4,"d":5}]} } }}`,
- dst: `{"1": { "2": { "3": {"a":"A", "n":[3,1,{"c":2}]} }, "a":3 }}`,
- expected: `{"1":{"2":{"3":{"a":"A","b":3,"n":[1,{"c":4,"d":5}]}},"a":3,"b":1}}`,
- },
- } {
- var dst map[string]interface{}
- if err := json.Unmarshal([]byte(tuple.dst), &dst); err != nil {
- t.Error(err)
- continue
- }
-
- var src map[string]interface{}
- if err := json.Unmarshal([]byte(tuple.src), &src); err != nil {
- t.Error(err)
- continue
- }
-
- var expected map[string]interface{}
- if err := json.Unmarshal([]byte(tuple.expected), &expected); err != nil {
- t.Error(err)
- continue
- }
-
- got := maputils.MergeOverwriteSlice(dst, src)
- assert(t, expected, got)
- }
-}
diff --git a/toolkit/memberlist/.gitignore b/toolkit/memberlist/.gitignore
deleted file mode 100644
index 1f3c4395..00000000
--- a/toolkit/memberlist/.gitignore
+++ /dev/null
@@ -1,26 +0,0 @@
-# Compiled Object files, Static and Dynamic libs (Shared Objects)
-*.o
-*.a
-*.so
-
-# Folders
-_obj
-_test
-
-# Architecture specific extensions/prefixes
-*.[568vq]
-[568vq].out
-
-*.cgo1.go
-*.cgo2.c
-_cgo_defun.c
-_cgo_gotypes.go
-_cgo_export.*
-
-_testmain.go
-
-*.exe
-*.test
-.vagrant/
-.idea
-
diff --git a/toolkit/memberlist/LICENSE b/toolkit/memberlist/LICENSE
deleted file mode 100644
index c33dcc7c..00000000
--- a/toolkit/memberlist/LICENSE
+++ /dev/null
@@ -1,354 +0,0 @@
-Mozilla Public License, version 2.0
-
-1. Definitions
-
-1.1. “Contributor”
-
- means each individual or legal entity that creates, contributes to the
- creation of, or owns Covered Software.
-
-1.2. “Contributor Version”
-
- means the combination of the Contributions of others (if any) used by a
- Contributor and that particular Contributor’s Contribution.
-
-1.3. “Contribution”
-
- means Covered Software of a particular Contributor.
-
-1.4. “Covered Software”
-
- means Source Code Form to which the initial Contributor has attached the
- notice in Exhibit A, the Executable Form of such Source Code Form, and
- Modifications of such Source Code Form, in each case including portions
- thereof.
-
-1.5. “Incompatible With Secondary Licenses”
- means
-
- a. that the initial Contributor has attached the notice described in
- Exhibit B to the Covered Software; or
-
- b. that the Covered Software was made available under the terms of version
- 1.1 or earlier of the License, but not also under the terms of a
- Secondary License.
-
-1.6. “Executable Form”
-
- means any form of the work other than Source Code Form.
-
-1.7. “Larger Work”
-
- means a work that combines Covered Software with other material, in a separate
- file or files, that is not Covered Software.
-
-1.8. “License”
-
- means this document.
-
-1.9. “Licensable”
-
- means having the right to grant, to the maximum extent possible, whether at the
- time of the initial grant or subsequently, any and all of the rights conveyed by
- this License.
-
-1.10. “Modifications”
-
- means any of the following:
-
- a. any file in Source Code Form that results from an addition to, deletion
- from, or modification of the contents of Covered Software; or
-
- b. any new file in Source Code Form that contains any Covered Software.
-
-1.11. “Patent Claims” of a Contributor
-
- means any patent claim(s), including without limitation, method, process,
- and apparatus claims, in any patent Licensable by such Contributor that
- would be infringed, but for the grant of the License, by the making,
- using, selling, offering for sale, having made, import, or transfer of
- either its Contributions or its Contributor Version.
-
-1.12. “Secondary License”
-
- means either the GNU General Public License, Version 2.0, the GNU Lesser
- General Public License, Version 2.1, the GNU Affero General Public
- License, Version 3.0, or any later versions of those licenses.
-
-1.13. “Source Code Form”
-
- means the form of the work preferred for making modifications.
-
-1.14. “You” (or “Your”)
-
- means an individual or a legal entity exercising rights under this
- License. For legal entities, “You” includes any entity that controls, is
- controlled by, or is under common control with You. For purposes of this
- definition, “control” means (a) the power, direct or indirect, to cause
- the direction or management of such entity, whether by contract or
- otherwise, or (b) ownership of more than fifty percent (50%) of the
- outstanding shares or beneficial ownership of such entity.
-
-
-2. License Grants and Conditions
-
-2.1. Grants
-
- Each Contributor hereby grants You a world-wide, royalty-free,
- non-exclusive license:
-
- a. under intellectual property rights (other than patent or trademark)
- Licensable by such Contributor to use, reproduce, make available,
- modify, display, perform, distribute, and otherwise exploit its
- Contributions, either on an unmodified basis, with Modifications, or as
- part of a Larger Work; and
-
- b. under Patent Claims of such Contributor to make, use, sell, offer for
- sale, have made, import, and otherwise transfer either its Contributions
- or its Contributor Version.
-
-2.2. Effective Date
-
- The licenses granted in Section 2.1 with respect to any Contribution become
- effective for each Contribution on the date the Contributor first distributes
- such Contribution.
-
-2.3. Limitations on Grant Scope
-
- The licenses granted in this Section 2 are the only rights granted under this
- License. No additional rights or licenses will be implied from the distribution
- or licensing of Covered Software under this License. Notwithstanding Section
- 2.1(b) above, no patent license is granted by a Contributor:
-
- a. for any code that a Contributor has removed from Covered Software; or
-
- b. for infringements caused by: (i) Your and any other third party’s
- modifications of Covered Software, or (ii) the combination of its
- Contributions with other software (except as part of its Contributor
- Version); or
-
- c. under Patent Claims infringed by Covered Software in the absence of its
- Contributions.
-
- This License does not grant any rights in the trademarks, service marks, or
- logos of any Contributor (except as may be necessary to comply with the
- notice requirements in Section 3.4).
-
-2.4. Subsequent Licenses
-
- No Contributor makes additional grants as a result of Your choice to
- distribute the Covered Software under a subsequent version of this License
- (see Section 10.2) or under the terms of a Secondary License (if permitted
- under the terms of Section 3.3).
-
-2.5. Representation
-
- Each Contributor represents that the Contributor believes its Contributions
- are its original creation(s) or it has sufficient rights to grant the
- rights to its Contributions conveyed by this License.
-
-2.6. Fair Use
-
- This License is not intended to limit any rights You have under applicable
- copyright doctrines of fair use, fair dealing, or other equivalents.
-
-2.7. Conditions
-
- Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted in
- Section 2.1.
-
-
-3. Responsibilities
-
-3.1. Distribution of Source Form
-
- All distribution of Covered Software in Source Code Form, including any
- Modifications that You create or to which You contribute, must be under the
- terms of this License. You must inform recipients that the Source Code Form
- of the Covered Software is governed by the terms of this License, and how
- they can obtain a copy of this License. You may not attempt to alter or
- restrict the recipients’ rights in the Source Code Form.
-
-3.2. Distribution of Executable Form
-
- If You distribute Covered Software in Executable Form then:
-
- a. such Covered Software must also be made available in Source Code Form,
- as described in Section 3.1, and You must inform recipients of the
- Executable Form how they can obtain a copy of such Source Code Form by
- reasonable means in a timely manner, at a charge no more than the cost
- of distribution to the recipient; and
-
- b. You may distribute such Executable Form under the terms of this License,
- or sublicense it under different terms, provided that the license for
- the Executable Form does not attempt to limit or alter the recipients’
- rights in the Source Code Form under this License.
-
-3.3. Distribution of a Larger Work
-
- You may create and distribute a Larger Work under terms of Your choice,
- provided that You also comply with the requirements of this License for the
- Covered Software. If the Larger Work is a combination of Covered Software
- with a work governed by one or more Secondary Licenses, and the Covered
- Software is not Incompatible With Secondary Licenses, this License permits
- You to additionally distribute such Covered Software under the terms of
- such Secondary License(s), so that the recipient of the Larger Work may, at
- their option, further distribute the Covered Software under the terms of
- either this License or such Secondary License(s).
-
-3.4. Notices
-
- You may not remove or alter the substance of any license notices (including
- copyright notices, patent notices, disclaimers of warranty, or limitations
- of liability) contained within the Source Code Form of the Covered
- Software, except that You may alter any license notices to the extent
- required to remedy known factual inaccuracies.
-
-3.5. Application of Additional Terms
-
- You may choose to offer, and to charge a fee for, warranty, support,
- indemnity or liability obligations to one or more recipients of Covered
- Software. However, You may do so only on Your own behalf, and not on behalf
- of any Contributor. You must make it absolutely clear that any such
- warranty, support, indemnity, or liability obligation is offered by You
- alone, and You hereby agree to indemnify every Contributor for any
- liability incurred by such Contributor as a result of warranty, support,
- indemnity or liability terms You offer. You may include additional
- disclaimers of warranty and limitations of liability specific to any
- jurisdiction.
-
-4. Inability to Comply Due to Statute or Regulation
-
- If it is impossible for You to comply with any of the terms of this License
- with respect to some or all of the Covered Software due to statute, judicial
- order, or regulation then You must: (a) comply with the terms of this License
- to the maximum extent possible; and (b) describe the limitations and the code
- they affect. Such description must be placed in a text file included with all
- distributions of the Covered Software under this License. Except to the
- extent prohibited by statute or regulation, such description must be
- sufficiently detailed for a recipient of ordinary skill to be able to
- understand it.
-
-5. Termination
-
-5.1. The rights granted under this License will terminate automatically if You
- fail to comply with any of its terms. However, if You become compliant,
- then the rights granted under this License from a particular Contributor
- are reinstated (a) provisionally, unless and until such Contributor
- explicitly and finally terminates Your grants, and (b) on an ongoing basis,
- if such Contributor fails to notify You of the non-compliance by some
- reasonable means prior to 60 days after You have come back into compliance.
- Moreover, Your grants from a particular Contributor are reinstated on an
- ongoing basis if such Contributor notifies You of the non-compliance by
- some reasonable means, this is the first time You have received notice of
- non-compliance with this License from such Contributor, and You become
- compliant prior to 30 days after Your receipt of the notice.
-
-5.2. If You initiate litigation against any entity by asserting a patent
- infringement claim (excluding declaratory judgment actions, counter-claims,
- and cross-claims) alleging that a Contributor Version directly or
- indirectly infringes any patent, then the rights granted to You by any and
- all Contributors for the Covered Software under Section 2.1 of this License
- shall terminate.
-
-5.3. In the event of termination under Sections 5.1 or 5.2 above, all end user
- license agreements (excluding distributors and resellers) which have been
- validly granted by You or Your distributors under this License prior to
- termination shall survive termination.
-
-6. Disclaimer of Warranty
-
- Covered Software is provided under this License on an “as is” basis, without
- warranty of any kind, either expressed, implied, or statutory, including,
- without limitation, warranties that the Covered Software is free of defects,
- merchantable, fit for a particular purpose or non-infringing. The entire
- risk as to the quality and performance of the Covered Software is with You.
- Should any Covered Software prove defective in any respect, You (not any
- Contributor) assume the cost of any necessary servicing, repair, or
- correction. This disclaimer of warranty constitutes an essential part of this
- License. No use of any Covered Software is authorized under this License
- except under this disclaimer.
-
-7. Limitation of Liability
-
- Under no circumstances and under no legal theory, whether tort (including
- negligence), contract, or otherwise, shall any Contributor, or anyone who
- distributes Covered Software as permitted above, be liable to You for any
- direct, indirect, special, incidental, or consequential damages of any
- character including, without limitation, damages for lost profits, loss of
- goodwill, work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses, even if such party shall have been
- informed of the possibility of such damages. This limitation of liability
- shall not apply to liability for death or personal injury resulting from such
- party’s negligence to the extent applicable law prohibits such limitation.
- Some jurisdictions do not allow the exclusion or limitation of incidental or
- consequential damages, so this exclusion and limitation may not apply to You.
-
-8. Litigation
-
- Any litigation relating to this License may be brought only in the courts of
- a jurisdiction where the defendant maintains its principal place of business
- and such litigation shall be governed by laws of that jurisdiction, without
- reference to its conflict-of-law provisions. Nothing in this Section shall
- prevent a party’s ability to bring cross-claims or counter-claims.
-
-9. Miscellaneous
-
- This License represents the complete agreement concerning the subject matter
- hereof. If any provision of this License is held to be unenforceable, such
- provision shall be reformed only to the extent necessary to make it
- enforceable. Any law or regulation which provides that the language of a
- contract shall be construed against the drafter shall not be used to construe
- this License against a Contributor.
-
-
-10. Versions of the License
-
-10.1. New Versions
-
- Mozilla Foundation is the license steward. Except as provided in Section
- 10.3, no one other than the license steward has the right to modify or
- publish new versions of this License. Each version will be given a
- distinguishing version number.
-
-10.2. Effect of New Versions
-
- You may distribute the Covered Software under the terms of the version of
- the License under which You originally received the Covered Software, or
- under the terms of any subsequent version published by the license
- steward.
-
-10.3. Modified Versions
-
- If you create software not governed by this License, and you want to
- create a new license for such software, you may create and use a modified
- version of this License if you rename the license and remove any
- references to the name of the license steward (except to note that such
- modified license differs from this License).
-
-10.4. Distributing Source Code Form that is Incompatible With Secondary Licenses
- If You choose to distribute Source Code Form that is Incompatible With
- Secondary Licenses under the terms of this version of the License, the
- notice described in Exhibit B of this License must be attached.
-
-Exhibit A - Source Code Form License Notice
-
- This Source Code Form is subject to the
- terms of the Mozilla Public License, v.
- 2.0. If a copy of the MPL was not
- distributed with this file, You can
- obtain one at
- http://mozilla.org/MPL/2.0/.
-
-If it is not possible or desirable to put the notice in a particular file, then
-You may include the notice in a location (such as a LICENSE file in a relevant
-directory) where a recipient would be likely to look for such a notice.
-
-You may add additional accurate notices of copyright ownership.
-
-Exhibit B - “Incompatible With Secondary Licenses” Notice
-
- This Source Code Form is “Incompatible
- With Secondary Licenses”, as defined by
- the Mozilla Public License, v. 2.0.
-
diff --git a/toolkit/memberlist/Makefile b/toolkit/memberlist/Makefile
deleted file mode 100644
index e9b7b287..00000000
--- a/toolkit/memberlist/Makefile
+++ /dev/null
@@ -1,33 +0,0 @@
-SHELL := bash
-
-GOFILES ?= $(shell go list ./... | grep -v /vendor/)
-
-default: test
-
-test: vet subnet
- go test ./...
-
-integ: subnet
- INTEG_TESTS=yes go test ./...
-
-subnet:
- ./test/setup_subnet.sh
-
-cov:
- go test ./... -coverprofile=coverage.out
- go tool cover -html=coverage.out
-
-format:
- @echo "--> Running go fmt"
- @go fmt $(GOFILES)
-
-vet:
- @echo "--> Running go vet"
- @go vet -tags '$(GOTAGS)' $(GOFILES); if [ $$? -eq 1 ]; then \
- echo ""; \
- echo "Vet found suspicious constructs. Please check the reported constructs"; \
- echo "and fix them if necessary before submitting the code for review."; \
- exit 1; \
- fi
-
-.PHONY: default test integ subnet cov format vet
diff --git a/toolkit/memberlist/README.md b/toolkit/memberlist/README.md
deleted file mode 100644
index 6a2caa30..00000000
--- a/toolkit/memberlist/README.md
+++ /dev/null
@@ -1,73 +0,0 @@
-# memberlist [![GoDoc](https://godoc.org/github.com/hashicorp/memberlist?status.png)](https://godoc.org/github.com/hashicorp/memberlist) [![CircleCI](https://circleci.com/gh/hashicorp/memberlist.svg?style=svg)](https://circleci.com/gh/hashicorp/memberlist)
-
-memberlist is a [Go](http://www.golang.org) library that manages cluster
-membership and member failure detection using a gossip based protocol.
-
-The use cases for such a library are far-reaching: all distributed systems
-require membership, and memberlist is a re-usable solution to managing
-cluster membership and node failure detection.
-
-memberlist is eventually consistent but converges quickly on average.
-The speed at which it converges can be heavily tuned via various knobs
-on the protocol. Node failures are detected and network partitions are partially
-tolerated by attempting to communicate to potentially dead nodes through
-multiple routes.
-
-## Building
-
-If you wish to build memberlist you'll need Go version 1.2+ installed.
-
-Please check your installation with:
-
-```
-go version
-```
-
-## Usage
-
-Memberlist is surprisingly simple to use. An example is shown below:
-
-```go
-/* Create the initial memberlist from a safe configuration.
- Please reference the godoc for other default config types.
- http://godoc.org/github.com/hashicorp/memberlist#Config
-*/
-list, err := memberlist.Create(memberlist.DefaultLocalConfig())
-if err != nil {
- panic("Failed to create memberlist: " + err.Error())
-}
-
-// Join an existing cluster by specifying at least one known member.
-n, err := list.Join([]string{"1.2.3.4"})
-if err != nil {
- panic("Failed to join cluster: " + err.Error())
-}
-
-// Ask for members of the cluster
-for _, member := range list.Members() {
- fmt.Printf("Member: %s %s\n", member.Name, member.Addr)
-}
-
-// Continue doing whatever you need, memberlist will maintain membership
-// information in the background. Delegates can be used for receiving
-// events when members join or leave.
-```
-
-The most difficult part of memberlist is configuring it since it has many
-available knobs in order to tune state propagation delay and convergence times.
-Memberlist provides a default configuration that offers a good starting point,
-but errs on the side of caution, choosing values that are optimized for
-higher convergence at the cost of higher bandwidth usage.
-
-For complete documentation, see the associated [Godoc](http://godoc.org/github.com/hashicorp/memberlist).
-
-## Protocol
-
-memberlist is based on ["SWIM: Scalable Weakly-consistent Infection-style Process Group Membership Protocol"](http://ieeexplore.ieee.org/document/1028914/). However, we extend the protocol in a number of ways:
-
-* Several extensions are made to increase propagation speed and
-convergence rate.
-* Another set of extensions, that we call Lifeguard, are made to make memberlist more robust in the presence of slow message processing (due to factors such as CPU starvation, and network delay or loss).
-
-For details on all of these extensions, please read our paper "[Lifeguard : SWIM-ing with Situational Awareness](https://arxiv.org/abs/1707.00788)", along with the memberlist source. We welcome any questions related
-to the protocol on our issue tracker.
diff --git a/toolkit/memberlist/alive_delegate.go b/toolkit/memberlist/alive_delegate.go
deleted file mode 100644
index 615f4a90..00000000
--- a/toolkit/memberlist/alive_delegate.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package memberlist
-
-// AliveDelegate is used to involve a client in processing
-// a node "alive" message. When a node joins, either through
-// a UDP gossip or TCP push/pull, we update the state of
-// that node via an alive message. This can be used to filter
-// a node out and prevent it from being considered a peer
-// using application specific logic.
-type AliveDelegate interface {
- // NotifyAlive is invoked when a message about a live
- // node is received from the network. Returning a non-nil
- // error prevents the node from being considered a peer.
- NotifyAlive(peer *Node) error
-}
diff --git a/toolkit/memberlist/awareness.go b/toolkit/memberlist/awareness.go
deleted file mode 100644
index ea95c753..00000000
--- a/toolkit/memberlist/awareness.go
+++ /dev/null
@@ -1,69 +0,0 @@
-package memberlist
-
-import (
- "sync"
- "time"
-
- "github.com/armon/go-metrics"
-)
-
-// awareness manages a simple metric for tracking the estimated health of the
-// local node. Health is primary the node's ability to respond in the soft
-// real-time manner required for correct health checking of other nodes in the
-// cluster.
-type awareness struct {
- sync.RWMutex
-
- // max is the upper threshold for the timeout scale (the score will be
- // constrained to be from 0 <= score < max).
- max int
-
- // score is the current awareness score. Lower values are healthier and
- // zero is the minimum value.
- score int
-}
-
-// newAwareness returns a new awareness object.
-func newAwareness(max int) *awareness {
- return &awareness{
- max: max,
- score: 0,
- }
-}
-
-// ApplyDelta takes the given delta and applies it to the score in a thread-safe
-// manner. It also enforces a floor of zero and a max of max, so deltas may not
-// change the overall score if it's railed at one of the extremes.
-func (a *awareness) ApplyDelta(delta int) {
- a.Lock()
- initial := a.score
- a.score += delta
- if a.score < 0 {
- a.score = 0
- } else if a.score > (a.max - 1) {
- a.score = (a.max - 1)
- }
- final := a.score
- a.Unlock()
-
- if initial != final {
- metrics.SetGauge([]string{"memberlist", "health", "score"}, float32(final))
- }
-}
-
-// GetHealthScore returns the raw health score.
-func (a *awareness) GetHealthScore() int {
- a.RLock()
- score := a.score
- a.RUnlock()
- return score
-}
-
-// ScaleTimeout takes the given duration and scales it based on the current
-// score. Less healthyness will lead to longer timeouts.
-func (a *awareness) ScaleTimeout(timeout time.Duration) time.Duration {
- a.RLock()
- score := a.score
- a.RUnlock()
- return timeout * (time.Duration(score) + 1)
-}
diff --git a/toolkit/memberlist/awareness_test.go b/toolkit/memberlist/awareness_test.go
deleted file mode 100644
index c6ade10a..00000000
--- a/toolkit/memberlist/awareness_test.go
+++ /dev/null
@@ -1,41 +0,0 @@
-package memberlist
-
-import (
- "testing"
- "time"
-)
-
-func TestAwareness(t *testing.T) {
- cases := []struct {
- delta int
- score int
- timeout time.Duration
- }{
- {0, 0, 1 * time.Second},
- {-1, 0, 1 * time.Second},
- {-10, 0, 1 * time.Second},
- {1, 1, 2 * time.Second},
- {-1, 0, 1 * time.Second},
- {10, 7, 8 * time.Second},
- {-1, 6, 7 * time.Second},
- {-1, 5, 6 * time.Second},
- {-1, 4, 5 * time.Second},
- {-1, 3, 4 * time.Second},
- {-1, 2, 3 * time.Second},
- {-1, 1, 2 * time.Second},
- {-1, 0, 1 * time.Second},
- {-1, 0, 1 * time.Second},
- }
-
- a := newAwareness(8)
- for i, c := range cases {
- a.ApplyDelta(c.delta)
- if a.GetHealthScore() != c.score {
- t.Errorf("case %d: score mismatch %d != %d", i, a.score, c.score)
- }
- if timeout := a.ScaleTimeout(1 * time.Second); timeout != c.timeout {
- t.Errorf("case %d: scaled timeout mismatch %9.6f != %9.6f",
- i, timeout.Seconds(), c.timeout.Seconds())
- }
- }
-}
diff --git a/toolkit/memberlist/broadcast.go b/toolkit/memberlist/broadcast.go
deleted file mode 100644
index b1d158e2..00000000
--- a/toolkit/memberlist/broadcast.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package memberlist
-
-/*
-The broadcast mechanism works by maintaining a sorted list of messages to be
-sent out. When a message is to be broadcast, the retransmit count
-is set to zero and appended to the queue. The retransmit count serves
-as the "priority", ensuring that newer messages get sent first. Once
-a message hits the retransmit limit, it is removed from the queue.
-
-Additionally, older entries can be invalidated by new messages that
-are contradictory. For example, if we send "{suspect M1 inc: 1},
-then a following {alive M1 inc: 2} will invalidate that message
-*/
-
-type memberlistBroadcast struct {
- node string
- msg []byte
- notify chan struct{}
-}
-
-func NewMemberlistBroadcast(node string, msg []byte, notify chan struct{}) *memberlistBroadcast {
- return &memberlistBroadcast{node: node, msg: msg, notify: notify}
-}
-
-func (b *memberlistBroadcast) Invalidates(other Broadcast) bool {
- // Check if that broadcast is a memberlist type
- mb, ok := other.(*memberlistBroadcast)
- if !ok {
- return false
- }
-
- // Invalidates any message about the same node
- return b.node == mb.node
-}
-
-// memberlist.NamedBroadcast optional interface
-func (b *memberlistBroadcast) Name() string {
- return b.node
-}
-
-func (b *memberlistBroadcast) Message() []byte {
- return b.msg
-}
-
-func (b *memberlistBroadcast) Finished() {
- select {
- case b.notify <- struct{}{}:
- default:
- }
-}
diff --git a/toolkit/memberlist/broadcast_test.go b/toolkit/memberlist/broadcast_test.go
deleted file mode 100644
index cb7e6587..00000000
--- a/toolkit/memberlist/broadcast_test.go
+++ /dev/null
@@ -1,38 +0,0 @@
-package memberlist_test
-
-import (
- "reflect"
- "testing"
-
- "github.com/golang/mock/gomock"
- "github.com/stretchr/testify/require"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
- memmock "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist/mock"
-)
-
-func TestMemberlistBroadcast_Invalidates(t *testing.T) {
- m1 := memberlist.NewMemberlistBroadcast("test", nil, nil)
- m2 := memberlist.NewMemberlistBroadcast("foo", nil, nil)
-
- if m1.Invalidates(m2) || m2.Invalidates(m1) {
- t.Fatalf("unexpected invalidation")
- }
-
- if !m1.Invalidates(m1) {
- t.Fatalf("expected invalidation")
- }
-
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- b := memmock.NewMockBroadcast(ctrl)
- ok := m1.Invalidates(b)
- require.False(t, ok)
-}
-
-func TestMemberlistBroadcast_Message(t *testing.T) {
- m1 := memberlist.NewMemberlistBroadcast("test", []byte("test"), nil)
- msg := m1.Message()
- if !reflect.DeepEqual(msg, []byte("test")) {
- t.Fatalf("messages do not match")
- }
-}
diff --git a/toolkit/memberlist/config.go b/toolkit/memberlist/config.go
deleted file mode 100644
index cd699f0d..00000000
--- a/toolkit/memberlist/config.go
+++ /dev/null
@@ -1,378 +0,0 @@
-package memberlist
-
-import (
- "fmt"
- "io"
- "log"
- "net"
- "os"
- "strings"
- "time"
-
- "github.com/hashicorp/go-multierror"
-)
-
-type Config struct {
- // The name of this node. This must be unique in the cluster.
- Name string
-
- // Transport is a hook for providing custom code to communicate with
- // other nodes. If this is left nil, then memberlist will by default
- // make a NetTransport using BindAddr and BindPort from this structure.
- Transport Transport
-
- // Configuration related to what address to bind to and ports to
- // listen on. The port is used for both UDP and TCP gossip. It is
- // assumed other nodes are running on this port, but they do not need
- // to.
- BindAddr string
- BindPort int
-
- // Configuration related to what address to advertise to other
- // cluster members. Used for nat traversal.
- AdvertiseAddr string
- AdvertisePort int
-
- // ProtocolVersion is the configured protocol version that we
- // will _speak_. This must be between ProtocolVersionMin and
- // ProtocolVersionMax.
- ProtocolVersion uint8
-
- // TCPTimeout is the timeout for establishing a stream connection with
- // a remote node for a full state sync, and for stream read and write
- // operations. This is a legacy name for backwards compatibility, but
- // should really be called StreamTimeout now that we have generalized
- // the transport.
- TCPTimeout time.Duration
-
- // IndirectChecks is the number of nodes that will be asked to perform
- // an indirect probe of a node in the case a direct probe fails. Memberlist
- // waits for an ack from any single indirect node, so increasing this
- // number will increase the likelihood that an indirect probe will succeed
- // at the expense of bandwidth.
- IndirectChecks int
-
- // RetransmitMult is the multiplier for the number of retransmissions
- // that are attempted for messages broadcasted over gossip. The actual
- // count of retransmissions is calculated using the formula:
- //
- // Retransmits = RetransmitMult * log(N+1)
- //
- // This allows the retransmits to scale properly with cluster size. The
- // higher the multiplier, the more likely a failed broadcast is to converge
- // at the expense of increased bandwidth.
- RetransmitMult int
-
- // SuspicionMult is the multiplier for determining the time an
- // inaccessible node is considered suspect before declaring it dead.
- // The actual timeout is calculated using the formula:
- //
- // SuspicionTimeout = SuspicionMult * log(N+1) * ProbeInterval
- //
- // This allows the timeout to scale properly with expected propagation
- // delay with a larger cluster size. The higher the multiplier, the longer
- // an inaccessible node is considered part of the cluster before declaring
- // it dead, giving that suspect node more time to refute if it is indeed
- // still alive.
- SuspicionMult int
-
- // SuspicionMaxTimeoutMult is the multiplier applied to the
- // SuspicionTimeout used as an upper bound on detection time. This max
- // timeout is calculated using the formula:
- //
- // SuspicionMaxTimeout = SuspicionMaxTimeoutMult * SuspicionTimeout
- //
- // If everything is working properly, confirmations from other nodes will
- // accelerate suspicion timers in a manner which will cause the timeout
- // to reach the base SuspicionTimeout before that elapses, so this value
- // will typically only come into play if a node is experiencing issues
- // communicating with other nodes. It should be set to a something fairly
- // large so that a node having problems will have a lot of chances to
- // recover before falsely declaring other nodes as failed, but short
- // enough for a legitimately isolated node to still make progress marking
- // nodes failed in a reasonable amount of time.
- SuspicionMaxTimeoutMult int
-
- // PushPullInterval is the interval between complete state syncs.
- // Complete state syncs are done with a single node over TCP and are
- // quite expensive relative to standard gossiped messages. Setting this
- // to zero will disable state push/pull syncs completely.
- //
- // Setting this interval lower (more frequent) will increase convergence
- // speeds across larger clusters at the expense of increased bandwidth
- // usage.
- PushPullInterval time.Duration
-
- // ProbeInterval and ProbeTimeout are used to configure probing
- // behavior for memberlist.
- //
- // ProbeInterval is the interval between random node probes. Setting
- // this lower (more frequent) will cause the memberlist cluster to detect
- // failed nodes more quickly at the expense of increased bandwidth usage.
- //
- // ProbeTimeout is the timeout to wait for an ack from a probed node
- // before assuming it is unhealthy. This should be set to 99-percentile
- // of RTT (round-trip time) on your network.
- ProbeInterval time.Duration
- ProbeTimeout time.Duration
-
- // DisableTcpPings will turn off the fallback TCP pings that are attempted
- // if the direct UDP ping fails. These get pipelined along with the
- // indirect UDP pings.
- DisableTcpPings bool
-
- // DisableTcpPingsForNode is like DisableTcpPings, but lets you control
- // whether to perform TCP pings on a node-by-node basis.
- DisableTcpPingsForNode func(nodeName string) bool
-
- // AwarenessMaxMultiplier will increase the probe interval if the node
- // becomes aware that it might be degraded and not meeting the soft real
- // time requirements to reliably probe other nodes.
- AwarenessMaxMultiplier int
-
- // GossipInterval and GossipNodes are used to configure the gossip
- // behavior of memberlist.
- //
- // GossipInterval is the interval between sending messages that need
- // to be gossiped that haven't been able to piggyback on probing messages.
- // If this is set to zero, non-piggyback gossip is disabled. By lowering
- // this value (more frequent) gossip messages are propagated across
- // the cluster more quickly at the expense of increased bandwidth.
- //
- // GossipNodes is the number of random nodes to send gossip messages to
- // per GossipInterval. Increasing this number causes the gossip messages
- // to propagate across the cluster more quickly at the expense of
- // increased bandwidth.
- //
- // GossipToTheDeadTime is the interval after which a node has died that
- // we will still try to gossip to it. This gives it a chance to refute.
- GossipInterval time.Duration
- GossipNodes int
- GossipToTheDeadTime time.Duration
-
- // WeightInterval is the interval between calculating local node weight and enqueue a message carrying the result
- // weight is calculated using the formula:
- //
- // Weight = (AwarenessMaxMultiplier - AwarenessScore) * 0.6 + AwarenessMaxMultiplier * CPUIdlePercent * 0.4
- //
- // local node weight messages will be gossiped to remote nodes. Remote nodes will use it to implement smooth
- // weighted round-robin load balance algo for choosing the next service provider
- // By default, this is 0, means not enabled
- WeightInterval time.Duration
-
- // GossipVerifyIncoming controls whether to enforce encryption for incoming
- // gossip. It is used for upshifting from unencrypted to encrypted gossip on
- // a running cluster.
- GossipVerifyIncoming bool
-
- // GossipVerifyOutgoing controls whether to enforce encryption for outgoing
- // gossip. It is used for upshifting from unencrypted to encrypted gossip on
- // a running cluster.
- GossipVerifyOutgoing bool
-
- // EnableCompression is used to control message compression. This can
- // be used to reduce bandwidth usage at the cost of slightly more CPU
- // utilization. This is only available starting at protocol version 1.
- EnableCompression bool
-
- // SecretKey is used to initialize the primary encryption key in a keyring.
- // The primary encryption key is the only key used to encrypt messages and
- // the first key used while attempting to decrypt messages. Providing a
- // value for this primary key will enable message-level encryption and
- // verification, and automatically install the key onto the keyring.
- // The value should be either 16, 24, or 32 bytes to select AES-128,
- // AES-192, or AES-256.
- SecretKey []byte
-
- // The keyring holds all of the encryption keys used internally. It is
- // automatically initialized using the SecretKey and SecretKeys values.
- Keyring *Keyring
-
- // Delegate and Events are delegates for receiving and providing
- // data to memberlist via callback mechanisms. For Delegate, see
- // the Delegate interface. For Events, see the EventDelegate interface.
- //
- // The DelegateProtocolMin/Max are used to guarantee protocol-compatibility
- // for any custom messages that the delegate might do (broadcasts,
- // local/remote state, etc.). If you don't set these, then the protocol
- // versions will just be zero, and version compliance won't be done.
- Delegate Delegate
- DelegateProtocolVersion uint8
- DelegateProtocolMin uint8
- DelegateProtocolMax uint8
- Events EventDelegate
- Conflict ConflictDelegate
- Merge MergeDelegate
- Ping PingDelegate
- Alive AliveDelegate
-
- // DNSConfigPath points to the system's DNS config file, usually located
- // at /etc/resolv.conf. It can be overridden via config for easier testing.
- DNSConfigPath string
-
- // LogOutput is the writer where logs should be sent. If this is not
- // set, logging will go to stderr by default. You cannot specify both LogOutput
- // and Logger at the same time.
- LogOutput io.Writer
-
- // Logger is a custom logger which you provide. If Logger is set, it will use
- // this for the internal logger. If Logger is not set, it will fall back to the
- // behavior for using LogOutput. You cannot specify both LogOutput and Logger
- // at the same time.
- Logger *log.Logger
-
- // Size of Memberlist's internal channel which handles UDP messages. The
- // size of this determines the size of the queue which Memberlist will keep
- // while UDP messages are handled.
- HandoffQueueDepth int
-
- // Maximum number of bytes that memberlist will put in a packet (this
- // will be for UDP packets by default with a NetTransport). A safe value
- // for this is typically 1400 bytes (which is the default). However,
- // depending on your network's MTU (Maximum Transmission Unit) you may
- // be able to increase this to get more content into each gossip packet.
- // This is a legacy name for backward compatibility but should really be
- // called PacketBufferSize now that we have generalized the transport.
- UDPBufferSize int
-
- // DeadNodeReclaimTime controls the time before a dead node's name can be
- // reclaimed by one with a different address or port. By default, this is 0,
- // meaning nodes cannot be reclaimed this way.
- DeadNodeReclaimTime time.Duration
-
- // RequireNodeNames controls if the name of a node is required when sending
- // a message to that node.
- RequireNodeNames bool
- // CIDRsAllowed If nil, allow any connection (default), otherwise specify all networks
- // allowed to connect (you must specify IPv6/IPv4 separately)
- // Using [] will block all connections.
- CIDRsAllowed []net.IPNet
-}
-
-// ParseCIDRs return a possible empty list of all Network that have been parsed
-// In case of error, it returns successfully parsed CIDRs and the last error found
-func ParseCIDRs(v []string) ([]net.IPNet, error) {
- nets := make([]net.IPNet, 0)
- if v == nil {
- return nets, nil
- }
- var errs error
- hasErrors := false
- for _, p := range v {
- _, net, err := net.ParseCIDR(strings.TrimSpace(p))
- if err != nil {
- err = fmt.Errorf("invalid cidr: %s", p)
- errs = multierror.Append(errs, err)
- hasErrors = true
- } else {
- nets = append(nets, *net)
- }
- }
- if !hasErrors {
- errs = nil
- }
- return nets, errs
-}
-
-// DefaultLANConfig returns a sane set of configurations for Memberlist.
-// It uses the hostname as the node name, and otherwise sets very conservative
-// values that are sane for most LAN environments. The default configuration
-// errs on the side of caution, choosing values that are optimized
-// for higher convergence at the cost of higher bandwidth usage. Regardless,
-// these values are a good starting point when getting started with memberlist.
-func DefaultLANConfig() *Config {
- hostname, _ := os.Hostname()
- return &Config{
- Name: hostname,
- BindAddr: "0.0.0.0",
- BindPort: 7946,
- AdvertiseAddr: "",
- AdvertisePort: 7946,
- ProtocolVersion: ProtocolVersion2Compatible,
- TCPTimeout: 10 * time.Second, // Timeout after 10 seconds
- IndirectChecks: 3, // Use 3 nodes for the indirect ping
- RetransmitMult: 4, // Retransmit a message 4 * log(N+1) nodes
- SuspicionMult: 4, // Suspect a node for 4 * log(N+1) * Interval
- SuspicionMaxTimeoutMult: 6, // For 10k nodes this will give a max timeout of 120 seconds when SuspicionMult is 5: 6 * suspicionTimeout(5, 10000, time.Second)
- PushPullInterval: 30 * time.Second, // Low frequency
- ProbeTimeout: 500 * time.Millisecond, // Reasonable RTT time for LAN
- ProbeInterval: 1 * time.Second, // Failure check every second
- DisableTcpPings: false, // TCP pings are safe, even with mixed versions
- AwarenessMaxMultiplier: 8, // Probe interval backs off to 8 seconds
-
- GossipNodes: 3, // Gossip to 3 nodes
- GossipInterval: 200 * time.Millisecond, // Gossip more rapidly
- GossipToTheDeadTime: 30 * time.Second, // Same as push/pull
- GossipVerifyIncoming: true,
- GossipVerifyOutgoing: true,
-
- EnableCompression: true, // Enable compression by default
-
- SecretKey: nil,
- Keyring: nil,
-
- DNSConfigPath: "/etc/resolv.conf",
-
- HandoffQueueDepth: 1024,
- UDPBufferSize: 1400,
- CIDRsAllowed: nil, // same as allow all
- }
-}
-
-// DefaultWANConfig works like DefaultConfig, however it returns a configuration
-// that is optimized for most WAN environments. The default configuration is
-// still very conservative and errs on the side of caution.
-func DefaultWANConfig() *Config {
- conf := DefaultLANConfig()
- conf.TCPTimeout = 30 * time.Second
- conf.SuspicionMult = 6
- conf.PushPullInterval = 60 * time.Second
- conf.ProbeTimeout = 3 * time.Second
- conf.ProbeInterval = 5 * time.Second
- conf.GossipNodes = 4 // Gossip less frequently, but to an additional node
- conf.GossipInterval = 500 * time.Millisecond
- conf.GossipToTheDeadTime = 60 * time.Second
- return conf
-}
-
-// IPMustBeChecked return true if IPAllowed must be called
-func (c *Config) IPMustBeChecked() bool {
- return len(c.CIDRsAllowed) > 0
-}
-
-// IPAllowed return an error if access to memberlist is denied
-func (c *Config) IPAllowed(ip net.IP) error {
- if !c.IPMustBeChecked() {
- return nil
- }
- for _, n := range c.CIDRsAllowed {
- if n.Contains(ip) {
- return nil
- }
- }
- return fmt.Errorf("%s is not allowed", ip)
-}
-
-var LookupIP = net.LookupIP
-
-// AddrAllowed return an error if access to memberlist is denied
-// addr can either be an ip address or a dns address
-func (c *Config) AddrAllowed(addr string) error {
- if !c.IPMustBeChecked() {
- return nil
- }
- ip := net.ParseIP(addr)
- if ip == nil {
- ips, err := LookupIP(addr)
- if err != nil || len(ips) == 0 {
- return fmt.Errorf("%s is not allowed", addr)
- }
- ip = ips[0]
- }
- return c.IPAllowed(ip)
-}
-
-// Returns whether or not encryption is enabled
-func (c *Config) EncryptionEnabled() bool {
- return c.Keyring != nil && len(c.Keyring.GetKeys()) > 0
-}
diff --git a/toolkit/memberlist/config_test.go b/toolkit/memberlist/config_test.go
deleted file mode 100644
index 6f2a9bee..00000000
--- a/toolkit/memberlist/config_test.go
+++ /dev/null
@@ -1,122 +0,0 @@
-package memberlist
-
-import (
- "net"
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func Test_IsValidAddressDefaults(t *testing.T) {
- tests := []string{
- "127.0.0.1",
- "127.0.0.5",
- "10.0.0.9",
- "172.16.0.7",
- "192.168.2.1",
- "fe80::aede:48ff:fe00:1122",
- "::1",
- }
- config := DefaultLANConfig()
- for _, ip := range tests {
- localV4 := net.ParseIP(ip)
- if err := config.IPAllowed(localV4); err != nil {
- t.Fatalf("IP %s Localhost Should be accepted for LAN", ip)
- }
- }
- config = DefaultWANConfig()
- for _, ip := range tests {
- localV4 := net.ParseIP(ip)
- if err := config.IPAllowed(localV4); err != nil {
- t.Fatalf("IP %s Localhost Should be accepted for WAN", ip)
- }
- }
-}
-
-func Test_IsValidAddressOverride(t *testing.T) {
- t.Parallel()
- cases := []struct {
- name string
- allow []string
- success []string
- fail []string
- }{
- {
- name: "Default, nil allows all",
- allow: nil,
- success: []string{"127.0.0.5", "10.0.0.9", "192.168.1.7", "::1"},
- fail: []string{},
- },
- {
- name: "Only IPv4",
- allow: []string{"0.0.0.0/0"},
- success: []string{"127.0.0.5", "10.0.0.9", "192.168.1.7"},
- fail: []string{"fe80::38bc:4dff:fe62:b1ae", "::1"},
- },
- {
- name: "Only IPv6",
- allow: []string{"::0/0"},
- success: []string{"fe80::38bc:4dff:fe62:b1ae", "::1"},
- fail: []string{"127.0.0.5", "10.0.0.9", "192.168.1.7"},
- },
- {
- name: "Only 127.0.0.0/8 and ::1",
- allow: []string{"::1/128", "127.0.0.0/8"},
- success: []string{"127.0.0.5", "::1"},
- fail: []string{"::2", "178.250.0.187", "10.0.0.9", "192.168.1.7", "fe80::38bc:4dff:fe62:b1ae"},
- },
- }
-
- for _, testCase := range cases {
- t.Run(testCase.name, func(t *testing.T) {
- config := DefaultLANConfig()
- var err error
- config.CIDRsAllowed, err = ParseCIDRs(testCase.allow)
- if err != nil {
- t.Fatalf("failed parsing %s", testCase.allow)
- }
- for _, ips := range testCase.success {
- ip := net.ParseIP(ips)
- if err := config.IPAllowed(ip); err != nil {
- t.Fatalf("Test case with %s should pass", ip)
- }
- }
- for _, ips := range testCase.fail {
- ip := net.ParseIP(ips)
- if err := config.IPAllowed(ip); err == nil {
- t.Fatalf("Test case with %s should fail", ip)
- }
- }
- })
-
- }
-
-}
-
-func TestParseCIDRs(t *testing.T) {
- got, _ := ParseCIDRs([]string{"172.28.0.0/16"})
- config := DefaultWANConfig()
- config.CIDRsAllowed = got
- err := config.AddrAllowed("172.16.238.10")
- require.Error(t, err)
-
- LookupIP = func(host string) ([]net.IP, error) {
- return []net.IP{
- net.ParseIP("172.16.238.10"),
- }, nil
- }
- defer func() {
- LookupIP = net.LookupIP
- }()
- err = config.AddrAllowed("this is a dns")
- require.Error(t, err)
-
- LookupIP = func(host string) ([]net.IP, error) {
- return nil, nil
- }
- err = config.AddrAllowed("this is a dns")
- require.Error(t, err)
-
- _, err = ParseCIDRs([]string{"this is a dns"})
- require.Error(t, err)
-}
diff --git a/toolkit/memberlist/conflict_delegate.go b/toolkit/memberlist/conflict_delegate.go
deleted file mode 100644
index f52b136e..00000000
--- a/toolkit/memberlist/conflict_delegate.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package memberlist
-
-// ConflictDelegate is a used to inform a client that
-// a node has attempted to join which would result in a
-// name conflict. This happens if two clients are configured
-// with the same name but different addresses.
-type ConflictDelegate interface {
- // NotifyConflict is invoked when a name conflict is detected
- NotifyConflict(existing, other *Node)
-}
diff --git a/toolkit/memberlist/delegate.go b/toolkit/memberlist/delegate.go
deleted file mode 100644
index 67774628..00000000
--- a/toolkit/memberlist/delegate.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package memberlist
-
-// Delegate is the interface that clients must implement if they want to hook
-// into the gossip layer of Memberlist. All the methods must be thread-safe,
-// as they can and generally will be called concurrently.
-type Delegate interface {
- // NodeMeta is used to retrieve meta-data about the current node
- // when broadcasting an alive message. Its length is limited to
- // the given byte size. This metadata is available in the Node structure.
- NodeMeta(limit int) []byte
-
- // NotifyMsg is called when a user-data message is received.
- // Care should be taken that this method does not block, since doing
- // so would block the entire UDP packet receive loop. Additionally, the byte
- // slice may be modified after the call returns, so it should be copied if needed
- NotifyMsg([]byte)
-
- // GetBroadcasts is called when user data messages can be broadcast.
- // It can return a list of buffers to send. Each buffer should assume an
- // overhead as provided with a limit on the total byte size allowed.
- // The total byte size of the resulting data to send must not exceed
- // the limit. Care should be taken that this method does not block,
- // since doing so would block the entire UDP packet receive loop.
- GetBroadcasts(overhead, limit int) [][]byte
-
- // LocalState is used for a TCP Push/Pull. This is sent to
- // the remote side in addition to the membership information. Any
- // data can be sent here. See MergeRemoteState as well. The `join`
- // boolean indicates this is for a join instead of a push/pull.
- LocalState(join bool) []byte
-
- // MergeRemoteState is invoked after a TCP Push/Pull. This is the
- // state received from the remote side and is the result of the
- // remote side's LocalState call. The 'join'
- // boolean indicates this is for a join instead of a push/pull.
- MergeRemoteState(buf []byte, join bool)
-}
diff --git a/toolkit/memberlist/dns_client_interface.go b/toolkit/memberlist/dns_client_interface.go
deleted file mode 100644
index ad5cfefa..00000000
--- a/toolkit/memberlist/dns_client_interface.go
+++ /dev/null
@@ -1,13 +0,0 @@
-package memberlist
-
-import (
- "time"
-
- "github.com/miekg/dns"
-)
-
-//go:generate mockgen -destination ./mock/mock_dns_client_interface.go -package mock -source=./dns_client_interface.go
-
-type IDNSClient interface {
- Exchange(m *dns.Msg, address string) (r *dns.Msg, rtt time.Duration, err error)
-}
diff --git a/toolkit/memberlist/event_delegate.go b/toolkit/memberlist/event_delegate.go
deleted file mode 100644
index 3a38c8e2..00000000
--- a/toolkit/memberlist/event_delegate.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package memberlist
-
-// EventDelegate is a simpler delegate that is used only to receive
-// notifications about members joining and leaving. The methods in this
-// delegate may be called by multiple goroutines, but never concurrently.
-// This allows you to reason about ordering.
-type EventDelegate interface {
- // NotifyJoin is invoked when a node is detected to have joined.
- // The Node argument must not be modified.
- NotifyJoin(*Node)
-
- // NotifyLeave is invoked when a node is detected to have left.
- // The Node argument must not be modified.
- NotifyLeave(*Node)
-
- // NotifyUpdate is invoked when a node is detected to have
- // updated, usually involving the meta data. The Node argument
- // must not be modified.
- NotifyUpdate(*Node)
-
- // NotifyWeight is invoked when a node's weight is detected to have updated.
- // The Node argument must not be modified.
- NotifyWeight(*Node)
-
- // NotifySuspectSateChange is invoked when a node state is changed from StateAlive to StateSuspect or from StateSuspect to StateAlive
- NotifySuspectSateChange(*Node)
-}
-
-// ChannelEventDelegate is used to enable an application to receive
-// events about joins and leaves over a channel instead of a direct
-// function call.
-//
-// Care must be taken that events are processed in a timely manner from
-// the channel, since this delegate will block until an event can be sent.
-type ChannelEventDelegate struct {
- Ch chan<- NodeEvent
-}
-
-// NodeEventType are the types of events that can be sent from the
-// ChannelEventDelegate.
-type NodeEventType int
-
-const (
- NodeJoin NodeEventType = iota
- NodeLeave
- NodeUpdate
- NodeWeight
- NodeSuspect
-)
-
-// NodeEvent is a single event related to node activity in the memberlist.
-// The Node member of this struct must not be directly modified. It is passed
-// as a pointer to avoid unnecessary copies. If you wish to modify the node,
-// make a copy first.
-type NodeEvent struct {
- Event NodeEventType
- Node *Node
-}
-
-func (c *ChannelEventDelegate) NotifyJoin(n *Node) {
- node := *n
- c.Ch <- NodeEvent{NodeJoin, &node}
-}
-
-func (c *ChannelEventDelegate) NotifyLeave(n *Node) {
- node := *n
- c.Ch <- NodeEvent{NodeLeave, &node}
-}
-
-func (c *ChannelEventDelegate) NotifyUpdate(n *Node) {
- node := *n
- c.Ch <- NodeEvent{NodeUpdate, &node}
-}
-
-func (c *ChannelEventDelegate) NotifyWeight(n *Node) {
- node := *n
- c.Ch <- NodeEvent{NodeWeight, &node}
-}
-
-func (c *ChannelEventDelegate) NotifySuspectSateChange(n *Node) {
- node := *n
- c.Ch <- NodeEvent{NodeSuspect, &node}
-}
diff --git a/toolkit/memberlist/event_delegate_test.go b/toolkit/memberlist/event_delegate_test.go
deleted file mode 100644
index 1f488449..00000000
--- a/toolkit/memberlist/event_delegate_test.go
+++ /dev/null
@@ -1,77 +0,0 @@
-package memberlist
-
-import (
- "testing"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestChannelEventDelegate_NotifyJoin(t *testing.T) {
- eventCh := make(chan NodeEvent, 1)
- c := &ChannelEventDelegate{
- Ch: eventCh,
- }
- c.NotifyJoin(&Node{
- Name: "",
- Addr: "test",
- Port: 7946,
- })
- ne := <-eventCh
- require.Equal(t, NodeJoin, ne.Event)
-}
-
-func TestChannelEventDelegate_NotifyLeave(t *testing.T) {
- eventCh := make(chan NodeEvent, 1)
- c := &ChannelEventDelegate{
- Ch: eventCh,
- }
- c.NotifyLeave(&Node{
- Name: "",
- Addr: "test",
- Port: 7946,
- })
- ne := <-eventCh
- require.Equal(t, NodeLeave, ne.Event)
-}
-
-func TestChannelEventDelegate_NotifyUpdate(t *testing.T) {
- eventCh := make(chan NodeEvent, 1)
- c := &ChannelEventDelegate{
- Ch: eventCh,
- }
- c.NotifyUpdate(&Node{
- Name: "",
- Addr: "test",
- Port: 7946,
- })
- ne := <-eventCh
- require.Equal(t, NodeUpdate, ne.Event)
-}
-
-func TestChannelEventDelegate_NotifyWeight(t *testing.T) {
- eventCh := make(chan NodeEvent, 1)
- c := &ChannelEventDelegate{
- Ch: eventCh,
- }
- c.NotifyWeight(&Node{
- Name: "",
- Addr: "test",
- Port: 7946,
- })
- ne := <-eventCh
- require.Equal(t, NodeWeight, ne.Event)
-}
-
-func TestChannelEventDelegate_NotifySuspectSateChange(t *testing.T) {
- eventCh := make(chan NodeEvent, 1)
- c := &ChannelEventDelegate{
- Ch: eventCh,
- }
- c.NotifySuspectSateChange(&Node{
- Name: "",
- Addr: "test",
- Port: 7946,
- })
- ne := <-eventCh
- require.Equal(t, NodeSuspect, ne.Event)
-}
diff --git a/toolkit/memberlist/integ_test.go b/toolkit/memberlist/integ_test.go
deleted file mode 100644
index c23d04f0..00000000
--- a/toolkit/memberlist/integ_test.go
+++ /dev/null
@@ -1,91 +0,0 @@
-package memberlist
-
-import (
- "fmt"
- "log"
- "os"
- "testing"
- "time"
-)
-
-// CheckInteg will skip a test if integration testing is not enabled.
-func CheckInteg(t *testing.T) {
- if !IsInteg() {
- t.SkipNow()
- }
-}
-
-// IsInteg returns a boolean telling you if we're in integ testing mode.
-func IsInteg() bool {
- return os.Getenv("INTEG_TESTS") != ""
-}
-
-// Tests the memberlist by creating a cluster of 100 nodes
-// and checking that we get strong convergence of changes.
-func TestMemberlist_Integ(t *testing.T) {
- CheckInteg(t)
-
- num := 16
- var members []*Memberlist
-
- secret := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
- eventCh := make(chan NodeEvent, num)
-
- addr := "127.0.0.1"
- for i := 0; i < num; i++ {
- c := DefaultLANConfig()
- c.Name = fmt.Sprintf("%s:%d", addr, 12345+i)
- c.BindAddr = addr
- c.BindPort = 12345 + i
- c.ProbeInterval = 20 * time.Millisecond
- c.ProbeTimeout = 100 * time.Millisecond
- c.GossipInterval = 20 * time.Millisecond
- c.PushPullInterval = 200 * time.Millisecond
- c.SecretKey = secret
- c.Logger = log.New(os.Stderr, c.Name, log.LstdFlags)
-
- if i == 0 {
- c.Events = &ChannelEventDelegate{eventCh}
- }
-
- m, err := Create(c)
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
- defer m.Shutdown()
-
- members = append(members, m)
-
- if i > 0 {
- last := members[i-1]
- num, err := m.Join([]string{last.config.Name + "/" + last.config.Name})
- if num == 0 || err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
- }
- }
-
- // Wait and print debug info
- breakTimer := time.After(250 * time.Millisecond)
-WAIT:
- for {
- select {
- case e := <-eventCh:
- if e.Event == NodeJoin {
- t.Logf("[DEBUG] Node join: %v (%d)", *e.Node, members[0].NumMembers())
- } else {
- t.Logf("[DEBUG] Node leave: %v (%d)", *e.Node, members[0].NumMembers())
- }
- case <-breakTimer:
- break WAIT
- }
- }
-
- for idx, m := range members {
- got := m.NumMembers()
- if got != num {
- t.Errorf("bad num members at idx %d. Expected %d. Got %d.",
- idx, num, got)
- }
- }
-}
diff --git a/toolkit/memberlist/keyring.go b/toolkit/memberlist/keyring.go
deleted file mode 100644
index a2774a0c..00000000
--- a/toolkit/memberlist/keyring.go
+++ /dev/null
@@ -1,160 +0,0 @@
-package memberlist
-
-import (
- "bytes"
- "fmt"
- "sync"
-)
-
-type Keyring struct {
- // Keys stores the key data used during encryption and decryption. It is
- // ordered in such a way where the first key (index 0) is the primary key,
- // which is used for encrypting messages, and is the first key tried during
- // message decryption.
- keys [][]byte
-
- // The keyring lock is used while performing IO operations on the keyring.
- l sync.Mutex
-}
-
-// Init allocates substructures
-func (k *Keyring) init() {
- k.keys = make([][]byte, 0)
-}
-
-// NewKeyring constructs a new container for a set of encryption keys. The
-// keyring contains all key data used internally by memberlist.
-//
-// While creating a new keyring, you must do one of:
-// - Omit keys and primary key, effectively disabling encryption
-// - Pass a set of keys plus the primary key
-// - Pass only a primary key
-//
-// If only a primary key is passed, then it will be automatically added to the
-// keyring. If creating a keyring with multiple keys, one key must be designated
-// primary by passing it as the primaryKey. If the primaryKey does not exist in
-// the list of secondary keys, it will be automatically added at position 0.
-//
-// A key should be either 16, 24, or 32 bytes to select AES-128,
-// AES-192, or AES-256.
-func NewKeyring(keys [][]byte, primaryKey []byte) (*Keyring, error) {
- keyring := &Keyring{}
- keyring.init()
-
- if len(keys) > 0 || len(primaryKey) > 0 {
- if len(primaryKey) == 0 {
- return nil, fmt.Errorf("Empty primary key not allowed")
- }
- if err := keyring.AddKey(primaryKey); err != nil {
- return nil, err
- }
- for _, key := range keys {
- if err := keyring.AddKey(key); err != nil {
- return nil, err
- }
- }
- }
-
- return keyring, nil
-}
-
-// ValidateKey will check to see if the key is valid and returns an error if not.
-//
-// key should be either 16, 24, or 32 bytes to select AES-128,
-// AES-192, or AES-256.
-func ValidateKey(key []byte) error {
- if l := len(key); l != 16 && l != 24 && l != 32 {
- return fmt.Errorf("key size must be 16, 24 or 32 bytes")
- }
- return nil
-}
-
-// AddKey will install a new key on the ring. Adding a key to the ring will make
-// it available for use in decryption. If the key already exists on the ring,
-// this function will just return noop.
-//
-// key should be either 16, 24, or 32 bytes to select AES-128,
-// AES-192, or AES-256.
-func (k *Keyring) AddKey(key []byte) error {
- if err := ValidateKey(key); err != nil {
- return err
- }
-
- // No-op if key is already installed
- for _, installedKey := range k.keys {
- if bytes.Equal(installedKey, key) {
- return nil
- }
- }
-
- keys := append(k.keys, key)
- primaryKey := k.GetPrimaryKey()
- if primaryKey == nil {
- primaryKey = key
- }
- k.installKeys(keys, primaryKey)
- return nil
-}
-
-// UseKey changes the key used to encrypt messages. This is the only key used to
-// encrypt messages, so peers should know this key before this method is called.
-func (k *Keyring) UseKey(key []byte) error {
- for _, installedKey := range k.keys {
- if bytes.Equal(key, installedKey) {
- k.installKeys(k.keys, key)
- return nil
- }
- }
- return fmt.Errorf("Requested key is not in the keyring")
-}
-
-// RemoveKey drops a key from the keyring. This will return an error if the key
-// requested for removal is currently at position 0 (primary key).
-func (k *Keyring) RemoveKey(key []byte) error {
- if bytes.Equal(key, k.keys[0]) {
- return fmt.Errorf("Removing the primary key is not allowed")
- }
- for i, installedKey := range k.keys {
- if bytes.Equal(key, installedKey) {
- keys := append(k.keys[:i], k.keys[i+1:]...)
- k.installKeys(keys, k.keys[0])
- }
- }
- return nil
-}
-
-// installKeys will take out a lock on the keyring, and replace the keys with a
-// new set of keys. The key indicated by primaryKey will be installed as the new
-// primary key.
-func (k *Keyring) installKeys(keys [][]byte, primaryKey []byte) {
- k.l.Lock()
- defer k.l.Unlock()
-
- newKeys := [][]byte{primaryKey}
- for _, key := range keys {
- if !bytes.Equal(key, primaryKey) {
- newKeys = append(newKeys, key)
- }
- }
- k.keys = newKeys
-}
-
-// GetKeys returns the current set of keys on the ring.
-func (k *Keyring) GetKeys() [][]byte {
- k.l.Lock()
- defer k.l.Unlock()
-
- return k.keys
-}
-
-// GetPrimaryKey returns the key on the ring at position 0. This is the key used
-// for encrypting messages, and is the first key tried for decrypting messages.
-func (k *Keyring) GetPrimaryKey() (key []byte) {
- k.l.Lock()
- defer k.l.Unlock()
-
- if len(k.keys) > 0 {
- key = k.keys[0]
- }
- return
-}
diff --git a/toolkit/memberlist/keyring_test.go b/toolkit/memberlist/keyring_test.go
deleted file mode 100644
index eec699fd..00000000
--- a/toolkit/memberlist/keyring_test.go
+++ /dev/null
@@ -1,154 +0,0 @@
-package memberlist
-
-import (
- "bytes"
- "testing"
-)
-
-var TestKeys [][]byte = [][]byte{
- []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
- []byte{15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0},
- []byte{8, 9, 10, 11, 12, 13, 14, 15, 0, 1, 2, 3, 4, 5, 6, 7},
-}
-
-func TestKeyring_EmptyRing(t *testing.T) {
- // Keyrings can be created with no encryption keys (disabled encryption)
- keyring, err := NewKeyring(nil, nil)
- if err != nil {
- t.Fatalf("err: %s", err)
- }
-
- keys := keyring.GetKeys()
- if len(keys) != 0 {
- t.Fatalf("Expected 0 keys but have %d", len(keys))
- }
-}
-
-func TestKeyring_PrimaryOnly(t *testing.T) {
- // Keyrings can be created using only a primary key
- keyring, err := NewKeyring(nil, TestKeys[0])
- if err != nil {
- t.Fatalf("err: %s", err)
- }
-
- keys := keyring.GetKeys()
- if len(keys) != 1 {
- t.Fatalf("Expected 1 key but have %d", len(keys))
- }
-}
-
-func TestKeyring_GetPrimaryKey(t *testing.T) {
- keyring, err := NewKeyring(TestKeys, TestKeys[1])
- if err != nil {
- t.Fatalf("err: %s", err)
- }
-
- // GetPrimaryKey returns correct key
- primaryKey := keyring.GetPrimaryKey()
- if !bytes.Equal(primaryKey, TestKeys[1]) {
- t.Fatalf("Unexpected primary key: %v", primaryKey)
- }
-}
-
-func TestKeyring_AddRemoveUse(t *testing.T) {
- keyring, err := NewKeyring(nil, TestKeys[1])
- if err != nil {
- t.Fatalf("err :%s", err)
- }
-
- // Use non-existent key throws error
- if err := keyring.UseKey(TestKeys[2]); err == nil {
- t.Fatalf("Expected key not installed error")
- }
-
- // Add key to ring
- if err := keyring.AddKey(TestKeys[2]); err != nil {
- t.Fatalf("err: %s", err)
- }
-
- keys := keyring.GetKeys()
- if !bytes.Equal(keys[0], TestKeys[1]) {
- t.Fatalf("Unexpected primary key change")
- }
-
- if len(keys) != 2 {
- t.Fatalf("Expected 2 keys but have %d", len(keys))
- }
-
- // Use key that exists should succeed
- if err := keyring.UseKey(TestKeys[2]); err != nil {
- t.Fatalf("err: %s", err)
- }
-
- primaryKey := keyring.GetPrimaryKey()
- if !bytes.Equal(primaryKey, TestKeys[2]) {
- t.Fatalf("Unexpected primary key: %v", primaryKey)
- }
-
- // Removing primary key should fail
- if err := keyring.RemoveKey(TestKeys[2]); err == nil {
- t.Fatalf("Expected primary key removal error")
- }
-
- // Removing non-primary key should succeed
- if err := keyring.RemoveKey(TestKeys[1]); err != nil {
- t.Fatalf("err: %s", err)
- }
-
- keys = keyring.GetKeys()
- if len(keys) != 1 {
- t.Fatalf("Expected 1 key but have %d", len(keys))
- }
-}
-
-func TestKeyRing_MultiKeyEncryptDecrypt(t *testing.T) {
- plaintext := []byte("this is a plain text message")
- extra := []byte("random data")
-
- keyring, err := NewKeyring(TestKeys, TestKeys[0])
- if err != nil {
- t.Fatalf("err: %s", err)
- }
-
- // First encrypt using the primary key and make sure we can decrypt
- var buf bytes.Buffer
- err = encryptPayload(1, TestKeys[0], plaintext, extra, &buf)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- msg, err := decryptPayload(keyring.GetKeys(), buf.Bytes(), extra)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- if !bytes.Equal(msg, plaintext) {
- t.Fatalf("bad: %v", msg)
- }
-
- // Now encrypt with a secondary key and try decrypting again.
- buf.Reset()
- err = encryptPayload(1, TestKeys[2], plaintext, extra, &buf)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- msg, err = decryptPayload(keyring.GetKeys(), buf.Bytes(), extra)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- if !bytes.Equal(msg, plaintext) {
- t.Fatalf("bad: %v", msg)
- }
-
- // Remove a key from the ring, and then try decrypting again
- if err := keyring.RemoveKey(TestKeys[2]); err != nil {
- t.Fatalf("err: %s", err)
- }
-
- msg, err = decryptPayload(keyring.GetKeys(), buf.Bytes(), extra)
- if err == nil {
- t.Fatalf("Expected no keys to decrypt message")
- }
-}
diff --git a/toolkit/memberlist/logging.go b/toolkit/memberlist/logging.go
deleted file mode 100644
index 2ca2bab4..00000000
--- a/toolkit/memberlist/logging.go
+++ /dev/null
@@ -1,30 +0,0 @@
-package memberlist
-
-import (
- "fmt"
- "net"
-)
-
-func LogAddress(addr net.Addr) string {
- if addr == nil {
- return "from="
- }
-
- return fmt.Sprintf("from=%s", addr.String())
-}
-
-func LogStringAddress(addr string) string {
- if addr == "" {
- return "from="
- }
-
- return fmt.Sprintf("from=%s", addr)
-}
-
-func LogConn(conn net.Conn) string {
- if conn == nil {
- return LogAddress(nil)
- }
-
- return LogAddress(conn.RemoteAddr())
-}
diff --git a/toolkit/memberlist/logging_test.go b/toolkit/memberlist/logging_test.go
deleted file mode 100644
index cc04b8a9..00000000
--- a/toolkit/memberlist/logging_test.go
+++ /dev/null
@@ -1,47 +0,0 @@
-package memberlist
-
-import (
- "fmt"
- "net"
- "testing"
-)
-
-func TestLogging_Address(t *testing.T) {
- s := LogAddress(nil)
- if s != "from=" {
- t.Fatalf("bad: %s", s)
- }
-
- addr, err := net.ResolveIPAddr("ip4", "127.0.0.1")
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- s = LogAddress(addr)
- if s != "from=127.0.0.1" {
- t.Fatalf("bad: %s", s)
- }
-}
-
-func TestLogging_Conn(t *testing.T) {
- s := LogConn(nil)
- if s != "from=" {
- t.Fatalf("bad: %s", s)
- }
-
- ln, err := net.Listen("tcp", ":0")
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- conn, err := net.Dial("tcp", ln.Addr().String())
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer conn.Close()
-
- s = LogConn(conn)
- if s != fmt.Sprintf("from=%s", conn.RemoteAddr().String()) {
- t.Fatalf("bad: %s", s)
- }
-}
diff --git a/toolkit/memberlist/memberlist.go b/toolkit/memberlist/memberlist.go
deleted file mode 100644
index 1e165596..00000000
--- a/toolkit/memberlist/memberlist.go
+++ /dev/null
@@ -1,765 +0,0 @@
-/*
-memberlist is a library that manages cluster
-membership and member failure detection using a gossip based protocol.
-
-The use cases for such a library are far-reaching: all distributed systems
-require membership, and memberlist is a re-usable solution to managing
-cluster membership and node failure detection.
-
-memberlist is eventually consistent but converges quickly on average.
-The speed at which it converges can be heavily tuned via various knobs
-on the protocol. Node failures are detected and network partitions are partially
-tolerated by attempting to communicate to potentially dead nodes through
-multiple routes.
-*/
-package memberlist
-
-import (
- "container/list"
- "errors"
- "fmt"
- "log"
- "net"
- "os"
- "strconv"
- "strings"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-
- multierror "github.com/hashicorp/go-multierror"
-)
-
-var errNodeNamesAreRequired = errors.New("memberlist: node names are required by configuration but one was not provided")
-
-type Memberlist struct {
- sequenceNum uint32 // Local sequence number
- incarnation uint32 // Local incarnation number
- numNodes uint32 // Number of known nodes (estimate)
- pushPullReq uint32 // Number of push/pull requests
-
- advertiseLock sync.RWMutex
- advertiseAddr string
- advertisePort uint16
-
- config *Config
- shutdown int32 // Used as an atomic boolean value
- shutdownCh chan struct{}
- leave int32 // Used as an atomic boolean value
- leaveBroadcast chan struct{}
-
- shutdownLock sync.Mutex // Serializes calls to Shutdown
- leaveLock sync.Mutex // Serializes calls to Leave
-
- transport NodeAwareTransport
-
- handoffCh chan struct{}
- highPriorityMsgQueue *list.List
- lowPriorityMsgQueue *list.List
- msgQueueLock sync.Mutex
-
- nodeLock sync.RWMutex
- nodes []*nodeState // Known nodes
- nodeMap map[string]*nodeState // Maps Node.Name -> NodeState
- nodeTimers map[string]*suspicion // Maps Node.Name -> suspicion timer
- awareness *awareness
-
- tickerLock sync.Mutex
- tickers []*time.Ticker
- stopTick chan struct{}
- probeIndex int
-
- ackLock sync.Mutex
- ackHandlers map[uint32]*ackHandler
-
- broadcasts *TransmitLimitedQueue
-
- logger *log.Logger
-}
-
-func (m *Memberlist) NodeMap() map[string]*nodeState {
- return m.nodeMap
-}
-
-func (m *Memberlist) AdvertiseAddr() string {
- return m.advertiseAddr
-}
-
-func (m *Memberlist) AdvertisePort() uint16 {
- return m.advertisePort
-}
-
-func (m *Memberlist) Nodes() []*nodeState {
- return m.nodes
-}
-
-func (m *Memberlist) SetNodes(nodes ...*nodeState) {
- m.nodes = nodes
-}
-
-func (m *Memberlist) Config() *Config {
- return m.config
-}
-
-// BuildVsnArray creates the array of Vsn
-func (conf *Config) BuildVsnArray() []uint8 {
- return []uint8{
- ProtocolVersionMin, ProtocolVersionMax, conf.ProtocolVersion,
- conf.DelegateProtocolMin, conf.DelegateProtocolMax,
- conf.DelegateProtocolVersion,
- }
-}
-
-// NewMemberlist creates the network listeners.
-// Does not schedule execution of background maintenance.
-func NewMemberlist(conf *Config) (*Memberlist, error) {
- if conf.ProtocolVersion < ProtocolVersionMin {
- return nil, fmt.Errorf("Protocol version '%d' too low. Must be in range: [%d, %d]",
- conf.ProtocolVersion, ProtocolVersionMin, ProtocolVersionMax)
- } else if conf.ProtocolVersion > ProtocolVersionMax {
- return nil, fmt.Errorf("Protocol version '%d' too high. Must be in range: [%d, %d]",
- conf.ProtocolVersion, ProtocolVersionMin, ProtocolVersionMax)
- }
-
- if len(conf.SecretKey) > 0 {
- if conf.Keyring == nil {
- keyring, err := NewKeyring(nil, conf.SecretKey)
- if err != nil {
- return nil, err
- }
- conf.Keyring = keyring
- } else {
- if err := conf.Keyring.AddKey(conf.SecretKey); err != nil {
- return nil, err
- }
- if err := conf.Keyring.UseKey(conf.SecretKey); err != nil {
- return nil, err
- }
- }
- }
-
- if conf.LogOutput != nil && conf.Logger != nil {
- return nil, fmt.Errorf("Cannot specify both LogOutput and Logger. Please choose a single log configuration setting.")
- }
-
- logDest := conf.LogOutput
- if logDest == nil {
- logDest = os.Stderr
- }
-
- logger := conf.Logger
- if logger == nil {
- logger = log.New(logDest, "", log.LstdFlags)
- }
-
- // Set up a network transport by default if a custom one wasn't given
- // by the config.
- transport := conf.Transport
- if transport == nil {
- nc := &NetTransportConfig{
- BindAddrs: []string{conf.BindAddr},
- BindPort: conf.BindPort,
- Logger: logger,
- }
-
- // See comment below for details about the retry in here.
- makeNetRetry := func(limit int) (*NetTransport, error) {
- var err error
- for try := 0; try < limit; try++ {
- var nt *NetTransport
- if nt, err = NewNetTransport(nc); err == nil {
- return nt, nil
- }
- if strings.Contains(err.Error(), "address already in use") {
- logger.Printf("[DEBUG] memberlist: Got bind error: %v", err)
- continue
- }
- }
-
- return nil, fmt.Errorf("failed to obtain an address: %v", err)
- }
-
- // The dynamic bind port operation is inherently racy because
- // even though we are using the kernel to find a port for us, we
- // are attempting to bind multiple protocols (and potentially
- // multiple addresses) with the same port number. We build in a
- // few retries here since this often gets transient errors in
- // busy unit tests.
- limit := 1
- if conf.BindPort == 0 {
- limit = 10
- }
-
- nt, err := makeNetRetry(limit)
- if err != nil {
- return nil, fmt.Errorf("Could not set up network transport: %v", err)
- }
- if conf.BindPort == 0 {
- port := nt.GetAutoBindPort()
- conf.BindPort = port
- conf.AdvertisePort = port
- logger.Printf("[DEBUG] memberlist: Using dynamic bind port %d", port)
- }
- transport = nt
- }
-
- nodeAwareTransport, ok := transport.(NodeAwareTransport)
- if !ok {
- logger.Printf("[DEBUG] memberlist: configured Transport is not a NodeAwareTransport and some features may not work as desired")
- nodeAwareTransport = &shimNodeAwareTransport{transport}
- }
-
- m := &Memberlist{
- config: conf,
- shutdownCh: make(chan struct{}),
- leaveBroadcast: make(chan struct{}, 1),
- transport: nodeAwareTransport,
- handoffCh: make(chan struct{}, 1),
- highPriorityMsgQueue: list.New(),
- lowPriorityMsgQueue: list.New(),
- nodeMap: make(map[string]*nodeState),
- nodeTimers: make(map[string]*suspicion),
- awareness: newAwareness(conf.AwarenessMaxMultiplier),
- ackHandlers: make(map[uint32]*ackHandler),
- broadcasts: &TransmitLimitedQueue{RetransmitMultGetter: func() int {
- return conf.RetransmitMult
- }},
- logger: logger,
- }
- m.broadcasts.NumNodes = func() int {
- return m.estNumNodes()
- }
-
- // Get the final advertise address from the transport, which may need
- // to see which address we bound to. We'll refresh this each time we
- // send out an alive message.
- if _, _, err := m.refreshAdvertise(); err != nil {
- return nil, err
- }
-
- go m.streamListen()
- go m.packetListen()
- go m.packetHandler()
- return m, nil
-}
-
-// Create will create a new Memberlist using the given configuration.
-// This will not connect to any other node (see Join) yet, but will start
-// all the listeners to allow other nodes to join this memberlist.
-// After creating a Memberlist, the configuration given should not be
-// modified by the user anymore.
-func Create(conf *Config) (*Memberlist, error) {
- m, err := NewMemberlist(conf)
- if err != nil {
- return nil, err
- }
- if err := m.setAlive(); err != nil {
- m.Shutdown()
- return nil, err
- }
- m.schedule()
- return m, nil
-}
-
-// Join is used to take an existing Memberlist and attempt to join a cluster
-// by contacting all the given hosts and performing a state sync. Initially,
-// the Memberlist only contains our own state, so doing this will cause
-// remote nodes to become aware of the existence of this node, effectively
-// joining the cluster.
-//
-// This returns the number of hosts successfully contacted and an error if
-// none could be reached. If an error is returned, the node did not successfully
-// join the cluster.
-func (m *Memberlist) Join(existing []string) (int, error) {
- numSuccess := 0
- var errs error
- for _, exist := range existing {
- addrs, err := m.ResolveAddr(exist)
- if err != nil {
- err = fmt.Errorf("Failed to resolve %s: %v", exist, err)
- errs = multierror.Append(errs, err)
- m.logger.Printf("[WARN] memberlist: %v", err)
- continue
- }
-
- for _, addr := range addrs {
- hp := joinHostPort(addr.ip, addr.port)
- a := Address{Addr: hp, Name: addr.nodeName}
- if err := m.pushPullNode(a, true); err != nil {
- err = fmt.Errorf("Failed to join %s: %v", addr.ip, err)
- errs = multierror.Append(errs, err)
- m.logger.Printf("[DEBUG] memberlist: %v", err)
- continue
- }
- numSuccess++
- }
- }
- if numSuccess > 0 {
- errs = nil
- }
- return numSuccess, errs
-}
-
-// IpPort holds information about a node we want to try to join.
-type IpPort struct {
- ip string
- port uint16
- nodeName string // optional
-}
-
-func (i *IpPort) SetIp(ip string) {
- i.ip = ip
-}
-
-func (i *IpPort) SetPort(port uint16) {
- i.port = port
-}
-
-func (i *IpPort) SetNodeName(nodeName string) {
- i.nodeName = nodeName
-}
-
-func (i IpPort) Ip() string {
- return i.ip
-}
-
-func (i IpPort) Port() uint16 {
- return i.port
-}
-
-func (i IpPort) NodeName() string {
- return i.nodeName
-}
-
-func NewIpPort(ip string, port uint16, nodeName string) IpPort {
- return IpPort{ip: ip, port: port, nodeName: nodeName}
-}
-
-// ResolveAddr is used to resolve the address into an address,
-// port, and error. If no port is given, use the default
-func (m *Memberlist) ResolveAddr(hostStr string) ([]IpPort, error) {
- // First peel off any leading node name. This is optional.
- nodeName := ""
- if slashIdx := strings.Index(hostStr, "/"); slashIdx >= 0 {
- if slashIdx == 0 {
- return nil, fmt.Errorf("empty node name provided")
- }
- nodeName = hostStr[0:slashIdx]
- hostStr = hostStr[slashIdx+1:]
- }
-
- // This captures the supplied port, or the default one.
- hostStr = ensurePort(hostStr, m.config.BindPort)
- host, sport, err := net.SplitHostPort(hostStr)
- if err != nil {
- return nil, err
- }
- if stringutils.IsEmpty(host) {
- return nil, errors.New("host should not be empty")
- }
- lport, err := strconv.ParseUint(sport, 10, 16)
- if err != nil {
- return nil, err
- }
- port := uint16(lport)
-
- return []IpPort{
- {ip: host, port: port, nodeName: nodeName},
- }, nil
-}
-
-// setAlive is used to mark this node as being alive. This is the same
-// as if we received an alive notification our own network channel for
-// ourself.
-func (m *Memberlist) setAlive() error {
- // Get the final advertise address from the transport, which may need
- // to see which address we bound to.
- addr, port, err := m.refreshAdvertise()
- if err != nil {
- return err
- }
-
- // Set any metadata from the delegate.
- var meta []byte
- if m.config.Delegate != nil {
- meta = m.config.Delegate.NodeMeta(MetaMaxSize)
- if len(meta) > MetaMaxSize {
- panic("Node meta data provided is longer than the limit")
- }
- }
-
- a := alive{
- Incarnation: m.nextIncarnation(),
- Node: m.config.Name,
- Addr: addr,
- Port: uint16(port),
- Meta: meta,
- Vsn: m.config.BuildVsnArray(),
- }
- m.aliveNode(&a, nil, true)
-
- return nil
-}
-
-func (m *Memberlist) getAdvertise() (string, uint16) {
- m.advertiseLock.RLock()
- defer m.advertiseLock.RUnlock()
- return m.advertiseAddr, m.advertisePort
-}
-
-func (m *Memberlist) setAdvertise(addr string, port int) {
- m.advertiseLock.Lock()
- defer m.advertiseLock.Unlock()
- m.advertiseAddr = addr
- m.advertisePort = uint16(port)
-}
-
-func (m *Memberlist) refreshAdvertise() (string, int, error) {
- addr, port, err := m.transport.FinalAdvertiseAddr(
- m.config.AdvertiseAddr, m.config.AdvertisePort)
- if err != nil {
- return "", 0, fmt.Errorf("Failed to get final advertise address: %v", err)
- }
- m.setAdvertise(addr, port)
- return addr, port, nil
-}
-
-// LocalNode is used to return the local Node
-func (m *Memberlist) LocalNode() *Node {
- m.nodeLock.RLock()
- defer m.nodeLock.RUnlock()
- state := m.nodeMap[m.config.Name]
- return &state.Node
-}
-
-// UpdateNode is used to trigger re-advertising the local node. This is
-// primarily used with a Delegate to support dynamic updates to the local
-// meta data. This will block until the update message is successfully
-// broadcasted to a member of the cluster, if any exist or until a specified
-// timeout is reached.
-func (m *Memberlist) UpdateNode(timeout time.Duration) error {
- // Get the node meta data
- var meta []byte
- if m.config.Delegate != nil {
- meta = m.config.Delegate.NodeMeta(MetaMaxSize)
- if len(meta) > MetaMaxSize {
- panic("Node meta data provided is longer than the limit")
- }
- }
-
- // Get the existing node
- m.nodeLock.RLock()
- state := m.nodeMap[m.config.Name]
- m.nodeLock.RUnlock()
-
- // Format a new alive message
- a := alive{
- Incarnation: m.nextIncarnation(),
- Node: m.config.Name,
- Addr: state.Addr,
- Port: state.Port,
- Meta: meta,
- Vsn: m.config.BuildVsnArray(),
- }
- notifyCh := make(chan struct{})
- m.aliveNode(&a, notifyCh, true)
-
- // Wait for the broadcast or a timeout
- if m.anyAlive() {
- var timeoutCh <-chan time.Time
- if timeout > 0 {
- timeoutCh = time.After(timeout)
- }
- select {
- case <-notifyCh:
- case <-timeoutCh:
- return fmt.Errorf("timeout waiting for update broadcast")
- }
- }
- return nil
-}
-
-func (m *Memberlist) SendToAddress(a Address, msg []byte) error {
- // Encode as a user message
- buf := make([]byte, 1, len(msg)+1)
- buf[0] = byte(userMsg)
- buf = append(buf, msg...)
-
- // Send the message
- return m.rawSendMsgPacket(a, nil, buf)
-}
-
-// SendBestEffort uses the unreliable packet-oriented interface of the transport
-// to target a user message at the given node (this does not use the gossip
-// mechanism). The maximum size of the message depends on the configured
-// UDPBufferSize for this memberlist instance.
-func (m *Memberlist) SendBestEffort(to *Node, msg []byte) error {
- // Encode as a user message
- buf := make([]byte, 1, len(msg)+1)
- buf[0] = byte(userMsg)
- buf = append(buf, msg...)
-
- // Send the message
- a := Address{Addr: to.Address(), Name: to.Name}
- return m.rawSendMsgPacket(a, to, buf)
-}
-
-// SendReliable uses the reliable stream-oriented interface of the transport to
-// target a user message at the given node (this does not use the gossip
-// mechanism). Delivery is guaranteed if no error is returned, and there is no
-// limit on the size of the message.
-func (m *Memberlist) SendReliable(to *Node, msg []byte) error {
- return m.sendUserMsg(to.FullAddress(), msg)
-}
-
-// Members returns a list of all known live nodes. The node structures
-// returned must not be modified. If you wish to modify a Node, make a
-// copy first.
-func (m *Memberlist) Members() []*Node {
- m.nodeLock.RLock()
- defer m.nodeLock.RUnlock()
-
- nodes := make([]*Node, 0, len(m.nodes))
- for _, n := range m.nodes {
- if !n.DeadOrLeft() {
- nodes = append(nodes, &n.Node)
- }
- }
-
- return nodes
-}
-
-// NumMembers returns the number of alive nodes currently known. Between
-// the time of calling this and calling Members, the number of alive nodes
-// may have changed, so this shouldn't be used to determine how many
-// members will be returned by Members.
-func (m *Memberlist) NumMembers() (alive int) {
- m.nodeLock.RLock()
- defer m.nodeLock.RUnlock()
-
- for _, n := range m.nodes {
- if !n.DeadOrLeft() {
- alive++
- }
- }
-
- return
-}
-
-// Leave will broadcast a leave message but will not shutdown the background
-// listeners, meaning the node will continue participating in gossip and state
-// updates.
-//
-// This will block until the leave message is successfully broadcasted to
-// a member of the cluster, if any exist or until a specified timeout
-// is reached.
-//
-// This method is safe to call multiple times, but must not be called
-// after the cluster is already shut down.
-func (m *Memberlist) Leave(timeout time.Duration) error {
- m.leaveLock.Lock()
- defer m.leaveLock.Unlock()
-
- if m.hasShutdown() {
- panic("leave after shutdown")
- }
-
- if !m.hasLeft() {
- atomic.StoreInt32(&m.leave, 1)
-
- m.nodeLock.Lock()
- state, ok := m.nodeMap[m.config.Name]
- m.nodeLock.Unlock()
- if !ok {
- m.logger.Printf("[WARN] memberlist: Leave but we're not in the node map.")
- return nil
- }
-
- // This dead message is special, because Node and From are the
- // same. This helps other nodes figure out that a node left
- // intentionally. When Node equals From, other nodes know for
- // sure this node is gone.
- d := dead{
- Incarnation: state.Incarnation,
- Node: state.Name,
- From: state.Name,
- }
- m.deadNode(&d)
-
- // Block until the broadcast goes out
- if m.anyAlive() {
- var timeoutCh <-chan time.Time
- if timeout > 0 {
- timeoutCh = time.After(timeout)
- }
- select {
- case <-m.leaveBroadcast:
- case <-timeoutCh:
- return fmt.Errorf("timeout waiting for leave broadcast")
- }
- }
- }
-
- return nil
-}
-
-// Check for any other alive node.
-func (m *Memberlist) anyAlive() bool {
- m.nodeLock.RLock()
- defer m.nodeLock.RUnlock()
- for _, n := range m.nodes {
- if !n.DeadOrLeft() && n.Name != m.config.Name {
- return true
- }
- }
- return false
-}
-
-// GetHealthScore gives this instance's idea of how well it is meeting the soft
-// real-time requirements of the protocol. Lower numbers are better, and zero
-// means "totally healthy".
-func (m *Memberlist) GetHealthScore() int {
- return m.awareness.GetHealthScore()
-}
-
-// ProtocolVersion returns the protocol version currently in use by
-// this memberlist.
-func (m *Memberlist) ProtocolVersion() uint8 {
- // NOTE: This method exists so that in the future we can control
- // any locking if necessary, if we change the protocol version at
- // runtime, etc.
- return m.config.ProtocolVersion
-}
-
-// Shutdown will stop any background maintenance of network activity
-// for this memberlist, causing it to appear "dead". A leave message
-// will not be broadcasted prior, so the cluster being left will have
-// to detect this node's shutdown using probing. If you wish to more
-// gracefully exit the cluster, call Leave prior to shutting down.
-//
-// This method is safe to call multiple times.
-func (m *Memberlist) Shutdown() error {
- m.shutdownLock.Lock()
- defer m.shutdownLock.Unlock()
-
- if m.hasShutdown() {
- return nil
- }
-
- // Shut down the transport first, which should block until it's
- // completely torn down. If we kill the memberlist-side handlers
- // those I/O handlers might get stuck.
- if err := m.transport.Shutdown(); err != nil {
- m.logger.Printf("[ERR] Failed to shutdown transport: %v", err)
- }
-
- // Now tear down everything else.
- atomic.StoreInt32(&m.shutdown, 1)
- close(m.shutdownCh)
- m.deschedule()
- return nil
-}
-
-func (m *Memberlist) hasShutdown() bool {
- return atomic.LoadInt32(&m.shutdown) == 1
-}
-
-func (m *Memberlist) hasLeft() bool {
- return atomic.LoadInt32(&m.leave) == 1
-}
-
-func (m *Memberlist) getNodeState(addr string) NodeStateType {
- m.nodeLock.RLock()
- defer m.nodeLock.RUnlock()
-
- n := m.nodeMap[addr]
- return n.State
-}
-
-func (m *Memberlist) getNodeStateChange(addr string) time.Time {
- m.nodeLock.RLock()
- defer m.nodeLock.RUnlock()
-
- n := m.nodeMap[addr]
- return n.StateChange
-}
-
-func (m *Memberlist) changeNode(addr string, f func(*nodeState)) {
- m.nodeLock.Lock()
- defer m.nodeLock.Unlock()
-
- n := m.nodeMap[addr]
- f(n)
-}
-
-// encodeAndBroadcast encodes a message and enqueues it for broadcast. Fails
-// silently if there is an encoding error.
-func (m *Memberlist) encodeAndBroadcast(node string, msgType messageType, msg interface{}) {
- m.encodeBroadcastNotify(node, msgType, msg, nil)
-}
-
-// encodeBroadcastNotify encodes a message and enqueues it for broadcast
-// and notifies the given channel when transmission is finished. Fails
-// silently if there is an encoding error.
-func (m *Memberlist) encodeBroadcastNotify(node string, msgType messageType, msg interface{}, notify chan struct{}) {
- buf, err := encode(msgType, msg)
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to encode message for broadcast: %s", err)
- } else {
- m.queueBroadcast(node, buf.Bytes(), notify)
- }
-}
-
-// queueBroadcast is used to start dissemination of a message. It will be
-// sent up to a configured number of times. The message could potentially
-// be invalidated by a future message about the same node
-func (m *Memberlist) queueBroadcast(node string, msg []byte, notify chan struct{}) {
- b := &memberlistBroadcast{node, msg, notify}
- m.broadcasts.QueueBroadcast(b)
-}
-
-// getBroadcasts is used to return a slice of broadcasts to send up to
-// a maximum byte size, while imposing a per-broadcast overhead. This is used
-// to fill a UDP packet with piggybacked data
-func (m *Memberlist) getBroadcasts(overhead, limit int) [][]byte {
- // Get memberlist messages first
- toSend := m.broadcasts.GetBroadcasts(overhead, limit)
-
- // Check if the user has anything to broadcast
- d := m.config.Delegate
- if d != nil {
- // Determine the bytes used already
- bytesUsed := 0
- for _, msg := range toSend {
- bytesUsed += len(msg) + overhead
- }
-
- // Check space remaining for user messages
- avail := limit - bytesUsed
- if avail > overhead+userMsgOverhead {
- userMsgs := d.GetBroadcasts(overhead+userMsgOverhead, avail)
-
- // Frame each user message
- for _, msg := range userMsgs {
- buf := make([]byte, 1, len(msg)+1)
- buf[0] = byte(userMsg)
- buf = append(buf, msg...)
- toSend = append(toSend, buf)
- }
- }
- }
- return toSend
-}
-
-// encodeWeightMsgAndBroadcast encodes a weight message and enqueues it for broadcast. Fails
-// silently if there is an encoding error.
-func (m *Memberlist) encodeWeightMsgAndBroadcast(node string, msg interface{}) {
- buf, err := encode(weightMsg, msg)
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to encode message for weight message broadcast: %s", err)
- } else {
- m.broadcasts.QueueBroadcast(&weightBroadcast{node, buf.Bytes()})
- }
-}
diff --git a/toolkit/memberlist/memberlist_interface.go b/toolkit/memberlist/memberlist_interface.go
deleted file mode 100644
index 48495d84..00000000
--- a/toolkit/memberlist/memberlist_interface.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package memberlist
-
-import (
- "net"
- "time"
-)
-
-//go:generate mockgen -destination ./mock/mock_memberlist_interface.go -package mock -source=./memberlist_interface.go
-
-type IMemberlist interface {
- Join(existing []string) (int, error)
- Ping(node string, addr net.Addr) (time.Duration, error)
- LocalNode() *Node
- UpdateNode(timeout time.Duration) error
- SendToAddress(a Address, msg []byte) error
- SendBestEffort(to *Node, msg []byte) error
- SendReliable(to *Node, msg []byte) error
- Members() []*Node
- NumMembers() (alive int)
- Leave(timeout time.Duration) error
- GetHealthScore() int
- ProtocolVersion() uint8
- Shutdown() error
- Config() *Config
- AdvertiseAddr() string
-}
diff --git a/toolkit/memberlist/memberlist_test.go b/toolkit/memberlist/memberlist_test.go
deleted file mode 100644
index 0b9c7a0b..00000000
--- a/toolkit/memberlist/memberlist_test.go
+++ /dev/null
@@ -1,2368 +0,0 @@
-package memberlist_test
-
-import (
- "bytes"
- "fmt"
- "io/ioutil"
- "log"
- "net"
- "os"
- "reflect"
- "runtime"
- "strconv"
- "strings"
- "sync"
- "testing"
- "time"
-
- "github.com/golang/mock/gomock"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
- memmock "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist/mock"
-
- "github.com/miekg/dns"
- "github.com/stretchr/testify/require"
-)
-
-func retry(t *testing.T, n int, w time.Duration, fn func(func(string, ...interface{}))) {
- t.Helper()
- for try := 1; try <= n; try++ {
- failed := false
- failFormat := ""
- failArgs := []interface{}{}
- failf := func(format string, args ...interface{}) {
- failed = true
- failFormat = format
- failArgs = args
- }
-
- fn(failf)
-
- if !failed {
- return
- }
- if try == n {
- t.Fatalf(failFormat, failArgs...)
- }
- time.Sleep(w)
- }
-}
-
-var bindLock sync.Mutex
-var bindNum byte = 10
-
-func getBindAddrNet(network byte) net.IP {
- bindLock.Lock()
- defer bindLock.Unlock()
-
- //result := net.IPv4(127, 0, network, bindNum)
- //bindNum++
- //if bindNum > 255 {
- // bindNum = 10
- //}
-
- result := net.IPv4(127, 0, 0, 1)
-
- return result
-}
-
-func getBindAddr() net.IP {
- return getBindAddrNet(0)
-}
-
-func testConfigNet(tb testing.TB, network byte) *memberlist.Config {
- tb.Helper()
-
- config := memberlist.DefaultLANConfig()
- config.BindAddr = getBindAddrNet(network).String()
- config.Name = config.BindAddr
- config.BindPort = 0 // choose free port
- config.RequireNodeNames = true
- config.Logger = log.New(os.Stderr, config.Name, log.LstdFlags)
- return config
-}
-
-func testConfig(tb testing.TB) *memberlist.Config {
- return testConfigNet(tb, 0)
-}
-
-func yield() {
- time.Sleep(250 * time.Millisecond)
-}
-
-type MockDelegate struct {
- mu sync.Mutex
- meta []byte
- msgs [][]byte
- broadcasts [][]byte
- state []byte
- remoteState []byte
-}
-
-func (m *MockDelegate) setMeta(meta []byte) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.meta = meta
-}
-
-func (m *MockDelegate) setState(state []byte) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.state = state
-}
-
-func (m *MockDelegate) setBroadcasts(broadcasts [][]byte) {
- m.mu.Lock()
- defer m.mu.Unlock()
- m.broadcasts = broadcasts
-}
-
-func (m *MockDelegate) getRemoteState() []byte {
- m.mu.Lock()
- defer m.mu.Unlock()
-
- out := make([]byte, len(m.remoteState))
- copy(out, m.remoteState)
- return out
-}
-
-func (m *MockDelegate) getMessages() [][]byte {
- m.mu.Lock()
- defer m.mu.Unlock()
-
- out := make([][]byte, len(m.msgs))
- for i, msg := range m.msgs {
- out[i] = make([]byte, len(msg))
- copy(out[i], msg)
- }
- return out
-}
-
-func (m *MockDelegate) NodeMeta(limit int) []byte {
- m.mu.Lock()
- defer m.mu.Unlock()
-
- return m.meta
-}
-
-func (m *MockDelegate) NotifyMsg(msg []byte) {
- m.mu.Lock()
- defer m.mu.Unlock()
-
- cp := make([]byte, len(msg))
- copy(cp, msg)
- m.msgs = append(m.msgs, cp)
-}
-
-func (m *MockDelegate) GetBroadcasts(overhead, limit int) [][]byte {
- m.mu.Lock()
- defer m.mu.Unlock()
-
- b := m.broadcasts
- m.broadcasts = nil
- return b
-}
-
-func (m *MockDelegate) LocalState(join bool) []byte {
- m.mu.Lock()
- defer m.mu.Unlock()
-
- return m.state
-}
-
-func (m *MockDelegate) MergeRemoteState(s []byte, join bool) {
- m.mu.Lock()
- defer m.mu.Unlock()
-
- m.remoteState = s
-}
-
-func TestDefaultLANConfig_protocolVersion(t *testing.T) {
- c := memberlist.DefaultLANConfig()
- if c.ProtocolVersion != memberlist.ProtocolVersion2Compatible {
- t.Fatalf("should be max: %d", c.ProtocolVersion)
- }
-}
-
-func TestCreate_protocolVersion(t *testing.T) {
- cases := []struct {
- name string
- version uint8
- err bool
- }{
- {"min", memberlist.ProtocolVersionMin, false},
- {"max", memberlist.ProtocolVersionMax, false},
- {"max+1", memberlist.ProtocolVersionMax + 1, true},
- {"min-1", memberlist.ProtocolVersionMax - 1, false},
- }
-
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- c := memberlist.DefaultLANConfig()
- c.BindAddr = getBindAddr().String()
- c.ProtocolVersion = tc.version
-
- m, err := memberlist.Create(c)
- if err == nil {
- require.NoError(t, m.Shutdown())
- }
-
- if tc.err && err == nil {
- t.Fatalf("Should've failed with version: %d", tc.version)
- } else if !tc.err && err != nil {
- t.Fatalf("Version '%d' error: %s", tc.version, err)
- }
- })
- }
-}
-
-func TestCreate_secretKey(t *testing.T) {
- cases := []struct {
- name string
- key []byte
- err bool
- }{
- {"size-0", make([]byte, 0), false},
- {"abc", []byte("abc"), true},
- {"size-16", make([]byte, 16), false},
- {"size-38", make([]byte, 38), true},
- }
-
- for _, tc := range cases {
- t.Run(tc.name, func(t *testing.T) {
- c := memberlist.DefaultLANConfig()
- c.BindAddr = getBindAddr().String()
- c.SecretKey = tc.key
-
- m, err := memberlist.Create(c)
- if err == nil {
- require.NoError(t, m.Shutdown())
- }
-
- if tc.err && err == nil {
- t.Fatalf("Should've failed with key: %#v", tc.key)
- } else if !tc.err && err != nil {
- t.Fatalf("Key '%#v' error: %s", tc.key, err)
- }
- })
- }
-}
-
-func TestCreate_secretKeyEmpty(t *testing.T) {
- c := memberlist.DefaultLANConfig()
- c.BindAddr = getBindAddr().String()
- c.SecretKey = make([]byte, 0)
-
- m, err := memberlist.Create(c)
- require.NoError(t, err)
- defer m.Shutdown()
-
- if m.Config().EncryptionEnabled() {
- t.Fatalf("Expected encryption to be disabled")
- }
-}
-
-func TestCreate_keyringOnly(t *testing.T) {
- c := memberlist.DefaultLANConfig()
- c.BindAddr = getBindAddr().String()
-
- keyring, err := memberlist.NewKeyring(nil, make([]byte, 16))
- require.NoError(t, err)
- c.Keyring = keyring
-
- m, err := memberlist.Create(c)
- require.NoError(t, err)
- defer m.Shutdown()
-}
-
-func TestCreate_keyringAndSecretKey(t *testing.T) {
- c := memberlist.DefaultLANConfig()
- c.BindAddr = getBindAddr().String()
-
- keyring, err := memberlist.NewKeyring(nil, make([]byte, 16))
- require.NoError(t, err)
- c.Keyring = keyring
- c.SecretKey = []byte{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}
-
- m, err := memberlist.Create(c)
- require.NoError(t, err)
- defer m.Shutdown()
-
- ringKeys := c.Keyring.GetKeys()
- if !bytes.Equal(c.SecretKey, ringKeys[0]) {
- t.Fatalf("Unexpected primary key %v", ringKeys[0])
- }
-}
-
-func TestCreate_invalidLoggerSettings(t *testing.T) {
- c := memberlist.DefaultLANConfig()
- c.BindAddr = getBindAddr().String()
- c.Logger = log.New(ioutil.Discard, "", log.LstdFlags)
- c.LogOutput = ioutil.Discard
-
- m, err := memberlist.Create(c)
- if err == nil {
- require.NoError(t, m.Shutdown())
- t.Fatal("Memberlist should not allow both LogOutput and Logger to be set, but it did not raise an error")
- }
-}
-
-func TestCreate(t *testing.T) {
- c := testConfig(t)
- c.ProtocolVersion = memberlist.ProtocolVersionMin
- c.DelegateProtocolVersion = 13
- c.DelegateProtocolMin = 12
- c.DelegateProtocolMax = 24
-
- m, err := memberlist.Create(c)
- require.NoError(t, err)
- defer m.Shutdown()
-
- yield()
-
- members := m.Members()
- if len(members) != 1 {
- t.Fatalf("bad number of members")
- }
-
- if members[0].PMin != memberlist.ProtocolVersionMin {
- t.Fatalf("bad: %#v", members[0])
- }
-
- if members[0].PMax != memberlist.ProtocolVersionMax {
- t.Fatalf("bad: %#v", members[0])
- }
-
- if members[0].PCur != c.ProtocolVersion {
- t.Fatalf("bad: %#v", members[0])
- }
-
- if members[0].DMin != c.DelegateProtocolMin {
- t.Fatalf("bad: %#v", members[0])
- }
-
- if members[0].DMax != c.DelegateProtocolMax {
- t.Fatalf("bad: %#v", members[0])
- }
-
- if members[0].DCur != c.DelegateProtocolVersion {
- t.Fatalf("bad: %#v", members[0])
- }
-}
-
-func GetMemberlist(tb testing.TB, f func(c *memberlist.Config)) *memberlist.Memberlist {
- c := testConfig(tb)
- c.BindPort = 0
- if f != nil {
- f(c)
- }
-
- m, err := memberlist.NewMemberlist(c)
- require.NoError(tb, err)
- return m
-}
-
-func TestMemberList_ResolveAddr(t *testing.T) {
- m := GetMemberlist(t, nil)
- defer m.Shutdown()
-
- defaultPort := uint16(m.Config().BindPort)
-
- type testCase struct {
- name string
- in string
- expectErr bool
- ignoreExpectIP bool
- expect []memberlist.IpPort
- }
-
- baseCases := []testCase{
- {
- name: "localhost",
- in: "localhost",
- ignoreExpectIP: true,
- expect: []memberlist.IpPort{
- memberlist.NewIpPort("", defaultPort, ""),
- },
- },
- {
- name: "ipv6 pair",
- in: "[::1]:80",
- expect: []memberlist.IpPort{
- memberlist.NewIpPort(net.IPv6loopback.String(), 80, ""),
- },
- },
- {
- name: "ipv6 non-pair",
- in: "[::1]",
- expect: []memberlist.IpPort{
- memberlist.NewIpPort(net.IPv6loopback.String(), defaultPort, ""),
- },
- },
- {
- name: "hostless port",
- in: ":80",
- expectErr: true,
- },
- {
- name: "hostname port combo",
- in: "localhost:80",
- ignoreExpectIP: true,
- expect: []memberlist.IpPort{
- memberlist.NewIpPort("", 80, ""),
- },
- },
- {
- name: "too high port",
- in: "localhost:80000",
- expectErr: true,
- },
- {
- name: "ipv4 port combo",
- in: "127.0.0.1:80",
- expect: []memberlist.IpPort{
- memberlist.NewIpPort(net.IPv4(127, 0, 0, 1).String(), 80, ""),
- },
- },
- {
- name: "ipv6 port combo",
- in: "[2001:db8:a0b:12f0::1]:80",
- expect: []memberlist.IpPort{
- memberlist.NewIpPort(net.IP{0x20, 0x01, 0x0d, 0xb8, 0x0a, 0x0b, 0x12, 0xf0, 0, 0, 0, 0, 0, 0, 0, 0x1}.String(), 80, ""),
- },
- },
- {
- name: "ipv4 port combo with empty tag",
- in: "/127.0.0.1:80",
- expectErr: true,
- },
- {
- name: "ipv4 only",
- in: "127.0.0.1",
- expect: []memberlist.IpPort{
- memberlist.NewIpPort(net.IPv4(127, 0, 0, 1).String(), defaultPort, ""),
- },
- },
- {
- name: "ipv6 only",
- in: "[2001:db8:a0b:12f0::1]",
- expect: []memberlist.IpPort{
- memberlist.NewIpPort(net.IP{0x20, 0x01, 0x0d, 0xb8, 0x0a, 0x0b, 0x12, 0xf0, 0, 0, 0, 0, 0, 0, 0, 0x1}.String(), defaultPort, ""),
- },
- },
- }
-
- // explode the cases to include tagged versions of everything
- var cases []testCase
- for _, tc := range baseCases {
- cases = append(cases, tc)
- if !strings.Contains(tc.in, "/") { // don't double tag already tagged cases
- tc2 := testCase{
- name: tc.name + " (tagged)",
- in: "foo.bar/" + tc.in,
- expectErr: tc.expectErr,
- ignoreExpectIP: tc.ignoreExpectIP,
- }
- for _, ipp := range tc.expect {
- tc2.expect = append(tc2.expect, memberlist.NewIpPort(ipp.Ip(), ipp.Port(), "foo.bar"))
- }
- cases = append(cases, tc2)
- }
- }
-
- for _, tc := range cases {
- tc := tc
- t.Run(tc.name, func(t *testing.T) {
- t.Parallel()
- got, err := m.ResolveAddr(tc.in)
- if tc.expectErr {
- require.Error(t, err)
- } else {
- require.NoError(t, err)
- if tc.ignoreExpectIP {
- if len(got) > 1 {
- got = got[0:1]
- }
- for i := 0; i < len(got); i++ {
- got[i].SetIp("")
- }
- }
- require.Equal(t, tc.expect, got)
- }
- })
- }
-}
-
-type dnsHandler struct {
- t *testing.T
-}
-
-func (h dnsHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
- if len(r.Question) != 1 {
- h.t.Fatalf("bad: %#v", r.Question)
- }
-
- name := "join.service.consul."
- question := r.Question[0]
- if question.Name != name || question.Qtype != dns.TypeANY {
- h.t.Fatalf("bad: %#v", question)
- }
-
- m := new(dns.Msg)
- m.SetReply(r)
- m.Authoritative = true
- m.RecursionAvailable = false
- m.Answer = append(m.Answer, &dns.A{
- Hdr: dns.RR_Header{
- Name: name,
- Rrtype: dns.TypeA,
- Class: dns.ClassINET},
- A: net.ParseIP("127.0.0.1"),
- })
- m.Answer = append(m.Answer, &dns.AAAA{
- Hdr: dns.RR_Header{
- Name: name,
- Rrtype: dns.TypeAAAA,
- Class: dns.ClassINET},
- AAAA: net.ParseIP("2001:db8:a0b:12f0::1"),
- })
- if err := w.WriteMsg(m); err != nil {
- h.t.Fatalf("err: %v", err)
- }
-}
-
-func TestMemberList_Members(t *testing.T) {
- n1 := &memberlist.Node{Name: "test"}
- n2 := &memberlist.Node{Name: "test2"}
- n3 := &memberlist.Node{Name: "test3"}
-
- m := &memberlist.Memberlist{}
- m.SetNodes(
- memberlist.NewNodeState(*n1, memberlist.StateAlive),
- memberlist.NewNodeState(*n2, memberlist.StateDead),
- memberlist.NewNodeState(*n3, memberlist.StateSuspect),
- )
-
- members := m.Members()
- if !reflect.DeepEqual(members, []*memberlist.Node{n1, n3}) {
- t.Fatalf("bad members")
- }
-}
-
-func TestMemberlist_Join(t *testing.T) {
- c1 := testConfig(t)
- c1.BindPort = 55111
- c1.AdvertisePort = 55111
- c1.Name = "m1"
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- // Create a second node
- c2 := testConfig(t)
- c2.BindPort = 55112
- c2.AdvertisePort = 55112
- c2.Name = "m2"
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m1.Config().Name, m1.AdvertiseAddr(), m1.AdvertisePort())
- num, err := m2.Join([]string{joinUrl})
- if num != 1 {
- t.Fatalf("unexpected 1: %d", num)
- }
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
-
- // Check the hosts
- if len(m2.Members()) != 2 {
- t.Fatalf("should have 2 nodes! %v", m2.Members())
- }
-}
-
-func TestMemberlist_JoinDifferentNetworksUniqueMask(t *testing.T) {
- c1 := testConfigNet(t, 0)
- c1.BindPort = 55113
- c1.AdvertisePort = 55113
- c1.Name = "m1"
- c1.CIDRsAllowed, _ = memberlist.ParseCIDRs([]string{"127.0.0.0/8"})
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- // Create a second node
- c2 := testConfigNet(t, 1)
- c2.CIDRsAllowed, _ = memberlist.ParseCIDRs([]string{"127.0.0.0/8"})
- c2.BindPort = 55114
- c2.AdvertisePort = 55114
- c2.Name = "m2"
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m1.Config().Name, m1.AdvertiseAddr(), m1.AdvertisePort())
- num, err := m2.Join([]string{joinUrl})
- if num != 1 {
- t.Fatalf("unexpected 1: %d", num)
- }
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
-
- // Check the hosts
- if len(m2.Members()) != 2 {
- t.Fatalf("should have 2 nodes! %v", m2.Members())
- }
-}
-
-func TestMemberlist_JoinDifferentNetworksMultiMasks(t *testing.T) {
- c1 := testConfigNet(t, 0)
- c1.BindPort = 55115
- c1.AdvertisePort = 55115
- c1.Name = "m1"
- c1.CIDRsAllowed, _ = memberlist.ParseCIDRs([]string{"127.0.0.0/24", "127.0.1.0/24"})
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- // Create a second node
- c2 := testConfigNet(t, 1)
- c2.CIDRsAllowed, _ = memberlist.ParseCIDRs([]string{"127.0.0.0/24", "127.0.1.0/24"})
- c2.BindPort = 55116
- c2.AdvertisePort = 55116
- c2.Name = "m2"
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m1.Config().Name, m1.AdvertiseAddr(), m1.AdvertisePort())
- err = joinAndTestMemberShip(t, m2, []string{joinUrl}, 2)
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
-}
-
-type CustomMergeDelegate struct {
- invoked bool
- t *testing.T
-}
-
-func (c *CustomMergeDelegate) NotifyMerge(nodes []*memberlist.Node) error {
- c.t.Logf("Cancel merge")
- c.invoked = true
- return fmt.Errorf("Custom merge canceled")
-}
-
-func TestMemberlist_Join_Cancel(t *testing.T) {
- c1 := testConfig(t)
- c1.BindPort = 55117
- c1.AdvertisePort = 55117
- c1.Name = "m1"
- merge1 := &CustomMergeDelegate{t: t}
- c1.Merge = merge1
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- // Create a second node
- c2 := testConfig(t)
- c2.BindPort = 55118
- c2.AdvertisePort = 55118
- c2.Name = "m2"
- merge2 := &CustomMergeDelegate{t: t}
- c2.Merge = merge2
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m1.Config().Name, m1.AdvertiseAddr(), m1.AdvertisePort())
- num, err := m2.Join([]string{joinUrl})
- if num != 0 {
- t.Fatalf("unexpected 0: %d", num)
- }
- if !strings.Contains(err.Error(), "Custom merge canceled") {
- t.Fatalf("unexpected err: %s", err)
- }
-
- // Check the hosts
- if len(m2.Members()) != 1 {
- t.Fatalf("should have 1 nodes! %v", m2.Members())
- }
- if len(m1.Members()) != 1 {
- t.Fatalf("should have 1 nodes! %v", m1.Members())
- }
-
- // Check delegate invocation
- if !merge1.invoked {
- t.Fatalf("should invoke delegate")
- }
- if !merge2.invoked {
- t.Fatalf("should invoke delegate")
- }
-}
-
-type CustomAliveDelegate struct {
- Ignore string
- count int
-
- t *testing.T
-}
-
-func (c *CustomAliveDelegate) NotifyAlive(peer *memberlist.Node) error {
- c.count++
- if peer.Name == c.Ignore {
- return nil
- }
- c.t.Logf("Cancel alive")
- return fmt.Errorf("Custom alive canceled")
-}
-
-func TestMemberlist_Join_Cancel_Passive(t *testing.T) {
- c1 := testConfig(t)
- c1.BindPort = 55119
- c1.AdvertisePort = 55119
- c1.Name = "m1"
- alive1 := &CustomAliveDelegate{
- Ignore: c1.Name,
- t: t,
- }
- c1.Alive = alive1
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- // Create a second node
- c2 := testConfig(t)
- c2.BindPort = 55120
- c2.AdvertisePort = 55120
- c2.Name = "m2"
- alive2 := &CustomAliveDelegate{
- Ignore: c2.Name,
- t: t,
- }
- c2.Alive = alive2
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m1.Config().Name, m1.AdvertiseAddr(), m1.AdvertisePort())
- num, err := m2.Join([]string{joinUrl})
- if num != 1 {
- t.Fatalf("unexpected 1: %d", num)
- }
- if err != nil {
- t.Fatalf("err: %s", err)
- }
-
- // Check the hosts
- if len(m2.Members()) != 1 {
- t.Fatalf("should have 1 nodes! %v", m2.Members())
- }
- if len(m1.Members()) != 1 {
- t.Fatalf("should have 1 nodes! %v", m1.Members())
- }
-
- // Check delegate invocation
- if alive1.count == 0 {
- t.Fatalf("should invoke delegate: %d", alive1.count)
- }
- if alive2.count == 0 {
- t.Fatalf("should invoke delegate: %d", alive2.count)
- }
-}
-
-func TestMemberlist_Join_protocolVersions(t *testing.T) {
- c1 := testConfig(t)
- c1.BindPort = 55121
- c1.AdvertisePort = 55121
- c1.Name = "m1"
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- c2 := testConfig(t)
- c2.BindPort = 55110
- c2.AdvertisePort = 55110
- c2.Name = "m2"
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- c3 := testConfig(t)
- c3.BindPort = 55109
- c3.AdvertisePort = 55109
- c3.Name = "m3"
- c3.ProtocolVersion = memberlist.ProtocolVersionMax
-
- m3, err := memberlist.Create(c3)
- require.NoError(t, err)
- defer m3.Shutdown()
-
- _, err = m1.Join([]string{c2.Name + "/" + c2.BindAddr})
- require.NoError(t, err)
-
- yield()
-
- _, err = m1.Join([]string{c3.Name + "/" + c3.BindAddr})
- require.NoError(t, err)
-}
-
-func joinAndTestMemberShip(t *testing.T, self *memberlist.Memberlist, membersToJoin []string, expectedMembers int) error {
- t.Helper()
- num, err := self.Join(membersToJoin)
- if err != nil {
- return err
- }
- if num != len(membersToJoin) {
- t.Fatalf("unexpected %d, was expecting %d to be joined", num, len(membersToJoin))
- }
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
- // Check the hosts
- if len(self.Members()) != expectedMembers {
- t.Fatalf("should have 2 nodes! %v", self.Members())
- }
- if len(self.Members()) != expectedMembers {
- t.Fatalf("should have 2 nodes! %v", self.Members())
- }
- return nil
-}
-
-func TestMemberlist_Leave(t *testing.T) {
- newConfig := func() *memberlist.Config {
- c := testConfig(t)
- c.GossipInterval = time.Millisecond
- return c
- }
-
- c1 := newConfig()
- c1.BindPort = 55108
- c1.AdvertisePort = 55108
- c1.Name = "m1"
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- // Create a second node
- c2 := newConfig()
- c2.BindPort = 55107
- c2.AdvertisePort = 55107
- c2.Name = "m2"
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m1.Config().Name, m1.AdvertiseAddr(), m1.AdvertisePort())
- err = joinAndTestMemberShip(t, m2, []string{joinUrl}, 2)
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
-
- // Leave
- err = m1.Leave(time.Second)
- require.NoError(t, err)
-
- // Wait for leave
- time.Sleep(10 * time.Millisecond)
-
- // m1 should think dead
- if len(m1.Members()) != 1 {
- t.Fatalf("should have 1 node")
- }
-
- if len(m2.Members()) != 1 {
- t.Fatalf("should have 1 node")
- }
-
- if m2.NodeMap()[c1.Name].State != memberlist.StateLeft {
- t.Fatalf("bad state")
- }
-}
-
-func TestMemberlist_JoinShutdown(t *testing.T) {
- newConfig := func() *memberlist.Config {
- c := testConfig(t)
- c.ProbeInterval = time.Millisecond
- c.ProbeTimeout = 100 * time.Microsecond
- c.SuspicionMaxTimeoutMult = 1
- return c
- }
-
- c1 := newConfig()
- c1.BindPort = 55106
- c1.AdvertisePort = 55106
- c1.Name = "m1"
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- // Create a second node
- c2 := newConfig()
- c2.BindPort = 55105
- c2.AdvertisePort = 55105
- c2.Name = "m2"
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m1.Config().Name, m1.AdvertiseAddr(), m1.AdvertisePort())
- num, err := m2.Join([]string{joinUrl})
- if num != 1 {
- t.Fatalf("unexpected 1: %d", num)
- }
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
-
- // Check the hosts
- if len(m2.Members()) != 2 {
- t.Fatalf("should have 2 nodes! %v", m2.Members())
- }
-
- require.NoError(t, m1.Shutdown())
-
- waitForCondition(t, func() (bool, string) {
- n := len(m2.Members())
- return n == 1, fmt.Sprintf("expected 1 node, got %d", n)
- })
-}
-
-func TestMemberlist_delegateMeta(t *testing.T) {
- c1 := testConfig(t)
- c1.Delegate = &MockDelegate{meta: []byte("web")}
- c1.BindPort = 55104
- c1.AdvertisePort = 55104
- c1.Name = "c1"
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- c2 := testConfig(t)
- c2.BindPort = 55103
- c2.AdvertisePort = 55103
- c2.Name = "c2"
- c2.Delegate = &MockDelegate{meta: []byte("lb")}
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m2.Config().Name, m2.AdvertiseAddr(), m2.AdvertisePort())
- _, err = m1.Join([]string{joinUrl})
- require.NoError(t, err)
-
- yield()
-
- var roles map[string]string
-
- // Check the roles of members of m1
- m1m := m1.Members()
- if len(m1m) != 2 {
- t.Fatalf("bad: %#v", m1m)
- }
-
- roles = make(map[string]string)
- for _, m := range m1m {
- roles[m.Name] = string(m.Meta)
- }
-
- if r := roles[c1.Name]; r != "web" {
- t.Fatalf("bad role for %s: %s", c1.Name, r)
- }
-
- if r := roles[c2.Name]; r != "lb" {
- t.Fatalf("bad role for %s: %s", c2.Name, r)
- }
-
- // Check the roles of members of m2
- m2m := m2.Members()
- if len(m2m) != 2 {
- t.Fatalf("bad: %#v", m2m)
- }
-
- roles = make(map[string]string)
- for _, m := range m2m {
- roles[m.Name] = string(m.Meta)
- }
-
- if r := roles[c1.Name]; r != "web" {
- t.Fatalf("bad role for %s: %s", c1.Name, r)
- }
-
- if r := roles[c2.Name]; r != "lb" {
- t.Fatalf("bad role for %s: %s", c2.Name, r)
- }
-}
-
-func TestMemberlist_delegateMeta_Update(t *testing.T) {
- c1 := testConfig(t)
- c1.BindPort = 55122
- c1.AdvertisePort = 55122
- c1.Name = "m1"
- mock1 := &MockDelegate{meta: []byte("web")}
- c1.Delegate = mock1
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- c2 := testConfig(t)
- c2.BindPort = 55123
- c2.AdvertisePort = 55123
- c2.Name = "m2"
- mock2 := &MockDelegate{meta: []byte("lb")}
- c2.Delegate = mock2
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m2.Config().Name, m2.AdvertiseAddr(), m2.AdvertisePort())
- _, err = m1.Join([]string{joinUrl})
-
- require.NoError(t, err)
-
- yield()
-
- // Update the meta data roles
- mock1.setMeta([]byte("api"))
- mock2.setMeta([]byte("db"))
-
- err = m1.UpdateNode(0)
- require.NoError(t, err)
- err = m2.UpdateNode(0)
- require.NoError(t, err)
-
- yield()
-
- // Check the updates have propagated
- var roles map[string]string
-
- // Check the roles of members of m1
- m1m := m1.Members()
- if len(m1m) != 2 {
- t.Fatalf("bad: %#v", m1m)
- }
-
- roles = make(map[string]string)
- for _, m := range m1m {
- roles[m.Name] = string(m.Meta)
- }
-
- if r := roles[c1.Name]; r != "api" {
- t.Fatalf("bad role for %s: %s", c1.Name, r)
- }
-
- if r := roles[c2.Name]; r != "db" {
- t.Fatalf("bad role for %s: %s", c2.Name, r)
- }
-
- // Check the roles of members of m2
- m2m := m2.Members()
- if len(m2m) != 2 {
- t.Fatalf("bad: %#v", m2m)
- }
-
- roles = make(map[string]string)
- for _, m := range m2m {
- roles[m.Name] = string(m.Meta)
- }
-
- if r := roles[c1.Name]; r != "api" {
- t.Fatalf("bad role for %s: %s", c1.Name, r)
- }
-
- if r := roles[c2.Name]; r != "db" {
- t.Fatalf("bad role for %s: %s", c2.Name, r)
- }
-}
-
-func TestMemberlist_UserData(t *testing.T) {
- newConfig := func() (*memberlist.Config, *MockDelegate) {
- d := &MockDelegate{}
- c := testConfig(t)
- // Set the gossip/pushpull intervals fast enough to get a reasonable test,
- // but slow enough to avoid "sendto: operation not permitted"
- c.GossipInterval = 100 * time.Millisecond
- c.PushPullInterval = 100 * time.Millisecond
- c.Delegate = d
- return c, d
- }
-
- c1, d1 := newConfig()
- c1.BindPort = 55133
- c1.AdvertisePort = 55133
- c1.Name = "m1"
- d1.setState([]byte("something"))
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- bcasts := [][]byte{
- []byte("test"),
- []byte("foobar"),
- }
-
- // Create a second node
- c2, d2 := newConfig()
- c2.BindPort = 55134
- c2.AdvertisePort = 55134
- c2.Name = "m2"
-
- // Second delegate has things to send
- d2.setBroadcasts(bcasts)
- d2.setState([]byte("my state"))
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m1.Config().Name, m1.AdvertiseAddr(), m1.AdvertisePort())
- num, err := m2.Join([]string{joinUrl})
- if num != 1 {
- t.Fatalf("unexpected 1: %d", num)
- }
- require.NoError(t, err)
-
- // Check the hosts
- if m2.NumMembers() != 2 {
- t.Fatalf("should have 2 nodes! %v", m2.Members())
- }
-}
-
-func TestMemberlist_SendTo(t *testing.T) {
- newConfig := func() (*memberlist.Config, *MockDelegate, net.IP) {
- d := &MockDelegate{}
- c := testConfig(t)
- c.GossipInterval = time.Millisecond
- c.PushPullInterval = time.Millisecond
- c.Delegate = d
- return c, d, net.ParseIP(c.BindAddr)
- }
-
- c1, d1, _ := newConfig()
- c1.BindPort = 55144
- c1.AdvertisePort = 55144
- c1.Name = "m1"
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- c2, d2, addr2 := newConfig()
- c2.BindPort = 55145
- c2.AdvertisePort = 55145
- c2.Name = "m2"
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m1.Config().Name, m1.AdvertiseAddr(), m1.AdvertisePort())
- num, err := m2.Join([]string{joinUrl})
- require.NoError(t, err)
- require.Equal(t, 1, num)
-
- // Check the hosts
- require.Equal(t, 2, m2.NumMembers(), "should have 2 nodes! %v", m2.Members())
-
- // Try to do a direct send
- m2Addr := &net.UDPAddr{
- IP: addr2,
- Port: int(m2.AdvertisePort()),
- }
- m2Address := memberlist.Address{
- Addr: m2Addr.String(),
- Name: m2.Config().Name,
- }
- if err := m1.SendToAddress(m2Address, []byte("ping")); err != nil {
- t.Fatalf("err: %v", err)
- }
-
- m1Addr := &net.UDPAddr{
- IP: net.ParseIP(m1.Config().BindAddr),
- Port: int(m1.AdvertisePort()),
- }
- m1Address := memberlist.Address{
- Addr: m1Addr.String(),
- Name: m1.Config().Name,
- }
- if err := m2.SendToAddress(m1Address, []byte("pong")); err != nil {
- t.Fatalf("err: %v", err)
- }
-
- waitForCondition(t, func() (bool, string) {
- msgs := d1.getMessages()
- return len(msgs) == 1, fmt.Sprintf("expected 1 message, got %d", len(msgs))
- })
-
- msgs1 := d1.getMessages()
- if !reflect.DeepEqual(msgs1[0], []byte("pong")) {
- t.Fatalf("bad msg %v", msgs1[0])
- }
-
- waitForCondition(t, func() (bool, string) {
- msgs := d2.getMessages()
- return len(msgs) == 1, fmt.Sprintf("expected 1 message, got %d", len(msgs))
- })
- msgs2 := d2.getMessages()
- if !reflect.DeepEqual(msgs2[0], []byte("ping")) {
- t.Fatalf("bad msg %v", msgs2[0])
- }
-}
-
-func waitForCondition(t *testing.T, fn func() (bool, string)) {
- start := time.Now()
-
- var msg string
- for time.Since(start) < 20*time.Second {
- var done bool
- done, msg = fn()
- if done {
- return
- }
- time.Sleep(5 * time.Millisecond)
- }
- t.Fatalf("timeout waiting for condition: %v", msg)
-}
-
-func TestMemberlistProtocolVersion(t *testing.T) {
- c := testConfig(t)
- c.ProtocolVersion = memberlist.ProtocolVersionMax
-
- m, err := memberlist.Create(c)
- require.NoError(t, err)
- defer m.Shutdown()
-
- result := m.ProtocolVersion()
- if result != memberlist.ProtocolVersionMax {
- t.Fatalf("bad: %d", result)
- }
-}
-
-func TestMemberlist_Join_DeadNode(t *testing.T) {
- c1 := testConfig(t)
- c1.TCPTimeout = 50 * time.Millisecond
- c1.BindPort = 55155
- c1.AdvertisePort = 55155
- c1.Name = "m1"
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- // Create a second "node", which is just a TCP listener that
- // does not ever respond. This is to test our deadlines
- addr2 := getBindAddr()
- list, err := net.Listen("tcp", net.JoinHostPort(addr2.String(), strconv.Itoa(56156)))
- if err != nil {
- t.Fatalf("err: %v", err)
- }
- defer list.Close()
-
- // Ensure we don't hang forever
- timer := time.AfterFunc(100*time.Millisecond, func() {
- panic("should have timed out by now")
- })
- defer timer.Stop()
-
- num, err := m1.Join([]string{fmt.Sprintf("%s/%s:%d", "fake", addr2.String(), 56156)})
- if num != 0 {
- t.Fatalf("unexpected 0: %d", num)
- }
- if err == nil {
- t.Fatal("expect err")
- }
-}
-
-// Tests that nodes running different versions of the protocol can successfully
-// discover each other and add themselves to their respective member lists.
-func TestMemberlist_Join_Protocol_Compatibility(t *testing.T) {
- testProtocolVersionPair := func(t *testing.T, pv1 uint8, pv2 uint8) {
- t.Helper()
-
- c1 := testConfig(t)
- c1.ProtocolVersion = pv1
- c1.BindPort = 55166
- c1.AdvertisePort = 55166
- c1.Name = "m1"
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- c2 := testConfig(t)
- c2.BindPort = 55167
- c2.AdvertisePort = 55167
- c2.ProtocolVersion = pv2
- c1.Name = "m2"
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m1.Config().Name, m1.AdvertiseAddr(), m1.AdvertisePort())
- _, err = m2.Join([]string{joinUrl})
-
- require.NoError(t, err)
- //require.Equal(t, 1, num)
-
- // Check the hosts
- //if len(m2.Members()) != 2 {
- // t.Fatalf("should have 2 nodes! %v", m2.Members())
- //}
- //
- // Check the hosts
- //if len(m1.Members()) != 2 {
- // t.Fatalf("should have 2 nodes! %v", m1.Members())
- //}
- }
-
- t.Run("2,1", func(t *testing.T) {
- testProtocolVersionPair(t, 2, 1)
- })
- t.Run("2,3", func(t *testing.T) {
- testProtocolVersionPair(t, 2, 3)
- })
- t.Run("3,2", func(t *testing.T) {
- testProtocolVersionPair(t, 3, 2)
- })
- t.Run("3,1", func(t *testing.T) {
- testProtocolVersionPair(t, 3, 1)
- })
-}
-
-var (
- ipv6LoopbackAvailableOnce sync.Once
- ipv6LoopbackAvailable bool
-)
-
-func isIPv6LoopbackAvailable(t *testing.T) bool {
- const ipv6LoopbackAddress = "::1"
- ipv6LoopbackAvailableOnce.Do(func() {
- ifaces, err := net.Interfaces()
- require.NoError(t, err)
-
- for _, iface := range ifaces {
- if iface.Flags&net.FlagLoopback == 0 {
- continue
- }
- addrs, err := iface.Addrs()
- require.NoError(t, err)
-
- for _, addr := range addrs {
- ipaddr := addr.(*net.IPNet)
- if ipaddr.IP.String() == ipv6LoopbackAddress {
- ipv6LoopbackAvailable = true
- return
- }
- }
- }
- ipv6LoopbackAvailable = false
- t.Logf("IPv6 loopback address %q not found, disabling tests that require it", ipv6LoopbackAddress)
- })
-
- return ipv6LoopbackAvailable
-}
-
-func TestMemberlist_Join_IPv6(t *testing.T) {
- if !isIPv6LoopbackAvailable(t) {
- t.SkipNow()
- return
- }
- // Since this binds to all interfaces we need to exclude other tests
- // from grabbing an interface.
- bindLock.Lock()
- defer bindLock.Unlock()
-
- c1 := memberlist.DefaultLANConfig()
- c1.Name = "A"
- c1.BindAddr = "[::1]"
- c1.BindPort = 0 // choose free
- c1.Logger = log.New(os.Stderr, c1.Name, log.LstdFlags)
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- // Create a second node
- c2 := memberlist.DefaultLANConfig()
- c2.Name = "B"
- c2.BindAddr = "[::1]"
- c2.BindPort = 0 // choose free
- c2.Logger = log.New(os.Stderr, c2.Name, log.LstdFlags)
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- num, err := m2.Join([]string{fmt.Sprintf("%s/%s:%d", m1.Config().Name, m1.Config().BindAddr, m1.Config().BindPort)})
- require.NoError(t, err)
- require.Equal(t, 1, num)
-
- // Check the hosts
- if len(m2.Members()) != 2 {
- t.Fatalf("should have 2 nodes! %v", m2.Members())
- }
-
- if len(m1.Members()) != 2 {
- t.Fatalf("should have 2 nodes! %v", m2.Members())
- }
-}
-
-func reservePort(t *testing.T, ip net.IP, purpose string) int {
- for i := 0; i < 10; i++ {
- tcpAddr := &net.TCPAddr{IP: ip, Port: 0}
- tcpLn, err := net.ListenTCP("tcp", tcpAddr)
- if err != nil {
- if strings.Contains(err.Error(), "address already in use") {
- continue
- }
- t.Fatalf("unexpected error: %v", err)
- }
-
- port := tcpLn.Addr().(*net.TCPAddr).Port
-
- udpAddr := &net.UDPAddr{IP: ip, Port: port}
- udpLn, err := net.ListenUDP("udp", udpAddr)
- if err != nil {
- _ = tcpLn.Close()
- if strings.Contains(err.Error(), "address already in use") {
- continue
- }
- t.Fatalf("unexpected error: %v", err)
- }
-
- t.Logf("Using dynamic bind port %d for %s", port, purpose)
- _ = tcpLn.Close()
- _ = udpLn.Close()
- return port
- }
-
- t.Fatalf("could not find a free TCP+UDP port to listen on for %s", purpose)
- panic("IMPOSSIBLE")
-}
-
-func TestAdvertiseAddr(t *testing.T) {
- bindAddr := getBindAddr()
- advertiseAddr := getBindAddr()
-
- bindPort := reservePort(t, bindAddr, "BIND")
- advertisePort := reservePort(t, advertiseAddr, "ADVERTISE")
-
- c := memberlist.DefaultLANConfig()
- c.BindAddr = bindAddr.String()
- c.BindPort = bindPort
- c.Name = c.BindAddr
-
- c.AdvertiseAddr = advertiseAddr.String()
- c.AdvertisePort = advertisePort
-
- m, err := memberlist.Create(c)
- require.NoError(t, err)
- defer m.Shutdown()
-
- yield()
-
- members := m.Members()
- require.Equal(t, 1, len(members))
-
- require.Equal(t, advertiseAddr.String(), members[0].Addr)
- require.Equal(t, advertisePort, int(members[0].Port))
-}
-
-type MockConflict struct {
- existing *memberlist.Node
- other *memberlist.Node
-}
-
-func (m *MockConflict) NotifyConflict(existing, other *memberlist.Node) {
- m.existing = existing
- m.other = other
-}
-
-func TestMemberlist_conflictDelegate(t *testing.T) {
- c1 := testConfig(t)
- c1.BindPort = 55177
- c1.AdvertisePort = 55177
- mock := &MockConflict{}
- c1.Conflict = mock
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- // Ensure name conflict
- c2 := testConfig(t)
- c2.Name = c1.Name
- c2.BindPort = 55178
- c2.AdvertisePort = 55178
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m2.Config().Name, m2.AdvertiseAddr(), m2.AdvertisePort())
- num, err := m1.Join([]string{joinUrl})
- require.NoError(t, err)
- require.Equal(t, 1, num)
-
- yield()
-
- // Ensure we were notified
- if mock.existing == nil || mock.other == nil {
- t.Fatalf("should get notified mock.existing=%v VS mock.other=%v", mock.existing, mock.other)
- }
- if mock.existing.Name != mock.other.Name {
- t.Fatalf("bad: %v %v", mock.existing, mock.other)
- }
-}
-
-type MockPing struct {
- mu sync.Mutex
- other *memberlist.Node
- rtt time.Duration
- payload []byte
-}
-
-func (m *MockPing) NotifyPingComplete(other *memberlist.Node, rtt time.Duration, payload []byte) {
- m.mu.Lock()
- defer m.mu.Unlock()
-
- m.other = other
- m.rtt = rtt
- m.payload = payload
-}
-
-func (m *MockPing) getContents() (*memberlist.Node, time.Duration, []byte) {
- m.mu.Lock()
- defer m.mu.Unlock()
- return m.other, m.rtt, m.payload
-}
-
-const DEFAULT_PAYLOAD = "whatever"
-
-func (m *MockPing) AckPayload() []byte {
- return []byte(DEFAULT_PAYLOAD)
-}
-
-func TestMemberlist_PingDelegate(t *testing.T) {
- newConfig := func() *memberlist.Config {
- c := testConfig(t)
- c.ProbeInterval = 100 * time.Millisecond
- c.Ping = &MockPing{}
- return c
- }
-
- c1 := newConfig()
- c1.BindPort = 55199
- c1.AdvertisePort = 55199
- c1.Name = "m1"
-
- m1, err := memberlist.Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- // Create a second node
- c2 := newConfig()
- c2.BindPort = 55200
- c2.AdvertisePort = 55200
- c2.Name = "m2"
- mock := c2.Ping.(*MockPing)
-
- m2, err := memberlist.Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- joinUrl := fmt.Sprintf("%s/%s:%d", m1.Config().Name, m1.AdvertiseAddr(), m1.AdvertisePort())
- num, err := m2.Join([]string{joinUrl})
- require.NoError(t, err)
- require.Equal(t, 1, num)
-
- waitUntilSize(t, m1, 2)
- waitUntilSize(t, m2, 2)
-
- time.Sleep(2 * c1.ProbeInterval)
-
- require.NoError(t, m1.Shutdown())
- require.NoError(t, m2.Shutdown())
-
- mOther, mRTT, _ := mock.getContents()
-
- // Ensure we were notified
- if mOther == nil {
- t.Fatalf("should get notified")
- }
-
- //if !reflect.DeepEqual(mOther, m1.LocalNode()) {
- // t.Fatalf("not notified about the correct node; expected: %+v; actual: %+v",
- // m2.LocalNode(), mOther)
- //}
-
- if mRTT <= 0 {
- t.Fatalf("rtt should be greater than 0")
- }
-
- //if bytes.Compare(mPayload, []byte(DEFAULT_PAYLOAD)) != 0 {
- // t.Fatalf("incorrect payload. expected: %v; actual: %v",
- // []byte(DEFAULT_PAYLOAD), mPayload)
- //}
-}
-
-func waitUntilSize(t *testing.T, m *memberlist.Memberlist, expected int) {
- t.Helper()
- retry(t, 15, 500*time.Millisecond, func(failf func(string, ...interface{})) {
- t.Helper()
-
- if m.NumMembers() != expected {
- failf("%s expected to have %d members but had: %v", m.Config().Name, expected, m.Members())
- }
- })
-}
-
-func isPortFree(t *testing.T, addr string, port int) error {
- t.Helper()
-
- ip := net.ParseIP(addr)
- tcpAddr := &net.TCPAddr{IP: ip, Port: port}
- tcpLn, err := net.ListenTCP("tcp", tcpAddr)
- if err != nil {
- return err
- }
- if err := tcpLn.Close(); err != nil {
- return err
- }
-
- udpAddr := &net.UDPAddr{IP: ip, Port: port}
- udpLn, err := net.ListenUDP("udp", udpAddr)
- if err != nil {
- return err
- }
-
- return udpLn.Close()
-}
-
-func waitUntilPortIsFree(t *testing.T, m *memberlist.Memberlist) {
- t.Helper()
-
- // wait until we know for certain that m1 is dead dead
- addr := m.Config().BindAddr
- port := m.Config().BindPort
-
- retry(t, 15, 250*time.Millisecond, func(failf func(string, ...interface{})) {
- t.Helper()
-
- if err := isPortFree(t, addr, port); err != nil {
- failf("%s port is not yet free", m.Config().Name)
- }
- })
-}
-
-// This test should follow the recommended upgrade guide:
-// https://www.consul.io/docs/agent/encryption.html#configuring-gossip-encryption-on-an-existing-cluster
-//
-// We will use two nodes for this: m0 and m1
-//
-// 0. Start with nodes without encryption.
-// 1. Set an encryption key and set GossipVerifyIncoming=false and GossipVerifyOutgoing=false to all nodes.
-// 2. Change GossipVerifyOutgoing=true to all nodes.
-// 3. Change GossipVerifyIncoming=true to all nodes.
-//func TestMemberlist_EncryptedGossipTransition(t *testing.T) {
-// // ensure these all get the same general set of customizations
-// pretty := make(map[string]string) // addr->shortName
-// newConfig := func(shortName string, addr string) *memberlist.Config {
-// t.Helper()
-//
-// conf := memberlist.DefaultLANConfig()
-// if addr == "" {
-// addr = getBindAddr().String()
-// }
-// conf.Name = addr
-// // conf.Name = shortName
-// conf.BindAddr = addr
-// conf.BindPort = 0
-// // Set the gossip interval fast enough to get a reasonable test,
-// // but slow enough to avoid "sendto: operation not permitted"
-// conf.GossipInterval = 100 * time.Millisecond
-// conf.Logger = log.New(os.Stderr, shortName, log.LstdFlags)
-//
-// pretty[conf.Name] = shortName
-// return conf
-// }
-//
-// var bindPort int
-// createOK := func(conf *memberlist.Config) *Memberlist {
-// t.Helper()
-//
-// if bindPort > 0 {
-// conf.BindPort = bindPort
-// } else {
-// // try a range of port numbers until something sticks
-// }
-// m, err := memberlist.Create(conf)
-// require.NoError(t, err)
-//
-// if bindPort == 0 {
-// bindPort = m.config.BindPort
-// }
-// return m
-// }
-//
-// joinOK := func(src, dst *Memberlist, numNodes int) {
-// t.Helper()
-//
-// srcName, dstName := pretty[src.Config().Name], pretty[dst.Config().Name]
-// t.Logf("Node %s[%s] joining node %s[%s]", srcName, src.Config().Name, dstName, dst.Config().Name)
-//
-// num, err := src.Join([]string{dst.Config().Name + "/" + dst.config.BindAddr})
-// require.NoError(t, err)
-// require.Equal(t, 1, num)
-//
-// waitUntilSize(t, src, numNodes)
-// waitUntilSize(t, dst, numNodes)
-//
-// // Check the hosts
-// require.Equal(t, numNodes, len(src.Members()), "nodes: %v", src.Members())
-// require.Equal(t, numNodes, src.estNumNodes(), "nodes: %v", src.Members())
-// require.Equal(t, numNodes, len(dst.Members()), "nodes: %v", dst.Members())
-// require.Equal(t, numNodes, dst.estNumNodes(), "nodes: %v", dst.Members())
-// }
-//
-// leaveOK := func(m *Memberlist, why string) {
-// t.Helper()
-//
-// name := pretty[m.Config().Name]
-// t.Logf("Node %s[%s] is leaving %s", name, m.Config().Name, why)
-// err := m.Leave(time.Second)
-// require.NoError(t, err)
-// }
-//
-// shutdownOK := func(m *Memberlist, why string) {
-// t.Helper()
-//
-// name := pretty[m.Config().Name]
-// t.Logf("Node %s[%s] is shutting down %s", name, m.Config().Name, why)
-// err := m.Shutdown()
-// require.NoError(t, err)
-//
-// // Double check that it genuinely shutdown.
-// waitUntilPortIsFree(t, m)
-// }
-//
-// leaveAndShutdown := func(leaver, bystander *Memberlist, why string) {
-// t.Helper()
-//
-// leaveOK(leaver, why)
-// waitUntilSize(t, bystander, 1)
-// shutdownOK(leaver, why)
-// waitUntilSize(t, bystander, 1)
-// }
-//
-// // ==== STEP 0 ====
-//
-// // Create a first cluster of 2 nodes with no gossip encryption settings.
-// conf0 := newConfig("m0", "")
-// m0 := memberlist.CreateOK(conf0)
-// defer m0.Shutdown()
-//
-// conf1 := newConfig("m1", "")
-// m1 := memberlist.CreateOK(conf1)
-// defer m1.Shutdown()
-//
-// joinOK(m1, m0, 2)
-//
-// t.Logf("==== STEP 0 complete: two node unencrypted cluster ====")
-//
-// // ==== STEP 1 ====
-//
-// // Take down m0, upgrade to first stage of gossip transition settings.
-// leaveAndShutdown(m0, m1, "to upgrade gossip to first stage")
-//
-// // Resurrect the first node with the first stage of gossip transition settings.
-// conf0 = newConfig("m0", m0.config.BindAddr)
-// conf0.SecretKey = []byte("Hi16ZXu2lNCRVwtr20khAg==")
-// conf0.GossipVerifyIncoming = false
-// conf0.GossipVerifyOutgoing = false
-// m0 = createOK(conf0)
-// defer m0.Shutdown()
-//
-// // Join the second node. m1 has no encryption while m0 has encryption configured and
-// // can receive encrypted gossip, but will not encrypt outgoing gossip.
-// joinOK(m0, m1, 2)
-//
-// leaveAndShutdown(m1, m0, "to upgrade gossip to first stage")
-//
-// // Resurrect the second node with the first stage of gossip transition settings.
-// conf1 = newConfig("m1", m1.config.BindAddr)
-// conf1.SecretKey = []byte("Hi16ZXu2lNCRVwtr20khAg==")
-// conf1.GossipVerifyIncoming = false
-// conf1.GossipVerifyOutgoing = false
-// m1 = createOK(conf1)
-// defer m1.Shutdown()
-//
-// // Join the first node. Both have encryption configured and can receive
-// // encrypted gossip, but will not encrypt outgoing gossip.
-// joinOK(m1, m0, 2)
-//
-// t.Logf("==== STEP 1 complete: two node encryption-aware cluster ====")
-//
-// // ==== STEP 2 ====
-//
-// // Take down m0, upgrade to second stage of gossip transition settings.
-// leaveAndShutdown(m0, m1, "to upgrade gossip to second stage")
-//
-// // Resurrect the first node with the second stage of gossip transition settings.
-// conf0 = newConfig("m0", m0.config.BindAddr)
-// conf0.SecretKey = []byte("Hi16ZXu2lNCRVwtr20khAg==")
-// conf0.GossipVerifyIncoming = false
-// m0 = createOK(conf0)
-// defer m0.Shutdown()
-//
-// // Join the second node. At this step, both nodes have encryption
-// // configured but only m0 is sending encrypted gossip.
-// joinOK(m0, m1, 2)
-//
-// leaveAndShutdown(m1, m0, "to upgrade gossip to second stage")
-//
-// // Resurrect the second node with the second stage of gossip transition settings.
-// conf1 = newConfig("m1", m1.config.BindAddr)
-// conf1.SecretKey = []byte("Hi16ZXu2lNCRVwtr20khAg==")
-// conf1.GossipVerifyIncoming = false
-// m1 = createOK(conf1)
-// defer m1.Shutdown()
-//
-// // Join the first node. Both have encryption configured and can receive
-// // encrypted gossip, and encrypt outgoing gossip, but aren't forcing
-// // incoming gossip is encrypted.
-// joinOK(m1, m0, 2)
-//
-// t.Logf("==== STEP 2 complete: two node encryption-aware cluster being encrypted ====")
-//
-// // ==== STEP 3 ====
-//
-// // Take down m0, upgrade to final stage of gossip transition settings.
-// leaveAndShutdown(m0, m1, "to upgrade gossip to final stage")
-//
-// // Resurrect the first node with the final stage of gossip transition settings.
-// conf0 = newConfig("m0", m0.config.BindAddr)
-// conf0.SecretKey = []byte("Hi16ZXu2lNCRVwtr20khAg==")
-// m0 = createOK(conf0)
-// defer m0.Shutdown()
-//
-// // Join the second node. At this step, both nodes have encryption
-// // configured and are sending it, bu tonly m0 is verifying inbound gossip
-// // is encrypted.
-// joinOK(m0, m1, 2)
-//
-// leaveAndShutdown(m1, m0, "to upgrade gossip to final stage")
-//
-// // Resurrect the second node with the final stage of gossip transition settings.
-// conf1 = newConfig("m1", m1.config.BindAddr)
-// conf1.SecretKey = []byte("Hi16ZXu2lNCRVwtr20khAg==")
-// m1 = createOK(conf1)
-// defer m1.Shutdown()
-//
-// // Join the first node. Both have encryption configured and fully in
-// // enforcement.
-// joinOK(m1, m0, 2)
-//
-// t.Logf("==== STEP 3 complete: two node encrypted cluster locked down ====")
-//}
-
-// Consul bug, rapid restart (before failure detection),
-// with an updated meta data. Should be at incarnation 1 for
-// both.
-//
-// This test is uncommented because it requires that either we
-// can rebind the socket (SO_REUSEPORT) which Go does not allow,
-// OR we must disable the address conflict checking in memberlist.
-// I just comment out that code to test this case.
-//
-//func TestMemberlist_Restart_delegateMeta_Update(t *testing.T) {
-// c1 := testConfig()
-// c2 := testConfig()
-// mock1 := &MockDelegate{meta: []byte("web")}
-// mock2 := &MockDelegate{meta: []byte("lb")}
-// c1.Delegate = mock1
-// c2.Delegate = mock2
-
-// m1, err := memberlist.Create(c1)
-// if err != nil {
-// t.Fatalf("err: %s", err)
-// }
-// defer m1.Shutdown()
-
-// m2, err := memberlist.Create(c2)
-// if err != nil {
-// t.Fatalf("err: %s", err)
-// }
-// defer m2.Shutdown()
-
-// _, err = m1.Join([]string{c2.BindAddr})
-// if err != nil {
-// t.Fatalf("err: %s", err)
-// }
-
-// yield()
-
-// // Recreate m1 with updated meta
-// m1.Shutdown()
-// c3 := testConfig()
-// c3.Name = c1.Name
-// c3.Delegate = mock1
-// c3.GossipInterval = time.Millisecond
-// mock1.meta = []byte("api")
-
-// m1, err = Create(c3)
-// if err != nil {
-// t.Fatalf("err: %s", err)
-// }
-// defer m1.Shutdown()
-
-// _, err = m1.Join([]string{c2.BindAddr})
-// if err != nil {
-// t.Fatalf("err: %s", err)
-// }
-
-// yield()
-// yield()
-
-// // Check the updates have propagated
-// var roles map[string]string
-
-// // Check the roles of members of m1
-// m1m := m1.Members()
-// if len(m1m) != 2 {
-// t.Fatalf("bad: %#v", m1m)
-// }
-
-// roles = make(map[string]string)
-// for _, m := range m1m {
-// roles[m.Name] = string(m.Meta)
-// }
-
-// if r := roles[c1.Name]; r != "api" {
-// t.Fatalf("bad role for %s: %s", c1.Name, r)
-// }
-
-// if r := roles[c2.Name]; r != "lb" {
-// t.Fatalf("bad role for %s: %s", c2.Name, r)
-// }
-
-// // Check the roles of members of m2
-// m2m := m2.Members()
-// if len(m2m) != 2 {
-// t.Fatalf("bad: %#v", m2m)
-// }
-
-// roles = make(map[string]string)
-// for _, m := range m2m {
-// roles[m.Name] = string(m.Meta)
-// }
-
-// if r := roles[c1.Name]; r != "api" {
-// t.Fatalf("bad role for %s: %s", c1.Name, r)
-// }
-
-// if r := roles[c2.Name]; r != "lb" {
-// t.Fatalf("bad role for %s: %s", c2.Name, r)
-// }
-//}
-
-// Failer is an interface compatible with testing.T.
-type Failer interface {
- // Log is called for the final test output
- Log(args ...interface{})
-
- // FailNow is called when the retrying is abandoned.
- FailNow()
-}
-
-// R provides context for the retryer.
-type R struct {
- fail bool
- output []string
-}
-
-func (r *R) FailNow() {
- r.fail = true
- runtime.Goexit()
-}
-
-func (r *R) Fatal(args ...interface{}) {
- r.log(fmt.Sprint(args...))
- r.FailNow()
-}
-
-func (r *R) Fatalf(format string, args ...interface{}) {
- r.log(fmt.Sprintf(format, args...))
- r.FailNow()
-}
-
-func (r *R) Error(args ...interface{}) {
- r.log(fmt.Sprint(args...))
- r.fail = true
-}
-
-func (r *R) Errorf(format string, args ...interface{}) {
- r.log(fmt.Sprintf(format, args...))
- r.fail = true
-}
-
-func (r *R) Check(err error) {
- if err != nil {
- r.log(err.Error())
- r.FailNow()
- }
-}
-
-func (r *R) log(s string) {
- r.output = append(r.output, decorate(s))
-}
-
-func decorate(s string) string {
- _, file, line, ok := runtime.Caller(3)
- if ok {
- n := strings.LastIndex(file, "/")
- if n >= 0 {
- file = file[n+1:]
- }
- } else {
- file = "???"
- line = 1
- }
- return fmt.Sprintf("%s:%d: %s", file, line, s)
-}
-
-func Run(t Failer, f func(r *R)) {
- run(DefaultFailer(), t, f)
-}
-
-func RunWith(r Retryer, t Failer, f func(r *R)) {
- run(r, t, f)
-}
-
-func dedup(a []string) string {
- if len(a) == 0 {
- return ""
- }
- m := map[string]int{}
- for _, s := range a {
- m[s] = m[s] + 1
- }
- var b bytes.Buffer
- for _, s := range a {
- if _, ok := m[s]; ok {
- b.WriteString(s)
- b.WriteRune('\n')
- delete(m, s)
- }
- }
- return b.String()
-}
-
-func run(r Retryer, t Failer, f func(r *R)) {
- rr := &R{}
- fail := func() {
- out := dedup(rr.output)
- if out != "" {
- t.Log(out)
- }
- t.FailNow()
- }
- for r.NextOr(fail) {
- var wg sync.WaitGroup
- wg.Add(1)
- go func() {
- defer wg.Done()
- f(rr)
- }()
- wg.Wait()
- if rr.fail {
- rr.fail = false
- continue
- }
- break
- }
-}
-
-// DefaultFailer provides default retry.Run() behavior for unit tests.
-func DefaultFailer() *Timer {
- return &Timer{Timeout: 7 * time.Second, Wait: 25 * time.Millisecond}
-}
-
-// TwoSeconds repeats an operation for two seconds and waits 25ms in between.
-func TwoSeconds() *Timer {
- return &Timer{Timeout: 2 * time.Second, Wait: 25 * time.Millisecond}
-}
-
-// ThreeTimes repeats an operation three times and waits 25ms in between.
-func ThreeTimes() *Counter {
- return &Counter{Count: 3, Wait: 25 * time.Millisecond}
-}
-
-// Retryer provides an interface for repeating operations
-// until they succeed or an exit condition is met.
-type Retryer interface {
- // NextOr returns true if the operation should be repeated.
- // Otherwise, it calls fail and returns false.
- NextOr(fail func()) bool
-}
-
-// Counter repeats an operation a given number of
-// times and waits between subsequent operations.
-type Counter struct {
- Count int
- Wait time.Duration
-
- count int
-}
-
-func (r *Counter) NextOr(fail func()) bool {
- if r.count == r.Count {
- fail()
- return false
- }
- if r.count > 0 {
- time.Sleep(r.Wait)
- }
- r.count++
- return true
-}
-
-// Timer repeats an operation for a given amount
-// of time and waits between subsequent operations.
-type Timer struct {
- Timeout time.Duration
- Wait time.Duration
-
- // stop is the timeout deadline.
- // Set on the first invocation of Next().
- stop time.Time
-}
-
-func (r *Timer) NextOr(fail func()) bool {
- if r.stop.IsZero() {
- r.stop = time.Now().Add(r.Timeout)
- return true
- }
- if time.Now().After(r.stop) {
- fail()
- return false
- }
- time.Sleep(r.Wait)
- return true
-}
-
-// delta defines the time band a test run should complete in.
-var delta = 25 * time.Millisecond
-
-func TestRetryer(t *testing.T) {
- tests := []struct {
- desc string
- r Retryer
- }{
- {"counter", &Counter{Count: 3, Wait: 100 * time.Millisecond}},
- {"timer", &Timer{Timeout: 200 * time.Millisecond, Wait: 100 * time.Millisecond}},
- }
-
- for _, tt := range tests {
- t.Run(tt.desc, func(t *testing.T) {
- var iters, fails int
- fail := func() { fails++ }
- start := time.Now()
- for tt.r.NextOr(fail) {
- iters++
- }
- dur := time.Since(start)
- if got, want := iters, 3; got != want {
- t.Fatalf("got %d retries want %d", got, want)
- }
- if got, want := fails, 1; got != want {
- t.Fatalf("got %d FailNow calls want %d", got, want)
- }
- // since the first iteration happens immediately
- // the retryer waits only twice for three iterations.
- // order of events: (true, (wait) true, (wait) true, false)
- if got, want := dur, 200*time.Millisecond; got < (want-delta) || got > (want+delta) {
- t.Fatalf("loop took %v want %v (+/- %v)", got, want, delta)
- }
- })
- }
-}
-
-func TestMemberlist_LocalNode(t *testing.T) {
- m, err := memberlist.Create(memberlist.DefaultWANConfig())
- require.NoError(t, err)
- defer m.Shutdown()
- require.NotNil(t, m.LocalNode())
-}
-
-func TestMemberlist_SendBestEffort(t *testing.T) {
- m := GetMemberlist(t, nil)
- defer m.Shutdown()
- err := m.SendBestEffort(&memberlist.Node{
- Name: "testNode",
- Addr: "127.0.0.1",
- Port: 7946,
- }, []byte("test message"))
- require.NoError(t, err)
-}
-
-// userMsgHeader is used to encapsulate a userMsg
-type userMsgHeader struct {
- UserMsgLen int // Encodes the byte lengh of user state
-}
-
-func TestMemberlist_SendReliable(t *testing.T) {
- node := &memberlist.Node{
- Name: "testNode",
- Addr: "127.0.0.1",
- Port: 7946,
- }
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
-
- //bufConn := bytes.NewBuffer(nil)
- //err := bufConn.WriteByte(byte(8))
- //require.NoError(t, err)
- //
- //msg := []byte("test message")
- //header := userMsgHeader{UserMsgLen: len(msg)}
- //hd := codec.MsgpackHandle{}
- //enc := codec.NewEncoder(bufConn, &hd)
- //err = enc.Encode(&header)
- //require.NoError(t, err)
- //_, err = bufConn.Write(msg)
- //require.NoError(t, err)
-
- conn := memmock.NewMockConn(ctrl)
- conn.
- EXPECT().
- Write(gomock.Any()).
- AnyTimes().
- Return(26, nil)
- conn.
- EXPECT().
- Close().
- AnyTimes().
- Return(nil)
-
- nat := memmock.NewMockNodeAwareTransport(ctrl)
- nat.
- EXPECT().
- DialAddressTimeout(node.FullAddress(), gomock.Any()).
- AnyTimes().
- Return(conn, nil)
- nat.
- EXPECT().
- FinalAdvertiseAddr(gomock.Any(), gomock.Any()).
- AnyTimes().
- Return("localhost", 7946, nil)
- nat.
- EXPECT().
- PacketCh().
- AnyTimes().
- Return(make(chan *memberlist.Packet))
- nat.
- EXPECT().
- StreamCh().
- AnyTimes().
- Return(make(<-chan net.Conn))
- nat.
- EXPECT().
- Shutdown().
- AnyTimes().
- Return(nil)
-
- m := GetMemberlist(t, func(c *memberlist.Config) {
- c.Transport = nat
- c.EnableCompression = false
- })
- defer m.Shutdown()
- err := m.SendReliable(node, []byte("test message"))
- require.NoError(t, err)
-}
-
-func TestMemberlist_SendReliable_Fail(t *testing.T) {
- m := GetMemberlist(t, nil)
- defer m.Shutdown()
- err := m.SendReliable(&memberlist.Node{
- Name: "testNode",
- Addr: "127.0.0.1",
- Port: 7946,
- }, []byte("test message"))
- require.Error(t, err)
-}
-
-func TestMemberlist_SendReliable_Fail2(t *testing.T) {
- node := &memberlist.Node{
- Name: "testNode",
- Addr: "127.0.0.1",
- Port: 7946,
- }
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- conn := memmock.NewMockConn(ctrl)
- conn.
- EXPECT().
- Write(gomock.Any()).
- AnyTimes().
- Return(12, nil)
- conn.
- EXPECT().
- Close().
- AnyTimes().
- Return(nil)
-
- nat := memmock.NewMockNodeAwareTransport(ctrl)
- nat.
- EXPECT().
- DialAddressTimeout(node.FullAddress(), gomock.Any()).
- AnyTimes().
- Return(conn, nil)
- nat.
- EXPECT().
- FinalAdvertiseAddr(gomock.Any(), gomock.Any()).
- AnyTimes().
- Return("localhost", 7946, nil)
- nat.
- EXPECT().
- PacketCh().
- AnyTimes().
- Return(make(chan *memberlist.Packet))
- nat.
- EXPECT().
- StreamCh().
- AnyTimes().
- Return(make(<-chan net.Conn))
- nat.
- EXPECT().
- Shutdown().
- AnyTimes().
- Return(nil)
-
- m := GetMemberlist(t, func(c *memberlist.Config) {
- c.Transport = nat
- })
- defer m.Shutdown()
-
- err := m.SendReliable(node, []byte("test message"))
- require.Error(t, err)
-}
diff --git a/toolkit/memberlist/merge_delegate.go b/toolkit/memberlist/merge_delegate.go
deleted file mode 100644
index 89afb59f..00000000
--- a/toolkit/memberlist/merge_delegate.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package memberlist
-
-// MergeDelegate is used to involve a client in
-// a potential cluster merge operation. Namely, when
-// a node does a TCP push/pull (as part of a join),
-// the delegate is involved and allowed to cancel the join
-// based on custom logic. The merge delegate is NOT invoked
-// as part of the push-pull anti-entropy.
-type MergeDelegate interface {
- // NotifyMerge is invoked when a merge could take place.
- // Provides a list of the nodes known by the peer. If
- // the return value is non-nil, the merge is canceled.
- NotifyMerge(peers []*Node) error
-}
diff --git a/toolkit/memberlist/mock/mock_dns_client_interface.go b/toolkit/memberlist/mock/mock_dns_client_interface.go
deleted file mode 100644
index 9c60b4dd..00000000
--- a/toolkit/memberlist/mock/mock_dns_client_interface.go
+++ /dev/null
@@ -1,52 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: ./dns_client_interface.go
-
-// Package mock is a generated GoMock package.
-package mock
-
-import (
- reflect "reflect"
- time "time"
-
- gomock "github.com/golang/mock/gomock"
- dns "github.com/miekg/dns"
-)
-
-// MockIDNSClient is a mock of IDNSClient interface.
-type MockIDNSClient struct {
- ctrl *gomock.Controller
- recorder *MockIDNSClientMockRecorder
-}
-
-// MockIDNSClientMockRecorder is the mock recorder for MockIDNSClient.
-type MockIDNSClientMockRecorder struct {
- mock *MockIDNSClient
-}
-
-// NewMockIDNSClient creates a new mock instance.
-func NewMockIDNSClient(ctrl *gomock.Controller) *MockIDNSClient {
- mock := &MockIDNSClient{ctrl: ctrl}
- mock.recorder = &MockIDNSClientMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockIDNSClient) EXPECT() *MockIDNSClientMockRecorder {
- return m.recorder
-}
-
-// Exchange mocks base method.
-func (m_2 *MockIDNSClient) Exchange(m *dns.Msg, address string) (*dns.Msg, time.Duration, error) {
- m_2.ctrl.T.Helper()
- ret := m_2.ctrl.Call(m_2, "Exchange", m, address)
- ret0, _ := ret[0].(*dns.Msg)
- ret1, _ := ret[1].(time.Duration)
- ret2, _ := ret[2].(error)
- return ret0, ret1, ret2
-}
-
-// Exchange indicates an expected call of Exchange.
-func (mr *MockIDNSClientMockRecorder) Exchange(m, address interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Exchange", reflect.TypeOf((*MockIDNSClient)(nil).Exchange), m, address)
-}
diff --git a/toolkit/memberlist/mock/mock_memberlist_interface.go b/toolkit/memberlist/mock/mock_memberlist_interface.go
deleted file mode 100644
index 09988edb..00000000
--- a/toolkit/memberlist/mock/mock_memberlist_interface.go
+++ /dev/null
@@ -1,249 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: ./memberlist_interface.go
-
-// Package mock is a generated GoMock package.
-package mock
-
-import (
- net "net"
- reflect "reflect"
- time "time"
-
- gomock "github.com/golang/mock/gomock"
- memberlist "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
-)
-
-// MockIMemberlist is a mock of IMemberlist interface.
-type MockIMemberlist struct {
- ctrl *gomock.Controller
- recorder *MockIMemberlistMockRecorder
-}
-
-// MockIMemberlistMockRecorder is the mock recorder for MockIMemberlist.
-type MockIMemberlistMockRecorder struct {
- mock *MockIMemberlist
-}
-
-// NewMockIMemberlist creates a new mock instance.
-func NewMockIMemberlist(ctrl *gomock.Controller) *MockIMemberlist {
- mock := &MockIMemberlist{ctrl: ctrl}
- mock.recorder = &MockIMemberlistMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockIMemberlist) EXPECT() *MockIMemberlistMockRecorder {
- return m.recorder
-}
-
-// AdvertiseAddr mocks base method.
-func (m *MockIMemberlist) AdvertiseAddr() string {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "AdvertiseAddr")
- ret0, _ := ret[0].(string)
- return ret0
-}
-
-// AdvertiseAddr indicates an expected call of AdvertiseAddr.
-func (mr *MockIMemberlistMockRecorder) AdvertiseAddr() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AdvertiseAddr", reflect.TypeOf((*MockIMemberlist)(nil).AdvertiseAddr))
-}
-
-// Config mocks base method.
-func (m *MockIMemberlist) Config() *memberlist.Config {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Config")
- ret0, _ := ret[0].(*memberlist.Config)
- return ret0
-}
-
-// Config indicates an expected call of Config.
-func (mr *MockIMemberlistMockRecorder) Config() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Config", reflect.TypeOf((*MockIMemberlist)(nil).Config))
-}
-
-// GetHealthScore mocks base method.
-func (m *MockIMemberlist) GetHealthScore() int {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "GetHealthScore")
- ret0, _ := ret[0].(int)
- return ret0
-}
-
-// GetHealthScore indicates an expected call of GetHealthScore.
-func (mr *MockIMemberlistMockRecorder) GetHealthScore() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetHealthScore", reflect.TypeOf((*MockIMemberlist)(nil).GetHealthScore))
-}
-
-// Join mocks base method.
-func (m *MockIMemberlist) Join(existing []string) (int, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Join", existing)
- ret0, _ := ret[0].(int)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Join indicates an expected call of Join.
-func (mr *MockIMemberlistMockRecorder) Join(existing interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Join", reflect.TypeOf((*MockIMemberlist)(nil).Join), existing)
-}
-
-// Leave mocks base method.
-func (m *MockIMemberlist) Leave(timeout time.Duration) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Leave", timeout)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Leave indicates an expected call of Leave.
-func (mr *MockIMemberlistMockRecorder) Leave(timeout interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Leave", reflect.TypeOf((*MockIMemberlist)(nil).Leave), timeout)
-}
-
-// LocalNode mocks base method.
-func (m *MockIMemberlist) LocalNode() *memberlist.Node {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "LocalNode")
- ret0, _ := ret[0].(*memberlist.Node)
- return ret0
-}
-
-// LocalNode indicates an expected call of LocalNode.
-func (mr *MockIMemberlistMockRecorder) LocalNode() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalNode", reflect.TypeOf((*MockIMemberlist)(nil).LocalNode))
-}
-
-// Members mocks base method.
-func (m *MockIMemberlist) Members() []*memberlist.Node {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Members")
- ret0, _ := ret[0].([]*memberlist.Node)
- return ret0
-}
-
-// Members indicates an expected call of Members.
-func (mr *MockIMemberlistMockRecorder) Members() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Members", reflect.TypeOf((*MockIMemberlist)(nil).Members))
-}
-
-// NumMembers mocks base method.
-func (m *MockIMemberlist) NumMembers() int {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "NumMembers")
- ret0, _ := ret[0].(int)
- return ret0
-}
-
-// NumMembers indicates an expected call of NumMembers.
-func (mr *MockIMemberlistMockRecorder) NumMembers() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NumMembers", reflect.TypeOf((*MockIMemberlist)(nil).NumMembers))
-}
-
-// Ping mocks base method.
-func (m *MockIMemberlist) Ping(node string, addr net.Addr) (time.Duration, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Ping", node, addr)
- ret0, _ := ret[0].(time.Duration)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Ping indicates an expected call of Ping.
-func (mr *MockIMemberlistMockRecorder) Ping(node, addr interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Ping", reflect.TypeOf((*MockIMemberlist)(nil).Ping), node, addr)
-}
-
-// ProtocolVersion mocks base method.
-func (m *MockIMemberlist) ProtocolVersion() uint8 {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "ProtocolVersion")
- ret0, _ := ret[0].(uint8)
- return ret0
-}
-
-// ProtocolVersion indicates an expected call of ProtocolVersion.
-func (mr *MockIMemberlistMockRecorder) ProtocolVersion() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ProtocolVersion", reflect.TypeOf((*MockIMemberlist)(nil).ProtocolVersion))
-}
-
-// SendBestEffort mocks base method.
-func (m *MockIMemberlist) SendBestEffort(to *memberlist.Node, msg []byte) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "SendBestEffort", to, msg)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// SendBestEffort indicates an expected call of SendBestEffort.
-func (mr *MockIMemberlistMockRecorder) SendBestEffort(to, msg interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendBestEffort", reflect.TypeOf((*MockIMemberlist)(nil).SendBestEffort), to, msg)
-}
-
-// SendReliable mocks base method.
-func (m *MockIMemberlist) SendReliable(to *memberlist.Node, msg []byte) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "SendReliable", to, msg)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// SendReliable indicates an expected call of SendReliable.
-func (mr *MockIMemberlistMockRecorder) SendReliable(to, msg interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendReliable", reflect.TypeOf((*MockIMemberlist)(nil).SendReliable), to, msg)
-}
-
-// SendToAddress mocks base method.
-func (m *MockIMemberlist) SendToAddress(a memberlist.Address, msg []byte) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "SendToAddress", a, msg)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// SendToAddress indicates an expected call of SendToAddress.
-func (mr *MockIMemberlistMockRecorder) SendToAddress(a, msg interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SendToAddress", reflect.TypeOf((*MockIMemberlist)(nil).SendToAddress), a, msg)
-}
-
-// Shutdown mocks base method.
-func (m *MockIMemberlist) Shutdown() error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Shutdown")
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Shutdown indicates an expected call of Shutdown.
-func (mr *MockIMemberlistMockRecorder) Shutdown() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockIMemberlist)(nil).Shutdown))
-}
-
-// UpdateNode mocks base method.
-func (m *MockIMemberlist) UpdateNode(timeout time.Duration) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "UpdateNode", timeout)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// UpdateNode indicates an expected call of UpdateNode.
-func (mr *MockIMemberlistMockRecorder) UpdateNode(timeout interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpdateNode", reflect.TypeOf((*MockIMemberlist)(nil).UpdateNode), timeout)
-}
diff --git a/toolkit/memberlist/mock/mock_net_conn_interface.go b/toolkit/memberlist/mock/mock_net_conn_interface.go
deleted file mode 100644
index be57dfb3..00000000
--- a/toolkit/memberlist/mock/mock_net_conn_interface.go
+++ /dev/null
@@ -1,150 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: ./net_conn_interface.go
-
-// Package mock is a generated GoMock package.
-package mock
-
-import (
- net "net"
- reflect "reflect"
- time "time"
-
- gomock "github.com/golang/mock/gomock"
-)
-
-// MockConn is a mock of Conn interface.
-type MockConn struct {
- ctrl *gomock.Controller
- recorder *MockConnMockRecorder
-}
-
-// MockConnMockRecorder is the mock recorder for MockConn.
-type MockConnMockRecorder struct {
- mock *MockConn
-}
-
-// NewMockConn creates a new mock instance.
-func NewMockConn(ctrl *gomock.Controller) *MockConn {
- mock := &MockConn{ctrl: ctrl}
- mock.recorder = &MockConnMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockConn) EXPECT() *MockConnMockRecorder {
- return m.recorder
-}
-
-// Close mocks base method.
-func (m *MockConn) Close() error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Close")
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Close indicates an expected call of Close.
-func (mr *MockConnMockRecorder) Close() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Close", reflect.TypeOf((*MockConn)(nil).Close))
-}
-
-// LocalAddr mocks base method.
-func (m *MockConn) LocalAddr() net.Addr {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "LocalAddr")
- ret0, _ := ret[0].(net.Addr)
- return ret0
-}
-
-// LocalAddr indicates an expected call of LocalAddr.
-func (mr *MockConnMockRecorder) LocalAddr() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "LocalAddr", reflect.TypeOf((*MockConn)(nil).LocalAddr))
-}
-
-// Read mocks base method.
-func (m *MockConn) Read(b []byte) (int, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Read", b)
- ret0, _ := ret[0].(int)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Read indicates an expected call of Read.
-func (mr *MockConnMockRecorder) Read(b interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Read", reflect.TypeOf((*MockConn)(nil).Read), b)
-}
-
-// RemoteAddr mocks base method.
-func (m *MockConn) RemoteAddr() net.Addr {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "RemoteAddr")
- ret0, _ := ret[0].(net.Addr)
- return ret0
-}
-
-// RemoteAddr indicates an expected call of RemoteAddr.
-func (mr *MockConnMockRecorder) RemoteAddr() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoteAddr", reflect.TypeOf((*MockConn)(nil).RemoteAddr))
-}
-
-// SetDeadline mocks base method.
-func (m *MockConn) SetDeadline(t time.Time) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "SetDeadline", t)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// SetDeadline indicates an expected call of SetDeadline.
-func (mr *MockConnMockRecorder) SetDeadline(t interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetDeadline", reflect.TypeOf((*MockConn)(nil).SetDeadline), t)
-}
-
-// SetReadDeadline mocks base method.
-func (m *MockConn) SetReadDeadline(t time.Time) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "SetReadDeadline", t)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// SetReadDeadline indicates an expected call of SetReadDeadline.
-func (mr *MockConnMockRecorder) SetReadDeadline(t interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetReadDeadline", reflect.TypeOf((*MockConn)(nil).SetReadDeadline), t)
-}
-
-// SetWriteDeadline mocks base method.
-func (m *MockConn) SetWriteDeadline(t time.Time) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "SetWriteDeadline", t)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// SetWriteDeadline indicates an expected call of SetWriteDeadline.
-func (mr *MockConnMockRecorder) SetWriteDeadline(t interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SetWriteDeadline", reflect.TypeOf((*MockConn)(nil).SetWriteDeadline), t)
-}
-
-// Write mocks base method.
-func (m *MockConn) Write(b []byte) (int, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Write", b)
- ret0, _ := ret[0].(int)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// Write indicates an expected call of Write.
-func (mr *MockConnMockRecorder) Write(b interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Write", reflect.TypeOf((*MockConn)(nil).Write), b)
-}
diff --git a/toolkit/memberlist/mock/mock_queue.go b/toolkit/memberlist/mock/mock_queue.go
deleted file mode 100644
index 8461efc2..00000000
--- a/toolkit/memberlist/mock/mock_queue.go
+++ /dev/null
@@ -1,227 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: ./queue.go
-
-// Package mock is a generated GoMock package.
-package mock
-
-import (
- reflect "reflect"
-
- gomock "github.com/golang/mock/gomock"
- memberlist "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
-)
-
-// MockBroadcast is a mock of Broadcast interface.
-type MockBroadcast struct {
- ctrl *gomock.Controller
- recorder *MockBroadcastMockRecorder
-}
-
-// MockBroadcastMockRecorder is the mock recorder for MockBroadcast.
-type MockBroadcastMockRecorder struct {
- mock *MockBroadcast
-}
-
-// NewMockBroadcast creates a new mock instance.
-func NewMockBroadcast(ctrl *gomock.Controller) *MockBroadcast {
- mock := &MockBroadcast{ctrl: ctrl}
- mock.recorder = &MockBroadcastMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockBroadcast) EXPECT() *MockBroadcastMockRecorder {
- return m.recorder
-}
-
-// Finished mocks base method.
-func (m *MockBroadcast) Finished() {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Finished")
-}
-
-// Finished indicates an expected call of Finished.
-func (mr *MockBroadcastMockRecorder) Finished() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Finished", reflect.TypeOf((*MockBroadcast)(nil).Finished))
-}
-
-// Invalidates mocks base method.
-func (m *MockBroadcast) Invalidates(b memberlist.Broadcast) bool {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Invalidates", b)
- ret0, _ := ret[0].(bool)
- return ret0
-}
-
-// Invalidates indicates an expected call of Invalidates.
-func (mr *MockBroadcastMockRecorder) Invalidates(b interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invalidates", reflect.TypeOf((*MockBroadcast)(nil).Invalidates), b)
-}
-
-// Message mocks base method.
-func (m *MockBroadcast) Message() []byte {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Message")
- ret0, _ := ret[0].([]byte)
- return ret0
-}
-
-// Message indicates an expected call of Message.
-func (mr *MockBroadcastMockRecorder) Message() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Message", reflect.TypeOf((*MockBroadcast)(nil).Message))
-}
-
-// MockNamedBroadcast is a mock of NamedBroadcast interface.
-type MockNamedBroadcast struct {
- ctrl *gomock.Controller
- recorder *MockNamedBroadcastMockRecorder
-}
-
-// MockNamedBroadcastMockRecorder is the mock recorder for MockNamedBroadcast.
-type MockNamedBroadcastMockRecorder struct {
- mock *MockNamedBroadcast
-}
-
-// NewMockNamedBroadcast creates a new mock instance.
-func NewMockNamedBroadcast(ctrl *gomock.Controller) *MockNamedBroadcast {
- mock := &MockNamedBroadcast{ctrl: ctrl}
- mock.recorder = &MockNamedBroadcastMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockNamedBroadcast) EXPECT() *MockNamedBroadcastMockRecorder {
- return m.recorder
-}
-
-// Finished mocks base method.
-func (m *MockNamedBroadcast) Finished() {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Finished")
-}
-
-// Finished indicates an expected call of Finished.
-func (mr *MockNamedBroadcastMockRecorder) Finished() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Finished", reflect.TypeOf((*MockNamedBroadcast)(nil).Finished))
-}
-
-// Invalidates mocks base method.
-func (m *MockNamedBroadcast) Invalidates(b memberlist.Broadcast) bool {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Invalidates", b)
- ret0, _ := ret[0].(bool)
- return ret0
-}
-
-// Invalidates indicates an expected call of Invalidates.
-func (mr *MockNamedBroadcastMockRecorder) Invalidates(b interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invalidates", reflect.TypeOf((*MockNamedBroadcast)(nil).Invalidates), b)
-}
-
-// Message mocks base method.
-func (m *MockNamedBroadcast) Message() []byte {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Message")
- ret0, _ := ret[0].([]byte)
- return ret0
-}
-
-// Message indicates an expected call of Message.
-func (mr *MockNamedBroadcastMockRecorder) Message() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Message", reflect.TypeOf((*MockNamedBroadcast)(nil).Message))
-}
-
-// Name mocks base method.
-func (m *MockNamedBroadcast) Name() string {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Name")
- ret0, _ := ret[0].(string)
- return ret0
-}
-
-// Name indicates an expected call of Name.
-func (mr *MockNamedBroadcastMockRecorder) Name() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Name", reflect.TypeOf((*MockNamedBroadcast)(nil).Name))
-}
-
-// MockUniqueBroadcast is a mock of UniqueBroadcast interface.
-type MockUniqueBroadcast struct {
- ctrl *gomock.Controller
- recorder *MockUniqueBroadcastMockRecorder
-}
-
-// MockUniqueBroadcastMockRecorder is the mock recorder for MockUniqueBroadcast.
-type MockUniqueBroadcastMockRecorder struct {
- mock *MockUniqueBroadcast
-}
-
-// NewMockUniqueBroadcast creates a new mock instance.
-func NewMockUniqueBroadcast(ctrl *gomock.Controller) *MockUniqueBroadcast {
- mock := &MockUniqueBroadcast{ctrl: ctrl}
- mock.recorder = &MockUniqueBroadcastMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockUniqueBroadcast) EXPECT() *MockUniqueBroadcastMockRecorder {
- return m.recorder
-}
-
-// Finished mocks base method.
-func (m *MockUniqueBroadcast) Finished() {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "Finished")
-}
-
-// Finished indicates an expected call of Finished.
-func (mr *MockUniqueBroadcastMockRecorder) Finished() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Finished", reflect.TypeOf((*MockUniqueBroadcast)(nil).Finished))
-}
-
-// Invalidates mocks base method.
-func (m *MockUniqueBroadcast) Invalidates(b memberlist.Broadcast) bool {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Invalidates", b)
- ret0, _ := ret[0].(bool)
- return ret0
-}
-
-// Invalidates indicates an expected call of Invalidates.
-func (mr *MockUniqueBroadcastMockRecorder) Invalidates(b interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Invalidates", reflect.TypeOf((*MockUniqueBroadcast)(nil).Invalidates), b)
-}
-
-// Message mocks base method.
-func (m *MockUniqueBroadcast) Message() []byte {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Message")
- ret0, _ := ret[0].([]byte)
- return ret0
-}
-
-// Message indicates an expected call of Message.
-func (mr *MockUniqueBroadcastMockRecorder) Message() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Message", reflect.TypeOf((*MockUniqueBroadcast)(nil).Message))
-}
-
-// UniqueBroadcast mocks base method.
-func (m *MockUniqueBroadcast) UniqueBroadcast() {
- m.ctrl.T.Helper()
- m.ctrl.Call(m, "UniqueBroadcast")
-}
-
-// UniqueBroadcast indicates an expected call of UniqueBroadcast.
-func (mr *MockUniqueBroadcastMockRecorder) UniqueBroadcast() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UniqueBroadcast", reflect.TypeOf((*MockUniqueBroadcast)(nil).UniqueBroadcast))
-}
diff --git a/toolkit/memberlist/mock/mock_transport.go b/toolkit/memberlist/mock/mock_transport.go
deleted file mode 100644
index cb4c3f6b..00000000
--- a/toolkit/memberlist/mock/mock_transport.go
+++ /dev/null
@@ -1,317 +0,0 @@
-// Code generated by MockGen. DO NOT EDIT.
-// Source: ./transport.go
-
-// Package mock is a generated GoMock package.
-package mock
-
-import (
- net "net"
- reflect "reflect"
- time "time"
-
- gomock "github.com/golang/mock/gomock"
- memberlist "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
-)
-
-// MockTransport is a mock of Transport interface.
-type MockTransport struct {
- ctrl *gomock.Controller
- recorder *MockTransportMockRecorder
-}
-
-// MockTransportMockRecorder is the mock recorder for MockTransport.
-type MockTransportMockRecorder struct {
- mock *MockTransport
-}
-
-// NewMockTransport creates a new mock instance.
-func NewMockTransport(ctrl *gomock.Controller) *MockTransport {
- mock := &MockTransport{ctrl: ctrl}
- mock.recorder = &MockTransportMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockTransport) EXPECT() *MockTransportMockRecorder {
- return m.recorder
-}
-
-// DialTimeout mocks base method.
-func (m *MockTransport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DialTimeout", addr, timeout)
- ret0, _ := ret[0].(net.Conn)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// DialTimeout indicates an expected call of DialTimeout.
-func (mr *MockTransportMockRecorder) DialTimeout(addr, timeout interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DialTimeout", reflect.TypeOf((*MockTransport)(nil).DialTimeout), addr, timeout)
-}
-
-// FinalAdvertiseAddr mocks base method.
-func (m *MockTransport) FinalAdvertiseAddr(ip string, port int) (string, int, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "FinalAdvertiseAddr", ip, port)
- ret0, _ := ret[0].(string)
- ret1, _ := ret[1].(int)
- ret2, _ := ret[2].(error)
- return ret0, ret1, ret2
-}
-
-// FinalAdvertiseAddr indicates an expected call of FinalAdvertiseAddr.
-func (mr *MockTransportMockRecorder) FinalAdvertiseAddr(ip, port interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinalAdvertiseAddr", reflect.TypeOf((*MockTransport)(nil).FinalAdvertiseAddr), ip, port)
-}
-
-// PacketCh mocks base method.
-func (m *MockTransport) PacketCh() <-chan *memberlist.Packet {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "PacketCh")
- ret0, _ := ret[0].(<-chan *memberlist.Packet)
- return ret0
-}
-
-// PacketCh indicates an expected call of PacketCh.
-func (mr *MockTransportMockRecorder) PacketCh() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PacketCh", reflect.TypeOf((*MockTransport)(nil).PacketCh))
-}
-
-// Shutdown mocks base method.
-func (m *MockTransport) Shutdown() error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Shutdown")
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Shutdown indicates an expected call of Shutdown.
-func (mr *MockTransportMockRecorder) Shutdown() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockTransport)(nil).Shutdown))
-}
-
-// StreamCh mocks base method.
-func (m *MockTransport) StreamCh() <-chan net.Conn {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "StreamCh")
- ret0, _ := ret[0].(<-chan net.Conn)
- return ret0
-}
-
-// StreamCh indicates an expected call of StreamCh.
-func (mr *MockTransportMockRecorder) StreamCh() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamCh", reflect.TypeOf((*MockTransport)(nil).StreamCh))
-}
-
-// WriteTo mocks base method.
-func (m *MockTransport) WriteTo(b []byte, addr string) (time.Time, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "WriteTo", b, addr)
- ret0, _ := ret[0].(time.Time)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// WriteTo indicates an expected call of WriteTo.
-func (mr *MockTransportMockRecorder) WriteTo(b, addr interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteTo", reflect.TypeOf((*MockTransport)(nil).WriteTo), b, addr)
-}
-
-// MockIngestionAwareTransport is a mock of IngestionAwareTransport interface.
-type MockIngestionAwareTransport struct {
- ctrl *gomock.Controller
- recorder *MockIngestionAwareTransportMockRecorder
-}
-
-// MockIngestionAwareTransportMockRecorder is the mock recorder for MockIngestionAwareTransport.
-type MockIngestionAwareTransportMockRecorder struct {
- mock *MockIngestionAwareTransport
-}
-
-// NewMockIngestionAwareTransport creates a new mock instance.
-func NewMockIngestionAwareTransport(ctrl *gomock.Controller) *MockIngestionAwareTransport {
- mock := &MockIngestionAwareTransport{ctrl: ctrl}
- mock.recorder = &MockIngestionAwareTransportMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockIngestionAwareTransport) EXPECT() *MockIngestionAwareTransportMockRecorder {
- return m.recorder
-}
-
-// IngestPacket mocks base method.
-func (m *MockIngestionAwareTransport) IngestPacket(conn net.Conn, addr net.Addr, now time.Time, shouldClose bool) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "IngestPacket", conn, addr, now, shouldClose)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// IngestPacket indicates an expected call of IngestPacket.
-func (mr *MockIngestionAwareTransportMockRecorder) IngestPacket(conn, addr, now, shouldClose interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IngestPacket", reflect.TypeOf((*MockIngestionAwareTransport)(nil).IngestPacket), conn, addr, now, shouldClose)
-}
-
-// IngestStream mocks base method.
-func (m *MockIngestionAwareTransport) IngestStream(conn net.Conn) error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "IngestStream", conn)
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// IngestStream indicates an expected call of IngestStream.
-func (mr *MockIngestionAwareTransportMockRecorder) IngestStream(conn interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "IngestStream", reflect.TypeOf((*MockIngestionAwareTransport)(nil).IngestStream), conn)
-}
-
-// MockNodeAwareTransport is a mock of NodeAwareTransport interface.
-type MockNodeAwareTransport struct {
- ctrl *gomock.Controller
- recorder *MockNodeAwareTransportMockRecorder
-}
-
-// MockNodeAwareTransportMockRecorder is the mock recorder for MockNodeAwareTransport.
-type MockNodeAwareTransportMockRecorder struct {
- mock *MockNodeAwareTransport
-}
-
-// NewMockNodeAwareTransport creates a new mock instance.
-func NewMockNodeAwareTransport(ctrl *gomock.Controller) *MockNodeAwareTransport {
- mock := &MockNodeAwareTransport{ctrl: ctrl}
- mock.recorder = &MockNodeAwareTransportMockRecorder{mock}
- return mock
-}
-
-// EXPECT returns an object that allows the caller to indicate expected use.
-func (m *MockNodeAwareTransport) EXPECT() *MockNodeAwareTransportMockRecorder {
- return m.recorder
-}
-
-// DialAddressTimeout mocks base method.
-func (m *MockNodeAwareTransport) DialAddressTimeout(addr memberlist.Address, timeout time.Duration) (net.Conn, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DialAddressTimeout", addr, timeout)
- ret0, _ := ret[0].(net.Conn)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// DialAddressTimeout indicates an expected call of DialAddressTimeout.
-func (mr *MockNodeAwareTransportMockRecorder) DialAddressTimeout(addr, timeout interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DialAddressTimeout", reflect.TypeOf((*MockNodeAwareTransport)(nil).DialAddressTimeout), addr, timeout)
-}
-
-// DialTimeout mocks base method.
-func (m *MockNodeAwareTransport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "DialTimeout", addr, timeout)
- ret0, _ := ret[0].(net.Conn)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// DialTimeout indicates an expected call of DialTimeout.
-func (mr *MockNodeAwareTransportMockRecorder) DialTimeout(addr, timeout interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DialTimeout", reflect.TypeOf((*MockNodeAwareTransport)(nil).DialTimeout), addr, timeout)
-}
-
-// FinalAdvertiseAddr mocks base method.
-func (m *MockNodeAwareTransport) FinalAdvertiseAddr(ip string, port int) (string, int, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "FinalAdvertiseAddr", ip, port)
- ret0, _ := ret[0].(string)
- ret1, _ := ret[1].(int)
- ret2, _ := ret[2].(error)
- return ret0, ret1, ret2
-}
-
-// FinalAdvertiseAddr indicates an expected call of FinalAdvertiseAddr.
-func (mr *MockNodeAwareTransportMockRecorder) FinalAdvertiseAddr(ip, port interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "FinalAdvertiseAddr", reflect.TypeOf((*MockNodeAwareTransport)(nil).FinalAdvertiseAddr), ip, port)
-}
-
-// PacketCh mocks base method.
-func (m *MockNodeAwareTransport) PacketCh() <-chan *memberlist.Packet {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "PacketCh")
- ret0, _ := ret[0].(<-chan *memberlist.Packet)
- return ret0
-}
-
-// PacketCh indicates an expected call of PacketCh.
-func (mr *MockNodeAwareTransportMockRecorder) PacketCh() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PacketCh", reflect.TypeOf((*MockNodeAwareTransport)(nil).PacketCh))
-}
-
-// Shutdown mocks base method.
-func (m *MockNodeAwareTransport) Shutdown() error {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "Shutdown")
- ret0, _ := ret[0].(error)
- return ret0
-}
-
-// Shutdown indicates an expected call of Shutdown.
-func (mr *MockNodeAwareTransportMockRecorder) Shutdown() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Shutdown", reflect.TypeOf((*MockNodeAwareTransport)(nil).Shutdown))
-}
-
-// StreamCh mocks base method.
-func (m *MockNodeAwareTransport) StreamCh() <-chan net.Conn {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "StreamCh")
- ret0, _ := ret[0].(<-chan net.Conn)
- return ret0
-}
-
-// StreamCh indicates an expected call of StreamCh.
-func (mr *MockNodeAwareTransportMockRecorder) StreamCh() *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "StreamCh", reflect.TypeOf((*MockNodeAwareTransport)(nil).StreamCh))
-}
-
-// WriteTo mocks base method.
-func (m *MockNodeAwareTransport) WriteTo(b []byte, addr string) (time.Time, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "WriteTo", b, addr)
- ret0, _ := ret[0].(time.Time)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// WriteTo indicates an expected call of WriteTo.
-func (mr *MockNodeAwareTransportMockRecorder) WriteTo(b, addr interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteTo", reflect.TypeOf((*MockNodeAwareTransport)(nil).WriteTo), b, addr)
-}
-
-// WriteToAddress mocks base method.
-func (m *MockNodeAwareTransport) WriteToAddress(b []byte, addr memberlist.Address) (time.Time, error) {
- m.ctrl.T.Helper()
- ret := m.ctrl.Call(m, "WriteToAddress", b, addr)
- ret0, _ := ret[0].(time.Time)
- ret1, _ := ret[1].(error)
- return ret0, ret1
-}
-
-// WriteToAddress indicates an expected call of WriteToAddress.
-func (mr *MockNodeAwareTransportMockRecorder) WriteToAddress(b, addr interface{}) *gomock.Call {
- mr.mock.ctrl.T.Helper()
- return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "WriteToAddress", reflect.TypeOf((*MockNodeAwareTransport)(nil).WriteToAddress), b, addr)
-}
diff --git a/toolkit/memberlist/net.go b/toolkit/memberlist/net.go
deleted file mode 100644
index 2011aac5..00000000
--- a/toolkit/memberlist/net.go
+++ /dev/null
@@ -1,1345 +0,0 @@
-package memberlist
-
-import (
- "bufio"
- "bytes"
- "encoding/binary"
- "fmt"
- "hash/crc32"
- "io"
- "net"
- "sync/atomic"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-
- metrics "github.com/armon/go-metrics"
- "github.com/hashicorp/go-msgpack/codec"
-)
-
-// This is the minimum and maximum protocol version that we can
-// _understand_. We're allowed to speak at any version within this
-// range. This range is inclusive.
-const (
- ProtocolVersionMin uint8 = 1
-
- // Version 3 added support for TCP pings but we kept the default
- // protocol version at 2 to ease transition to this new feature.
- // A memberlist speaking version 2 of the protocol will attempt
- // to TCP ping another memberlist who understands version 3 or
- // greater.
- //
- // Version 4 added support for nacks as part of indirect probes.
- // A memberlist speaking version 2 of the protocol will expect
- // nacks from another memberlist who understands version 4 or
- // greater, and likewise nacks will be sent to memberlists who
- // understand version 4 or greater.
- ProtocolVersion2Compatible = 2
-
- ProtocolVersionMax = 5
-)
-
-// messageType is an integer ID of a type of message that can be received
-// on network channels from other members.
-type messageType uint8
-
-// The list of available message types.
-const (
- pingMsg messageType = iota
- indirectPingMsg
- ackRespMsg
- suspectMsg
- aliveMsg
- deadMsg
- pushPullMsg
- compoundMsg
- userMsg // User message, not handled by us
- compressMsg
- encryptMsg
- nackRespMsg
- hasCrcMsg
- errMsg
- weightMsg
-)
-
-// compressionType is used to specify the compression algorithm
-type compressionType uint8
-
-const (
- lzwAlgo compressionType = iota
-)
-
-const (
- MetaMaxSize = 512 // Maximum size for node meta data
- compoundHeaderOverhead = 2 // Assumed header overhead
- compoundOverhead = 2 // Assumed overhead per entry in compoundHeader
- userMsgOverhead = 1
- weightMsgOverhead = 1
- blockingWarning = 10 * time.Millisecond // Warn if a UDP packet takes this long to process
- maxPushStateBytes = 20 * 1024 * 1024
- maxPushPullRequests = 128 // Maximum number of concurrent push/pull requests
-)
-
-// ping request sent directly to node
-type ping struct {
- SeqNo uint32
-
- // Node is sent so the target can verify they are
- // the intended recipient. This is to protect again an agent
- // restart with a new name.
- Node string
-
- SourceAddr string `codec:",omitempty"` // Source address, used for a direct reply
- SourcePort uint16 `codec:",omitempty"` // Source port, used for a direct reply
- SourceNode string `codec:",omitempty"` // Source name, used for a direct reply
-}
-
-// indirect ping sent to an indirect node
-type indirectPingReq struct {
- SeqNo uint32
- Target string
- Port uint16
-
- // Node is sent so the target can verify they are
- // the intended recipient. This is to protect against an agent
- // restart with a new name.
- Node string
-
- Nack bool // true if we'd like a nack back
-
- SourceAddr string `codec:",omitempty"` // Source address, used for a direct reply
- SourcePort uint16 `codec:",omitempty"` // Source port, used for a direct reply
- SourceNode string `codec:",omitempty"` // Source name, used for a direct reply
-}
-
-// ack response is sent for a ping
-type ackResp struct {
- SeqNo uint32
- Payload []byte
-}
-
-// nack response is sent for an indirect ping when the pinger doesn't hear from
-// the ping-ee within the configured timeout. This lets the original node know
-// that the indirect ping attempt happened but didn't succeed.
-type nackResp struct {
- SeqNo uint32
-}
-
-// err response is sent to relay the error from the remote end
-type errResp struct {
- Error string
-}
-
-// suspect is broadcast when we suspect a node is dead
-type suspect struct {
- Incarnation uint32
- Node string
- From string // Include who is suspecting
-}
-
-// alive is broadcast when we know a node is alive.
-// Overloaded for nodes joining
-type alive struct {
- Incarnation uint32
- Node string
- Addr string
- Port uint16
- Meta []byte
-
- // The versions of the protocol/delegate that are being spoken, order:
- // pmin, pmax, pcur, dmin, dmax, dcur
- Vsn []uint8
-}
-
-func NewAlive(incarnation uint32, node string, addr string, port uint16, meta []byte, vsn []uint8) alive {
- return alive{Incarnation: incarnation, Node: node, Addr: addr, Port: port, Meta: meta, Vsn: vsn}
-}
-
-// dead is broadcast when we confirm a node is dead
-// Overloaded for nodes leaving
-type dead struct {
- Incarnation uint32
- Node string
- From string // Include who is suspecting
-}
-
-// weight is broadcast when we send local node weight
-type weight struct {
- Incarnation uint32
- // Node whose weight
- Node string
- // From message from which node
- From string
- // Weight the weight for Node
- Weight int
- // WeightAt is UTC timestamp which the weight calculated at, used for ignoring old weight messages in milliseconds
- WeightAt int64
-}
-
-func NewWeight(incarnation uint32, node string, from string, wei int, weightAt int64) *weight {
- return &weight{Incarnation: incarnation, Node: node, From: from, Weight: wei, WeightAt: weightAt}
-}
-
-// pushPullHeader is used to inform the
-// otherside how many states we are transferring
-type pushPullHeader struct {
- Nodes int
- UserStateLen int // Encodes the byte lengh of user state
- Join bool // Is this a join request or a anti-entropy run
-}
-
-// userMsgHeader is used to encapsulate a userMsg
-type userMsgHeader struct {
- UserMsgLen int // Encodes the byte lengh of user state
-}
-
-// pushNodeState is used for pushPullReq when we are
-// transferring out node states
-type pushNodeState struct {
- Name string
- Addr string
- Port uint16
- Meta []byte
- Incarnation uint32
- State NodeStateType
- Vsn []uint8 // Protocol versions
-}
-
-// compress is used to wrap an underlying payload
-// using a specified compression algorithm
-type compress struct {
- Algo compressionType
- Buf []byte
-}
-
-// msgHandoff is used to transfer a message between goroutines
-type msgHandoff struct {
- msgType messageType
- buf []byte
- from net.Addr
-}
-
-// encryptionVersion returns the encryption version to use
-func (m *Memberlist) encryptionVersion() encryptionVersion {
- switch m.ProtocolVersion() {
- case 1:
- return 0
- default:
- return 1
- }
-}
-
-// streamListen is a long running goroutine that pulls incoming streams from the
-// transport and hands them off for processing.
-func (m *Memberlist) streamListen() {
- for {
- select {
- case conn := <-m.transport.StreamCh():
- go m.handleConn(conn)
-
- case <-m.shutdownCh:
- return
- }
- }
-}
-
-// handleConn handles a single incoming stream connection from the transport.
-func (m *Memberlist) handleConn(conn net.Conn) {
- defer conn.Close()
- m.logger.Printf("[DEBUG] memberlist: Stream connection %s", LogConn(conn))
-
- metrics.IncrCounter([]string{"memberlist", "tcp", "accept"}, 1)
-
- from := conn.RemoteAddr()
- if err := m.ensureCanConnect(from); err != nil {
- m.logger.Printf("[DEBUG] memberlist: Blocked message: %s from %s", err, LogAddress(from))
- return
- }
-
- conn.SetDeadline(time.Now().Add(m.config.TCPTimeout))
-
- msgType, bufConn, dec, err := m.readStream(conn)
- if err != nil {
- if err != io.EOF {
- m.logger.Printf("[ERR] memberlist: failed to receive: %s %s", err, LogConn(conn))
-
- resp := errResp{err.Error()}
- out, err := encode(errMsg, &resp)
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to encode error response: %s", err)
- return
- }
-
- err = m.rawSendMsgStream(conn, out.Bytes())
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to send error: %s %s", err, LogConn(conn))
- return
- }
- }
- return
- }
-
- switch msgType {
- case userMsg:
- if err := m.readUserMsg(bufConn, dec); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to receive user message: %s %s", err, LogConn(conn))
- }
- case pushPullMsg:
- // Increment counter of pending push/pulls
- numConcurrent := atomic.AddUint32(&m.pushPullReq, 1)
- defer atomic.AddUint32(&m.pushPullReq, ^uint32(0))
-
- // Check if we have too many open push/pull requests
- if numConcurrent >= maxPushPullRequests {
- m.logger.Printf("[ERR] memberlist: Too many pending push/pull requests")
- return
- }
-
- join, remoteNodes, userState, err := m.readRemoteState(bufConn, dec)
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to read remote state: %s %s", err, LogConn(conn))
- return
- }
-
- if join {
- remote := remoteNodes[0]
- if m.config.IPMustBeChecked() {
- if stringutils.IsNotEmpty(remote.Addr) {
- if err := m.config.AddrAllowed(remote.Addr); err != nil {
- m.logger.Printf("[DEBUG] memberlist: Blocked join.Addr=%s message from: %s %s", remote.Addr, err, LogAddress(from))
- return
- }
- }
- }
- }
-
- if err := m.sendLocalState(conn, join); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to push local state: %s %s", err, LogConn(conn))
- return
- }
-
- if err := m.mergeRemoteState(join, remoteNodes, userState); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed push/pull merge: %s %s", err, LogConn(conn))
- return
- }
- case pingMsg:
- var p ping
- if err := dec.Decode(&p); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to decode ping: %s %s", err, LogConn(conn))
- return
- }
-
- if p.Node != "" && p.Node != m.config.Name {
- m.logger.Printf("[WARN] memberlist: Got ping for unexpected node %s %s", p.Node, LogConn(conn))
- return
- }
-
- if m.config.IPMustBeChecked() {
- if stringutils.IsNotEmpty(p.SourceAddr) {
- if err := m.config.AddrAllowed(p.SourceAddr); err != nil {
- m.logger.Printf("[DEBUG] memberlist: Blocked ping.Addr=%s message from: %s %s", p.SourceAddr, err, LogAddress(from))
- return
- }
- }
- }
-
- ack := ackResp{p.SeqNo, nil}
- out, err := encode(ackRespMsg, &ack)
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to encode ack: %s", err)
- return
- }
-
- err = m.rawSendMsgStream(conn, out.Bytes())
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to send ack: %s %s", err, LogConn(conn))
- return
- }
- default:
- m.logger.Printf("[ERR] memberlist: Received invalid msgType (%d) %s", msgType, LogConn(conn))
- }
-}
-
-// packetListen is a long-running goroutine that pulls packets out of the
-// transport and hands them off for processing.
-func (m *Memberlist) packetListen() {
- for {
- select {
- case packet := <-m.transport.PacketCh():
- m.ingestPacket(packet.Buf, packet.From, packet.Timestamp)
-
- case <-m.shutdownCh:
- return
- }
- }
-}
-
-func (m *Memberlist) ingestPacket(buf []byte, from net.Addr, timestamp time.Time) {
- if err := m.ensureCanConnect(from); err != nil {
- m.logger.Printf("[DEBUG] memberlist: Blocked message: %s from %s", err, LogAddress(from))
- return
- }
- // Check if encryption is enabled
- if m.config.EncryptionEnabled() {
- // Decrypt the payload
- plain, err := decryptPayload(m.config.Keyring.GetKeys(), buf, nil)
- if err != nil {
- if !m.config.GossipVerifyIncoming {
- // Treat the message as plaintext
- plain = buf
- } else {
- m.logger.Printf("[ERR] memberlist: Decrypt packet failed: %v %s", err, LogAddress(from))
- return
- }
- }
-
- // Continue processing the plaintext buffer
- buf = plain
- }
-
- // See if there's a checksum included to verify the contents of the message
- if len(buf) >= 5 && messageType(buf[0]) == hasCrcMsg {
- crc := crc32.ChecksumIEEE(buf[5:])
- expected := binary.BigEndian.Uint32(buf[1:5])
- if crc != expected {
- m.logger.Printf("[WARN] memberlist: Got invalid checksum for UDP packet: %x, %x", crc, expected)
- return
- }
- m.handleCommand(buf[5:], from, timestamp)
- } else {
- m.handleCommand(buf, from, timestamp)
- }
-}
-
-func (m *Memberlist) handleCommand(buf []byte, from net.Addr, timestamp time.Time) {
- if len(buf) < 1 {
- m.logger.Printf("[ERR] memberlist: missing message type byte %s", LogAddress(from))
- return
- }
- // Decode the message type
- msgType := messageType(buf[0])
- buf = buf[1:]
-
- // Switch on the msgType
- switch msgType {
- case compoundMsg:
- m.handleCompound(buf, from, timestamp)
- case compressMsg:
- m.handleCompressed(buf, from, timestamp)
-
- case pingMsg:
- m.handlePing(buf, from)
- case indirectPingMsg:
- m.handleIndirectPing(buf, from)
- case ackRespMsg:
- m.handleAck(buf, from, timestamp)
- case nackRespMsg:
- m.handleNack(buf, from)
-
- case suspectMsg:
- fallthrough
- case aliveMsg:
- fallthrough
- case deadMsg:
- fallthrough
- case weightMsg:
- fallthrough
- case userMsg:
- // Determine the message queue, prioritize alive
- queue := m.lowPriorityMsgQueue
- if msgType == aliveMsg {
- queue = m.highPriorityMsgQueue
- }
-
- // Check for overflow and append if not full
- m.msgQueueLock.Lock()
- if queue.Len() >= m.config.HandoffQueueDepth {
- m.logger.Printf("[WARN] memberlist: handler queue full, dropping message (%d) %s", msgType, LogAddress(from))
- } else {
- queue.PushBack(msgHandoff{msgType, buf, from})
- }
- m.msgQueueLock.Unlock()
-
- // Notify of pending message
- select {
- case m.handoffCh <- struct{}{}:
- default:
- }
-
- default:
- m.logger.Printf("[ERR] memberlist: msg type (%d) not supported %s", msgType, LogAddress(from))
- }
-}
-
-// getNextMessage returns the next message to process in priority order, using LIFO
-func (m *Memberlist) getNextMessage() (msgHandoff, bool) {
- m.msgQueueLock.Lock()
- defer m.msgQueueLock.Unlock()
-
- if el := m.highPriorityMsgQueue.Back(); el != nil {
- m.highPriorityMsgQueue.Remove(el)
- msg := el.Value.(msgHandoff)
- return msg, true
- } else if el := m.lowPriorityMsgQueue.Back(); el != nil {
- m.lowPriorityMsgQueue.Remove(el)
- msg := el.Value.(msgHandoff)
- return msg, true
- }
- return msgHandoff{}, false
-}
-
-// packetHandler is a long-running goroutine that processes messages received
-// over the packet interface, but is decoupled from the listener to avoid
-// blocking the listener which may cause ping/ack messages to be delayed.
-func (m *Memberlist) packetHandler() {
- for {
- select {
- case <-m.handoffCh:
- for {
- msg, ok := m.getNextMessage()
- if !ok {
- break
- }
- msgType := msg.msgType
- buf := msg.buf
- from := msg.from
-
- switch msgType {
- case suspectMsg:
- m.handleSuspect(buf, from)
- case aliveMsg:
- m.handleAlive(buf, from)
- case deadMsg:
- m.handleDead(buf, from)
- case weightMsg:
- m.handleWeight(buf, from)
- case userMsg:
- m.handleUser(buf, from)
- default:
- m.logger.Printf("[ERR] memberlist: Message type (%d) not supported %s (packet handler)", msgType, LogAddress(from))
- }
- }
-
- case <-m.shutdownCh:
- return
- }
- }
-}
-
-func (m *Memberlist) handleCompound(buf []byte, from net.Addr, timestamp time.Time) {
- // Decode the parts
- trunc, parts, err := decodeCompoundMessage(buf)
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to decode compound request: %s %s", err, LogAddress(from))
- return
- }
-
- // Log any truncation
- if trunc > 0 {
- m.logger.Printf("[WARN] memberlist: Compound request had %d truncated messages %s", trunc, LogAddress(from))
- }
-
- // Handle each message
- for _, part := range parts {
- m.handleCommand(part, from, timestamp)
- }
-}
-
-func (m *Memberlist) handlePing(buf []byte, from net.Addr) {
- var p ping
- if err := decode(buf, &p); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to decode ping request: %s %s", err, LogAddress(from))
- return
- }
- // If node is provided, verify that it is for us
- if p.Node != "" && p.Node != m.config.Name {
- m.logger.Printf("[WARN] memberlist: Got ping for unexpected node '%s' %s", p.Node, LogAddress(from))
- return
- }
-
- if m.config.IPMustBeChecked() {
- if stringutils.IsNotEmpty(p.SourceAddr) {
- if err := m.config.AddrAllowed(p.SourceAddr); err != nil {
- m.logger.Printf("[DEBUG] memberlist: Blocked ping.Addr=%s message from: %s %s", p.SourceAddr, err, LogAddress(from))
- return
- }
- }
- }
-
- var ack ackResp
- ack.SeqNo = p.SeqNo
- if m.config.Ping != nil {
- ack.Payload = m.config.Ping.AckPayload()
- }
-
- addr := ""
- if len(p.SourceAddr) > 0 && p.SourcePort > 0 {
- addr = joinHostPort(p.SourceAddr, p.SourcePort)
- } else {
- addr = from.String()
- }
-
- a := Address{
- Addr: addr,
- Name: p.SourceNode,
- }
- if err := m.encodeAndSendMsg(a, ackRespMsg, &ack); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to send ack: %s %s", err, LogAddress(from))
- }
-}
-
-func (m *Memberlist) handleIndirectPing(buf []byte, from net.Addr) {
- var ind indirectPingReq
- if err := decode(buf, &ind); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to decode indirect ping request: %s %s", err, LogAddress(from))
- return
- }
-
- if m.config.IPMustBeChecked() {
- if stringutils.IsNotEmpty(ind.SourceAddr) {
- if err := m.config.AddrAllowed(ind.SourceAddr); err != nil {
- m.logger.Printf("[DEBUG] memberlist: Blocked indirectPing.Addr=%s message from: %s %s", ind.SourceAddr, err, LogAddress(from))
- return
- }
- }
- }
-
- // For proto versions < 2, there is no port provided. Mask old
- // behavior by using the configured port.
- if m.ProtocolVersion() < 2 || ind.Port == 0 {
- ind.Port = uint16(m.config.BindPort)
- }
-
- // Send a ping to the correct host.
- localSeqNo := m.nextSeqNo()
- selfAddr, selfPort := m.getAdvertise()
- ping := ping{
- SeqNo: localSeqNo,
- Node: ind.Node,
- // The outbound message is addressed FROM us.
- SourceAddr: selfAddr,
- SourcePort: selfPort,
- SourceNode: m.config.Name,
- }
-
- // Forward the ack back to the requestor. If the request encodes an origin
- // use that otherwise assume that the other end of the UDP socket is
- // usable.
- indAddr := ""
- if len(ind.SourceAddr) > 0 && ind.SourcePort > 0 {
- indAddr = joinHostPort(ind.SourceAddr, ind.SourcePort)
- } else {
- indAddr = from.String()
- }
-
- // Setup a response handler to relay the ack
- cancelCh := make(chan struct{})
- respHandler := func(payload []byte, timestamp time.Time) {
- // Try to prevent the nack if we've caught it in time.
- close(cancelCh)
-
- ack := ackResp{ind.SeqNo, nil}
- a := Address{
- Addr: indAddr,
- Name: ind.SourceNode,
- }
- if err := m.encodeAndSendMsg(a, ackRespMsg, &ack); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to forward ack: %s %s", err, LogStringAddress(indAddr))
- }
- }
- m.setAckHandler(localSeqNo, respHandler, m.config.ProbeTimeout)
-
- // Send the ping.
- addr := joinHostPort(ind.Target, ind.Port)
- a := Address{
- Addr: addr,
- Name: ind.Node,
- }
- if err := m.encodeAndSendMsg(a, pingMsg, &ping); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to send indirect ping: %s %s", err, LogStringAddress(indAddr))
- }
-
- // Setup a timer to fire off a nack if no ack is seen in time.
- if ind.Nack {
- go func() {
- select {
- case <-cancelCh:
- return
- case <-time.After(m.config.ProbeTimeout):
- nack := nackResp{ind.SeqNo}
- a := Address{
- Addr: indAddr,
- Name: ind.SourceNode,
- }
- if err := m.encodeAndSendMsg(a, nackRespMsg, &nack); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to send nack: %s %s", err, LogStringAddress(indAddr))
- }
- }
- }()
- }
-}
-
-func (m *Memberlist) handleAck(buf []byte, from net.Addr, timestamp time.Time) {
- var ack ackResp
- if err := decode(buf, &ack); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to decode ack response: %s %s", err, LogAddress(from))
- return
- }
- m.invokeAckHandler(ack, timestamp)
-}
-
-func (m *Memberlist) handleNack(buf []byte, from net.Addr) {
- var nack nackResp
- if err := decode(buf, &nack); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to decode nack response: %s %s", err, LogAddress(from))
- return
- }
- m.invokeNackHandler(nack)
-}
-
-func (m *Memberlist) handleSuspect(buf []byte, from net.Addr) {
- var sus suspect
- if err := decode(buf, &sus); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to decode suspect message: %s %s", err, LogAddress(from))
- return
- }
- m.suspectNode(&sus)
-}
-
-// ensureCanConnect return the IP from a RemoteAddress
-// return error if this client must not connect
-func (m *Memberlist) ensureCanConnect(from net.Addr) error {
- if !m.config.IPMustBeChecked() {
- return nil
- }
- source := from.String()
- if source == "pipe" {
- return nil
- }
- host, _, err := net.SplitHostPort(source)
- if err != nil {
- return err
- }
- return m.config.AddrAllowed(host)
-}
-
-func (m *Memberlist) handleAlive(buf []byte, from net.Addr) {
- if err := m.ensureCanConnect(from); err != nil {
- m.logger.Printf("[DEBUG] memberlist: Blocked alive message: %s %s", err, LogAddress(from))
- return
- }
- var live alive
- if err := decode(buf, &live); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to decode alive message: %s %s", err, LogAddress(from))
- return
- }
- if m.config.IPMustBeChecked() {
- if stringutils.IsNotEmpty(live.Addr) {
- if err := m.config.AddrAllowed(live.Addr); err != nil {
- m.logger.Printf("[DEBUG] memberlist: Blocked alive.Addr=%s message from: %s %s", live.Addr, err, LogAddress(from))
- return
- }
- }
- }
-
- // For proto versions < 2, there is no port provided. Mask old
- // behavior by using the configured port
- if m.ProtocolVersion() < 2 || live.Port == 0 {
- live.Port = uint16(m.config.BindPort)
- }
-
- m.aliveNode(&live, nil, false)
-}
-
-func (m *Memberlist) handleDead(buf []byte, from net.Addr) {
- var d dead
- if err := decode(buf, &d); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to decode dead message: %s %s", err, LogAddress(from))
- return
- }
- m.deadNode(&d)
-}
-
-func (m *Memberlist) handleWeight(buf []byte, from net.Addr) {
- var wei weight
- if err := decode(buf, &wei); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to decode weight message: %s %s", err, LogAddress(from))
- return
- }
- m.weightNode(&wei)
-}
-
-// handleUser is used to notify channels of incoming user data
-func (m *Memberlist) handleUser(buf []byte, from net.Addr) {
- d := m.config.Delegate
- if d != nil {
- d.NotifyMsg(buf)
- }
-}
-
-// handleCompressed is used to unpack a compressed message
-func (m *Memberlist) handleCompressed(buf []byte, from net.Addr, timestamp time.Time) {
- // Try to decode the payload
- payload, err := decompressPayload(buf)
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to decompress payload: %v %s", err, LogAddress(from))
- return
- }
-
- // Recursively handle the payload
- m.handleCommand(payload, from, timestamp)
-}
-
-// encodeAndSendMsg is used to combine the encoding and sending steps
-func (m *Memberlist) encodeAndSendMsg(a Address, msgType messageType, msg interface{}) error {
- out, err := encode(msgType, msg)
- if err != nil {
- return err
- }
- if err := m.sendMsg(a, out.Bytes()); err != nil {
- return err
- }
- return nil
-}
-
-// sendMsg is used to send a message via packet to another host. It will
-// opportunistically create a compoundMsg and piggy back other broadcasts.
-func (m *Memberlist) sendMsg(a Address, msg []byte) error {
- // Check if we can piggy back any messages
- bytesAvail := m.config.UDPBufferSize - len(msg) - compoundHeaderOverhead
- if m.config.EncryptionEnabled() && m.config.GossipVerifyOutgoing {
- bytesAvail -= encryptOverhead(m.encryptionVersion())
- }
- extra := m.getBroadcasts(compoundOverhead, bytesAvail)
-
- // Fast path if nothing to piggypack
- if len(extra) == 0 {
- return m.rawSendMsgPacket(a, nil, msg)
- }
-
- // Join all the messages
- msgs := make([][]byte, 0, 1+len(extra))
- msgs = append(msgs, msg)
- msgs = append(msgs, extra...)
-
- // Create a compound message
- compound := makeCompoundMessage(msgs)
-
- // Send the message
- return m.rawSendMsgPacket(a, nil, compound.Bytes())
-}
-
-// rawSendMsgPacket is used to send message via packet to another host without
-// modification, other than compression or encryption if enabled.
-func (m *Memberlist) rawSendMsgPacket(a Address, node *Node, msg []byte) error {
- if a.Name == "" && m.config.RequireNodeNames {
- return errNodeNamesAreRequired
- }
-
- // Check if we have compression enabled
- if m.config.EnableCompression {
- buf, err := compressPayload(msg)
- if err != nil {
- m.logger.Printf("[WARN] memberlist: Failed to compress payload: %v", err)
- } else {
- // Only use compression if it reduced the size
- if buf.Len() < len(msg) {
- msg = buf.Bytes()
- }
- }
- }
-
- // Try to look up the destination node. Note this will only work if the
- // bare ip address is used as the node name, which is not guaranteed.
- if node == nil {
- toAddr, _, err := net.SplitHostPort(a.Addr)
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to parse address %q: %v", a.Addr, err)
- return err
- }
- m.nodeLock.RLock()
- nodeState, ok := m.nodeMap[toAddr]
- m.nodeLock.RUnlock()
- if ok {
- node = &nodeState.Node
- }
- }
-
- // Add a CRC to the end of the payload if the recipient understands
- // ProtocolVersion >= 5
- if node != nil && node.PMax >= 5 {
- crc := crc32.ChecksumIEEE(msg)
- header := make([]byte, 5, 5+len(msg))
- header[0] = byte(hasCrcMsg)
- binary.BigEndian.PutUint32(header[1:], crc)
- msg = append(header, msg...)
- }
-
- // Check if we have encryption enabled
- if m.config.EncryptionEnabled() && m.config.GossipVerifyOutgoing {
- // Encrypt the payload
- var buf bytes.Buffer
- primaryKey := m.config.Keyring.GetPrimaryKey()
- err := encryptPayload(m.encryptionVersion(), primaryKey, msg, nil, &buf)
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Encryption of message failed: %v", err)
- return err
- }
- msg = buf.Bytes()
- }
-
- metrics.IncrCounter([]string{"memberlist", "udp", "sent"}, float32(len(msg)))
- _, err := m.transport.WriteToAddress(msg, a)
- return err
-}
-
-// rawSendMsgStream is used to stream a message to another host without
-// modification, other than applying compression and encryption if enabled.
-func (m *Memberlist) rawSendMsgStream(conn net.Conn, sendBuf []byte) error {
- // Check if compression is enabled
- if m.config.EnableCompression {
- compBuf, err := compressPayload(sendBuf)
- if err != nil {
- m.logger.Printf("[ERROR] memberlist: Failed to compress payload: %v", err)
- } else {
- sendBuf = compBuf.Bytes()
- }
- }
-
- // Check if encryption is enabled
- if m.config.EncryptionEnabled() && m.config.GossipVerifyOutgoing {
- crypt, err := m.encryptLocalState(sendBuf)
- if err != nil {
- m.logger.Printf("[ERROR] memberlist: Failed to encrypt local state: %v", err)
- return err
- }
- sendBuf = crypt
- }
-
- // Write out the entire send buffer
- metrics.IncrCounter([]string{"memberlist", "tcp", "sent"}, float32(len(sendBuf)))
-
- if n, err := conn.Write(sendBuf); err != nil {
- return err
- } else if n != len(sendBuf) {
- return fmt.Errorf("only %d of %d bytes written", n, len(sendBuf))
- }
-
- return nil
-}
-
-// sendUserMsg is used to stream a user message to another host.
-func (m *Memberlist) sendUserMsg(a Address, sendBuf []byte) error {
- if a.Name == "" && m.config.RequireNodeNames {
- return errNodeNamesAreRequired
- }
-
- conn, err := m.transport.DialAddressTimeout(a, m.config.TCPTimeout)
- if err != nil {
- return err
- }
- defer conn.Close()
-
- bufConn := bytes.NewBuffer(nil)
- if err := bufConn.WriteByte(byte(userMsg)); err != nil {
- return err
- }
-
- header := userMsgHeader{UserMsgLen: len(sendBuf)}
- hd := codec.MsgpackHandle{}
- enc := codec.NewEncoder(bufConn, &hd)
- if err := enc.Encode(&header); err != nil {
- return err
- }
- if _, err := bufConn.Write(sendBuf); err != nil {
- return err
- }
- return m.rawSendMsgStream(conn, bufConn.Bytes())
-}
-
-// sendAndReceiveState is used to initiate a push/pull over a stream with a
-// remote host.
-func (m *Memberlist) sendAndReceiveState(a Address, join bool) ([]pushNodeState, []byte, error) {
- if a.Name == "" && m.config.RequireNodeNames {
- return nil, nil, errNodeNamesAreRequired
- }
-
- // Attempt to connect
- conn, err := m.transport.DialAddressTimeout(a, m.config.TCPTimeout)
- if err != nil {
- return nil, nil, err
- }
- defer conn.Close()
- m.logger.Printf("[DEBUG] memberlist: Initiating push/pull sync with: %s %s", a.Name, conn.RemoteAddr())
- metrics.IncrCounter([]string{"memberlist", "tcp", "connect"}, 1)
-
- // Send our state
- if err := m.sendLocalState(conn, join); err != nil {
- return nil, nil, err
- }
-
- conn.SetDeadline(time.Now().Add(m.config.TCPTimeout))
- msgType, bufConn, dec, err := m.readStream(conn)
- if err != nil {
- return nil, nil, err
- }
-
- if msgType == errMsg {
- var resp errResp
- if err := dec.Decode(&resp); err != nil {
- return nil, nil, err
- }
- return nil, nil, fmt.Errorf("remote error: %v", resp.Error)
- }
-
- // Quit if not push/pull
- if msgType != pushPullMsg {
- err := fmt.Errorf("received invalid msgType (%d), expected pushPullMsg (%d) %s", msgType, pushPullMsg, LogConn(conn))
- return nil, nil, err
- }
-
- // Read remote state
- _, remoteNodes, userState, err := m.readRemoteState(bufConn, dec)
- return remoteNodes, userState, err
-}
-
-// sendLocalState is invoked to send our local state over a stream connection.
-func (m *Memberlist) sendLocalState(conn net.Conn, join bool) error {
- // Setup a deadline
- conn.SetDeadline(time.Now().Add(m.config.TCPTimeout))
-
- // Prepare the local node state
- m.nodeLock.RLock()
- localNodes := make([]pushNodeState, len(m.nodes))
- for idx, n := range m.nodes {
- localNodes[idx].Name = n.Name
- localNodes[idx].Addr = n.Addr
- localNodes[idx].Port = n.Port
- localNodes[idx].Incarnation = n.Incarnation
- localNodes[idx].State = n.State
- localNodes[idx].Meta = n.Meta
- localNodes[idx].Vsn = []uint8{
- n.PMin, n.PMax, n.PCur,
- n.DMin, n.DMax, n.DCur,
- }
- }
- m.nodeLock.RUnlock()
-
- // Get the delegate state
- var userData []byte
- if m.config.Delegate != nil {
- userData = m.config.Delegate.LocalState(join)
- }
-
- // Create a bytes buffer writer
- bufConn := bytes.NewBuffer(nil)
-
- // Send our node state
- header := pushPullHeader{Nodes: len(localNodes), UserStateLen: len(userData), Join: join}
- hd := codec.MsgpackHandle{}
- enc := codec.NewEncoder(bufConn, &hd)
-
- // Begin state push
- if _, err := bufConn.Write([]byte{byte(pushPullMsg)}); err != nil {
- return err
- }
-
- if err := enc.Encode(&header); err != nil {
- return err
- }
- for i := 0; i < header.Nodes; i++ {
- if err := enc.Encode(&localNodes[i]); err != nil {
- return err
- }
- }
-
- // Write the user state as well
- if userData != nil {
- if _, err := bufConn.Write(userData); err != nil {
- return err
- }
- }
-
- // Get the send buffer
- return m.rawSendMsgStream(conn, bufConn.Bytes())
-}
-
-// encryptLocalState is used to help encrypt local state before sending
-func (m *Memberlist) encryptLocalState(sendBuf []byte) ([]byte, error) {
- var buf bytes.Buffer
-
- // Write the encryptMsg byte
- buf.WriteByte(byte(encryptMsg))
-
- // Write the size of the message
- sizeBuf := make([]byte, 4)
- encVsn := m.encryptionVersion()
- encLen := encryptedLength(encVsn, len(sendBuf))
- binary.BigEndian.PutUint32(sizeBuf, uint32(encLen))
- buf.Write(sizeBuf)
-
- // Write the encrypted cipher text to the buffer
- key := m.config.Keyring.GetPrimaryKey()
- err := encryptPayload(encVsn, key, sendBuf, buf.Bytes()[:5], &buf)
- if err != nil {
- return nil, err
- }
- return buf.Bytes(), nil
-}
-
-// decryptRemoteState is used to help decrypt the remote state
-func (m *Memberlist) decryptRemoteState(bufConn io.Reader) ([]byte, error) {
- // Read in enough to determine message length
- cipherText := bytes.NewBuffer(nil)
- cipherText.WriteByte(byte(encryptMsg))
- _, err := io.CopyN(cipherText, bufConn, 4)
- if err != nil {
- return nil, err
- }
-
- // Ensure we aren't asked to download too much. This is to guard against
- // an attack vector where a huge amount of state is sent
- moreBytes := binary.BigEndian.Uint32(cipherText.Bytes()[1:5])
- if moreBytes > maxPushStateBytes {
- return nil, fmt.Errorf("Remote node state is larger than limit (%d)", moreBytes)
- }
-
- // Read in the rest of the payload
- _, err = io.CopyN(cipherText, bufConn, int64(moreBytes))
- if err != nil {
- return nil, err
- }
-
- // Decrypt the cipherText
- dataBytes := cipherText.Bytes()[:5]
- cipherBytes := cipherText.Bytes()[5:]
-
- // Decrypt the payload
- keys := m.config.Keyring.GetKeys()
- return decryptPayload(keys, cipherBytes, dataBytes)
-}
-
-// readStream is used to read from a stream connection, decrypting and
-// decompressing the stream if necessary.
-func (m *Memberlist) readStream(conn net.Conn) (messageType, io.Reader, *codec.Decoder, error) {
- // Created a buffered reader
- var bufConn io.Reader = bufio.NewReader(conn)
-
- // Read the message type
- buf := [1]byte{0}
- if _, err := bufConn.Read(buf[:]); err != nil {
- return 0, nil, nil, err
- }
- msgType := messageType(buf[0])
-
- // Check if the message is encrypted
- if msgType == encryptMsg {
- if !m.config.EncryptionEnabled() {
- return 0, nil, nil,
- fmt.Errorf("Remote state is encrypted and encryption is not configured")
- }
-
- plain, err := m.decryptRemoteState(bufConn)
- if err != nil {
- return 0, nil, nil, err
- }
-
- // Reset message type and bufConn
- msgType = messageType(plain[0])
- bufConn = bytes.NewReader(plain[1:])
- } else if m.config.EncryptionEnabled() && m.config.GossipVerifyIncoming {
- return 0, nil, nil,
- fmt.Errorf("Encryption is configured but remote state is not encrypted")
- }
-
- // Get the msgPack decoders
- hd := codec.MsgpackHandle{}
- dec := codec.NewDecoder(bufConn, &hd)
-
- // Check if we have a compressed message
- if msgType == compressMsg {
- var c compress
- if err := dec.Decode(&c); err != nil {
- return 0, nil, nil, err
- }
- decomp, err := decompressBuffer(&c)
- if err != nil {
- return 0, nil, nil, err
- }
-
- // Reset the message type
- msgType = messageType(decomp[0])
-
- // Create a new bufConn
- bufConn = bytes.NewReader(decomp[1:])
-
- // Create a new decoder
- dec = codec.NewDecoder(bufConn, &hd)
- }
-
- return msgType, bufConn, dec, nil
-}
-
-// readRemoteState is used to read the remote state from a connection
-func (m *Memberlist) readRemoteState(bufConn io.Reader, dec *codec.Decoder) (bool, []pushNodeState, []byte, error) {
- // Read the push/pull header
- var header pushPullHeader
- if err := dec.Decode(&header); err != nil {
- return false, nil, nil, err
- }
-
- // Allocate space for the transfer
- remoteNodes := make([]pushNodeState, header.Nodes)
-
- // Try to decode all the states
- for i := 0; i < header.Nodes; i++ {
- if err := dec.Decode(&remoteNodes[i]); err != nil {
- return false, nil, nil, err
- }
- }
-
- // Read the remote user state into a buffer
- var userBuf []byte
- if header.UserStateLen > 0 {
- userBuf = make([]byte, header.UserStateLen)
- bytes, err := io.ReadAtLeast(bufConn, userBuf, header.UserStateLen)
- if err == nil && bytes != header.UserStateLen {
- err = fmt.Errorf(
- "Failed to read full user state (%d / %d)",
- bytes, header.UserStateLen)
- }
- if err != nil {
- return false, nil, nil, err
- }
- }
-
- // For proto versions < 2, there is no port provided. Mask old
- // behavior by using the configured port
- for idx := range remoteNodes {
- if m.ProtocolVersion() < 2 || remoteNodes[idx].Port == 0 {
- remoteNodes[idx].Port = uint16(m.config.BindPort)
- }
- }
-
- return header.Join, remoteNodes, userBuf, nil
-}
-
-// mergeRemoteState is used to merge the remote state with our local state
-func (m *Memberlist) mergeRemoteState(join bool, remoteNodes []pushNodeState, userBuf []byte) error {
- if err := m.verifyProtocol(remoteNodes); err != nil {
- return err
- }
-
- // Invoke the merge delegate if any
- if join && m.config.Merge != nil {
- nodes := make([]*Node, len(remoteNodes))
- for idx, n := range remoteNodes {
- nodes[idx] = &Node{
- Name: n.Name,
- Addr: n.Addr,
- Port: n.Port,
- Meta: n.Meta,
- State: n.State,
- PMin: n.Vsn[0],
- PMax: n.Vsn[1],
- PCur: n.Vsn[2],
- DMin: n.Vsn[3],
- DMax: n.Vsn[4],
- DCur: n.Vsn[5],
- }
- }
- if err := m.config.Merge.NotifyMerge(nodes); err != nil {
- return err
- }
- }
-
- // Merge the membership state
- m.mergeState(remoteNodes)
-
- // Invoke the delegate for user state
- if userBuf != nil && m.config.Delegate != nil {
- m.config.Delegate.MergeRemoteState(userBuf, join)
- }
- return nil
-}
-
-// readUserMsg is used to decode a userMsg from a stream.
-func (m *Memberlist) readUserMsg(bufConn io.Reader, dec *codec.Decoder) error {
- // Read the user message header
- var header userMsgHeader
- if err := dec.Decode(&header); err != nil {
- return err
- }
-
- // Read the user message into a buffer
- var userBuf []byte
- if header.UserMsgLen > 0 {
- userBuf = make([]byte, header.UserMsgLen)
- bytes, err := io.ReadAtLeast(bufConn, userBuf, header.UserMsgLen)
- if err == nil && bytes != header.UserMsgLen {
- err = fmt.Errorf(
- "Failed to read full user message (%d / %d)",
- bytes, header.UserMsgLen)
- }
- if err != nil {
- return err
- }
-
- d := m.config.Delegate
- if d != nil {
- d.NotifyMsg(userBuf)
- }
- }
-
- return nil
-}
-
-// sendPingAndWaitForAck makes a stream connection to the given address, sends
-// a ping, and waits for an ack. All of this is done as a series of blocking
-// operations, given the deadline. The bool return parameter is true if we
-// we able to round trip a ping to the other node.
-func (m *Memberlist) sendPingAndWaitForAck(a Address, ping ping, deadline time.Time) (bool, error) {
- if a.Name == "" && m.config.RequireNodeNames {
- return false, errNodeNamesAreRequired
- }
-
- conn, err := m.transport.DialAddressTimeout(a, deadline.Sub(time.Now()))
- if err != nil {
- // If the node is actually dead we expect this to fail, so we
- // shouldn't spam the logs with it. After this point, errors
- // with the connection are real, unexpected errors and should
- // get propagated up.
- return false, nil
- }
- defer conn.Close()
- conn.SetDeadline(deadline)
-
- out, err := encode(pingMsg, &ping)
- if err != nil {
- return false, err
- }
-
- if err = m.rawSendMsgStream(conn, out.Bytes()); err != nil {
- return false, err
- }
-
- msgType, _, dec, err := m.readStream(conn)
- if err != nil {
- return false, err
- }
-
- if msgType != ackRespMsg {
- return false, fmt.Errorf("Unexpected msgType (%d) from ping %s", msgType, LogConn(conn))
- }
-
- var ack ackResp
- if err = dec.Decode(&ack); err != nil {
- return false, err
- }
-
- if ack.SeqNo != ping.SeqNo {
- return false, fmt.Errorf("Sequence number from ack (%d) doesn't match ping (%d)", ack.SeqNo, ping.SeqNo)
- }
-
- return true, nil
-}
diff --git a/toolkit/memberlist/net_conn_interface.go b/toolkit/memberlist/net_conn_interface.go
deleted file mode 100644
index 648015de..00000000
--- a/toolkit/memberlist/net_conn_interface.go
+++ /dev/null
@@ -1,68 +0,0 @@
-package memberlist
-
-import (
- "net"
- "time"
-)
-
-//go:generate mockgen -destination ./mock/mock_net_conn_interface.go -package mock -source=./net_conn_interface.go
-
-// Conn is a generic stream-oriented network connection.
-//
-// Multiple goroutines may invoke methods on a Conn simultaneously.
-type Conn interface {
- // Read reads data from the connection.
- // Read can be made to time out and return an error after a fixed
- // time limit; see SetDeadline and SetReadDeadline.
- Read(b []byte) (n int, err error)
-
- // Write writes data to the connection.
- // Write can be made to time out and return an error after a fixed
- // time limit; see SetDeadline and SetWriteDeadline.
- Write(b []byte) (n int, err error)
-
- // Close closes the connection.
- // Any blocked Read or Write operations will be unblocked and return errors.
- Close() error
-
- // LocalAddr returns the local network address.
- LocalAddr() net.Addr
-
- // RemoteAddr returns the remote network address.
- RemoteAddr() net.Addr
-
- // SetDeadline sets the read and write deadlines associated
- // with the connection. It is equivalent to calling both
- // SetReadDeadline and SetWriteDeadline.
- //
- // A deadline is an absolute time after which I/O operations
- // fail instead of blocking. The deadline applies to all future
- // and pending I/O, not just the immediately following call to
- // Read or Write. After a deadline has been exceeded, the
- // connection can be refreshed by setting a deadline in the future.
- //
- // If the deadline is exceeded a call to Read or Write or to other
- // I/O methods will return an error that wraps os.ErrDeadlineExceeded.
- // This can be tested using errors.Is(err, os.ErrDeadlineExceeded).
- // The error's Timeout method will return true, but note that there
- // are other possible errors for which the Timeout method will
- // return true even if the deadline has not been exceeded.
- //
- // An idle timeout can be implemented by repeatedly extending
- // the deadline after successful Read or Write calls.
- //
- // A zero value for t means I/O operations will not time out.
- SetDeadline(t time.Time) error
-
- // SetReadDeadline sets the deadline for future Read calls
- // and any currently-blocked Read call.
- // A zero value for t means Read will not time out.
- SetReadDeadline(t time.Time) error
-
- // SetWriteDeadline sets the deadline for future Write calls
- // and any currently-blocked Write call.
- // Even if write times out, it may return n > 0, indicating that
- // some of the data was successfully written.
- // A zero value for t means Write will not time out.
- SetWriteDeadline(t time.Time) error
-}
diff --git a/toolkit/memberlist/net_test.go b/toolkit/memberlist/net_test.go
deleted file mode 100644
index 8f94197b..00000000
--- a/toolkit/memberlist/net_test.go
+++ /dev/null
@@ -1,910 +0,0 @@
-package memberlist
-
-import (
- "bytes"
- "encoding/binary"
- "fmt"
- "io"
- "log"
- "net"
- "os"
- "reflect"
- "strconv"
- "strings"
- "testing"
- "time"
-
- "github.com/hashicorp/go-msgpack/codec"
- "github.com/stretchr/testify/require"
-)
-
-// As a regression we left this test very low-level and network-ey, even after
-// we abstracted the transport. We added some basic network-free transport tests
-// in transport_test.go to prove that we didn't hard code some network stuff
-// outside of NetTransport.
-
-func TestHandleCompoundPing(t *testing.T) {
- //m := GetMemberlist(t, func(c *Config) {
- // c.EnableCompression = false
- //})
- //defer m.Shutdown()
- //
- //udp := listenUDP(t)
- //defer udp.Close()
- //
- //udpAddr := udp.LocalAddr().(*net.UDPAddr)
-
- // Encode a ping
- p1 := ping{
- SeqNo: 42,
- SourceAddr: "127.0.0.1",
- SourcePort: 56199,
- SourceNode: "test",
- }
- buf, err := encode(pingMsg, p1)
- fmt.Println(buf.Bytes())
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- msg := []byte{133, 165, 83, 101, 113, 78, 111, 42, 164, 78, 111, 100, 101, 160, 170, 83, 111, 117, 114, 99, 101, 65, 100, 100, 114, 169, 49, 50, 55, 46, 48, 46, 48, 46, 49, 170, 83, 111, 117, 114, 99, 101, 80, 111, 114, 116, 205, 219, 135, 170, 83, 111, 117, 114, 99, 101, 78, 111, 100, 101, 164, 116, 101, 115, 116}
- var p ping
- decode(msg, &p)
- fmt.Printf("%+v\n", p)
-
- //// Make a compound message
- //compound := makeCompoundMessage([][]byte{buf.Bytes(), buf.Bytes(), buf.Bytes()})
- //
- //// Send compound version
- //addr := &net.UDPAddr{IP: net.ParseIP(m.config.BindAddr), Port: m.config.BindPort}
- //_, err = udp.WriteTo(compound.Bytes(), addr)
- //if err != nil {
- // t.Fatalf("unexpected err %s", err)
- //}
- //
- //// Wait for responses
- //doneCh := make(chan struct{}, 1)
- //go func() {
- // select {
- // case <-doneCh:
- // case <-time.After(2 * time.Second):
- // panic("timeout")
- // }
- //}()
- //
- //for i := 0; i < 3; i++ {
- // in := make([]byte, 1500)
- // n, _, err := udp.ReadFrom(in)
- // if err != nil {
- // t.Fatalf("unexpected err %s", err)
- // }
- // in = in[0:n]
- //
- // msgType := messageType(in[0])
- // if msgType != ackRespMsg {
- // t.Fatalf("bad response %v", in)
- // }
- //
- // var ack ackResp
- // if err := decode(in[1:], &ack); err != nil {
- // t.Fatalf("unexpected err %s", err)
- // }
- //
- // if ack.SeqNo != 42 {
- // t.Fatalf("bad sequence no")
- // }
- //}
- //
- //doneCh <- struct{}{}
-}
-
-func TestHandlePing(t *testing.T) {
- m := GetMemberlist(t, func(c *Config) {
- c.EnableCompression = false
- })
- defer m.Shutdown()
-
- udp := listenUDP(t)
- defer udp.Close()
-
- udpAddr := udp.LocalAddr().(*net.UDPAddr)
-
- // Encode a ping
- ping := ping{
- SeqNo: 42,
- SourceAddr: udpAddr.IP.String(),
- SourcePort: uint16(udpAddr.Port),
- SourceNode: "test",
- }
- buf, err := encode(pingMsg, ping)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- // Send
- addr := &net.UDPAddr{IP: net.ParseIP(m.config.BindAddr), Port: m.config.BindPort}
- _, err = udp.WriteTo(buf.Bytes(), addr)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- // Wait for response
- doneCh := make(chan struct{}, 1)
- go func() {
- select {
- case <-doneCh:
- case <-time.After(120 * time.Second):
- panic("timeout")
- }
- }()
-
- in := make([]byte, 1500)
- n, _, err := udp.ReadFrom(in)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- in = in[0:n]
-
- msgType := messageType(in[0])
- if msgType != ackRespMsg {
- t.Fatalf("bad response %v", in)
- }
-
- var ack ackResp
- if err := decode(in[1:], &ack); err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- if ack.SeqNo != 42 {
- t.Fatalf("bad sequence no")
- }
-
- doneCh <- struct{}{}
-}
-
-func TestHandlePing_WrongNode(t *testing.T) {
- m := GetMemberlist(t, func(c *Config) {
- c.EnableCompression = false
- })
- defer m.Shutdown()
-
- udp := listenUDP(t)
- defer udp.Close()
-
- udpAddr := udp.LocalAddr().(*net.UDPAddr)
-
- // Encode a ping, wrong node!
- ping := ping{
- SeqNo: 42,
- Node: m.config.Name + "-bad",
- SourceAddr: udpAddr.IP.String(),
- SourcePort: uint16(udpAddr.Port),
- SourceNode: "test",
- }
- buf, err := encode(pingMsg, ping)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- // Send
- addr := &net.UDPAddr{IP: net.ParseIP(m.config.BindAddr), Port: m.config.BindPort}
- _, err = udp.WriteTo(buf.Bytes(), addr)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- // Wait for response
- udp.SetDeadline(time.Now().Add(50 * time.Millisecond))
- in := make([]byte, 1500)
- _, _, err = udp.ReadFrom(in)
-
- // Should get an i/o timeout
- if err == nil {
- t.Fatalf("expected err %s", err)
- }
-}
-
-func TestHandleIndirectPing(t *testing.T) {
- m := GetMemberlist(t, func(c *Config) {
- c.EnableCompression = false
- })
- defer m.Shutdown()
-
- udp := listenUDP(t)
- defer udp.Close()
-
- udpAddr := udp.LocalAddr().(*net.UDPAddr)
-
- // Encode an indirect ping
- ind := indirectPingReq{
- SeqNo: 100,
- Target: m.config.BindAddr,
- Port: uint16(m.config.BindPort),
- Node: m.config.Name,
- SourceAddr: udpAddr.IP.String(),
- SourcePort: uint16(udpAddr.Port),
- SourceNode: "test",
- }
- buf, err := encode(indirectPingMsg, &ind)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- // Send
- addr := &net.UDPAddr{IP: net.ParseIP(m.config.BindAddr), Port: m.config.BindPort}
- _, err = udp.WriteTo(buf.Bytes(), addr)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- // Wait for response
- doneCh := make(chan struct{}, 1)
- go func() {
- select {
- case <-doneCh:
- case <-time.After(2 * time.Second):
- panic("timeout")
- }
- }()
-
- in := make([]byte, 1500)
- n, _, err := udp.ReadFrom(in)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- in = in[0:n]
-
- msgType := messageType(in[0])
- if msgType != ackRespMsg {
- t.Fatalf("bad response %v", in)
- }
-
- var ack ackResp
- if err := decode(in[1:], &ack); err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- if ack.SeqNo != 100 {
- t.Fatalf("bad sequence no")
- }
-
- doneCh <- struct{}{}
-}
-
-func TestTCPPing(t *testing.T) {
- var tcp *net.TCPListener
- var tcpAddr *net.TCPAddr
- for port := 60000; port < 61000; port++ {
- tcpAddr = &net.TCPAddr{IP: net.ParseIP("127.0.0.1"), Port: port}
- tcpLn, err := net.ListenTCP("tcp", tcpAddr)
- if err == nil {
- tcp = tcpLn
- break
- }
- }
- if tcp == nil {
- t.Fatalf("no tcp listener")
- }
-
- tcpAddr2 := Address{Addr: tcpAddr.String(), Name: "test"}
-
- // Note that tcp gets closed in the last test, so we avoid a deferred
- // Close() call here.
-
- m := GetMemberlist(t, nil)
- defer m.Shutdown()
-
- pingTimeout := m.config.ProbeInterval
- pingTimeMax := m.config.ProbeInterval + 10*time.Millisecond
-
- // Do a normal round trip.
- pingOut := ping{SeqNo: 23, Node: "mongo"}
- go func() {
- tcp.SetDeadline(time.Now().Add(pingTimeMax))
- conn, err := tcp.AcceptTCP()
- if err != nil {
- t.Fatalf("failed to connect: %s", err)
- }
- defer conn.Close()
-
- msgType, _, dec, err := m.readStream(conn)
- if err != nil {
- t.Fatalf("failed to read ping: %s", err)
- }
-
- if msgType != pingMsg {
- t.Fatalf("expecting ping, got message type (%d)", msgType)
- }
-
- var pingIn ping
- if err := dec.Decode(&pingIn); err != nil {
- t.Fatalf("failed to decode ping: %s", err)
- }
-
- if pingIn.SeqNo != pingOut.SeqNo {
- t.Fatalf("sequence number isn't correct (%d) vs (%d)", pingIn.SeqNo, pingOut.SeqNo)
- }
-
- if pingIn.Node != pingOut.Node {
- t.Fatalf("node name isn't correct (%s) vs (%s)", pingIn.Node, pingOut.Node)
- }
-
- ack := ackResp{pingIn.SeqNo, nil}
- out, err := encode(ackRespMsg, &ack)
- if err != nil {
- t.Fatalf("failed to encode ack: %s", err)
- }
-
- err = m.rawSendMsgStream(conn, out.Bytes())
- if err != nil {
- t.Fatalf("failed to send ack: %s", err)
- }
- }()
- deadline := time.Now().Add(pingTimeout)
- didContact, err := m.sendPingAndWaitForAck(tcpAddr2, pingOut, deadline)
- if err != nil {
- t.Fatalf("error trying to ping: %s", err)
- }
- if !didContact {
- t.Fatalf("expected successful ping")
- }
-
- // Make sure a mis-matched sequence number is caught.
- go func() {
- tcp.SetDeadline(time.Now().Add(pingTimeMax))
- conn, err := tcp.AcceptTCP()
- if err != nil {
- t.Fatalf("failed to connect: %s", err)
- }
- defer conn.Close()
-
- _, _, dec, err := m.readStream(conn)
- if err != nil {
- t.Fatalf("failed to read ping: %s", err)
- }
-
- var pingIn ping
- if err := dec.Decode(&pingIn); err != nil {
- t.Fatalf("failed to decode ping: %s", err)
- }
-
- ack := ackResp{pingIn.SeqNo + 1, nil}
- out, err := encode(ackRespMsg, &ack)
- if err != nil {
- t.Fatalf("failed to encode ack: %s", err)
- }
-
- err = m.rawSendMsgStream(conn, out.Bytes())
- if err != nil {
- t.Fatalf("failed to send ack: %s", err)
- }
- }()
- deadline = time.Now().Add(pingTimeout)
- didContact, err = m.sendPingAndWaitForAck(tcpAddr2, pingOut, deadline)
- if err == nil || !strings.Contains(err.Error(), "Sequence number") {
- t.Fatalf("expected an error from mis-matched sequence number")
- }
- if didContact {
- t.Fatalf("expected failed ping")
- }
-
- // Make sure an unexpected message type is handled gracefully.
- go func() {
- tcp.SetDeadline(time.Now().Add(pingTimeMax))
- conn, err := tcp.AcceptTCP()
- if err != nil {
- t.Fatalf("failed to connect: %s", err)
- }
- defer conn.Close()
-
- _, _, _, err = m.readStream(conn)
- if err != nil {
- t.Fatalf("failed to read ping: %s", err)
- }
-
- bogus := indirectPingReq{}
- out, err := encode(indirectPingMsg, &bogus)
- if err != nil {
- t.Fatalf("failed to encode bogus msg: %s", err)
- }
-
- err = m.rawSendMsgStream(conn, out.Bytes())
- if err != nil {
- t.Fatalf("failed to send bogus msg: %s", err)
- }
- }()
- deadline = time.Now().Add(pingTimeout)
- didContact, err = m.sendPingAndWaitForAck(tcpAddr2, pingOut, deadline)
- if err == nil || !strings.Contains(err.Error(), "Unexpected msgType") {
- t.Fatalf("expected an error from bogus message")
- }
- if didContact {
- t.Fatalf("expected failed ping")
- }
-
- // Make sure failed I/O respects the deadline. In this case we try the
- // common case of the receiving node being totally down.
- tcp.Close()
- deadline = time.Now().Add(pingTimeout)
- startPing := time.Now()
- didContact, err = m.sendPingAndWaitForAck(tcpAddr2, pingOut, deadline)
- pingTime := time.Now().Sub(startPing)
- if err != nil {
- t.Fatalf("expected no error during ping on closed socket, got: %s", err)
- }
- if didContact {
- t.Fatalf("expected failed ping")
- }
- if pingTime > pingTimeMax {
- t.Fatalf("took too long to fail ping, %9.6f", pingTime.Seconds())
- }
-}
-
-func TestTCPPushPull(t *testing.T) {
- m := GetMemberlist(t, nil)
- defer m.Shutdown()
-
- m.nodes = append(m.nodes, &nodeState{
- Node: Node{
- Name: "Test 0",
- Addr: m.config.BindAddr,
- Port: uint16(m.config.BindPort),
- },
- Incarnation: 0,
- State: StateSuspect,
- StateChange: time.Now().Add(-1 * time.Second),
- })
-
- addr := net.JoinHostPort(m.config.BindAddr, strconv.Itoa(m.config.BindPort))
- conn, err := net.Dial("tcp", addr)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- defer conn.Close()
-
- localNodes := make([]pushNodeState, 3)
- localNodes[0].Name = "Test 0"
- localNodes[0].Addr = m.config.BindAddr
- localNodes[0].Port = uint16(m.config.BindPort)
- localNodes[0].Incarnation = 1
- localNodes[0].State = StateAlive
- localNodes[1].Name = "Test 1"
- localNodes[1].Addr = m.config.BindAddr
- localNodes[1].Port = uint16(m.config.BindPort)
- localNodes[1].Incarnation = 1
- localNodes[1].State = StateAlive
- localNodes[2].Name = "Test 2"
- localNodes[2].Addr = m.config.BindAddr
- localNodes[2].Port = uint16(m.config.BindPort)
- localNodes[2].Incarnation = 1
- localNodes[2].State = StateAlive
-
- // Send our node state
- header := pushPullHeader{Nodes: 3}
- hd := codec.MsgpackHandle{}
- enc := codec.NewEncoder(conn, &hd)
-
- // Send the push/pull indicator
- conn.Write([]byte{byte(pushPullMsg)})
-
- if err := enc.Encode(&header); err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- for i := 0; i < header.Nodes; i++ {
- if err := enc.Encode(&localNodes[i]); err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- }
-
- // Read the message type
- var msgType messageType
- if err := binary.Read(conn, binary.BigEndian, &msgType); err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- var bufConn io.Reader = conn
- msghd := codec.MsgpackHandle{}
- dec := codec.NewDecoder(bufConn, &msghd)
-
- // Check if we have a compressed message
- if msgType == compressMsg {
- var c compress
- if err := dec.Decode(&c); err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- decomp, err := decompressBuffer(&c)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- // Reset the message type
- msgType = messageType(decomp[0])
-
- // Create a new bufConn
- bufConn = bytes.NewReader(decomp[1:])
-
- // Create a new decoder
- dec = codec.NewDecoder(bufConn, &hd)
- }
-
- // Quit if not push/pull
- if msgType != pushPullMsg {
- t.Fatalf("bad message type")
- }
-
- if err := dec.Decode(&header); err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- // Allocate space for the transfer
- remoteNodes := make([]pushNodeState, header.Nodes)
-
- // Try to decode all the states
- for i := 0; i < header.Nodes; i++ {
- if err := dec.Decode(&remoteNodes[i]); err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- }
-
- if len(remoteNodes) != 1 {
- t.Fatalf("bad response")
- }
-
- n := &remoteNodes[0]
- if n.Name != "Test 0" {
- t.Fatalf("bad name")
- }
- if n.Incarnation != 0 {
- t.Fatal("bad incarnation")
- }
- if n.State != StateSuspect {
- t.Fatal("bad state")
- }
-}
-
-func TestSendMsg_Piggyback(t *testing.T) {
- m := GetMemberlist(t, nil)
- defer m.Shutdown()
-
- // Add a message to be broadcast
- a := alive{
- Incarnation: 10,
- Node: "rand",
- Addr: "127.0.0.255",
- Meta: nil,
- Vsn: []uint8{
- ProtocolVersionMin, ProtocolVersionMax, ProtocolVersionMin,
- 1, 1, 1,
- },
- }
- m.encodeAndBroadcast("rand", aliveMsg, &a)
-
- udp := listenUDP(t)
- defer udp.Close()
-
- udpAddr := udp.LocalAddr().(*net.UDPAddr)
-
- // Encode a ping
- ping := ping{
- SeqNo: 42,
- SourceAddr: udpAddr.IP.String(),
- SourcePort: uint16(udpAddr.Port),
- SourceNode: "test",
- }
- buf, err := encode(pingMsg, ping)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- // Send
- addr := &net.UDPAddr{IP: net.ParseIP(m.config.BindAddr), Port: m.config.BindPort}
- _, err = udp.WriteTo(buf.Bytes(), addr)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- // Wait for response
- doneCh := make(chan struct{}, 1)
- go func() {
- select {
- case <-doneCh:
- case <-time.After(2 * time.Second):
- panic("timeout")
- }
- }()
-
- in := make([]byte, 1500)
- n, _, err := udp.ReadFrom(in)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- in = in[0:n]
-
- msgType := messageType(in[0])
- if msgType != compoundMsg {
- t.Fatalf("bad response %v", in)
- }
-
- // get the parts
- trunc, parts, err := decodeCompoundMessage(in[1:])
- if trunc != 0 {
- t.Fatalf("unexpected truncation")
- }
- if len(parts) != 2 {
- t.Fatalf("unexpected parts %v", parts)
- }
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- var ack ackResp
- if err := decode(parts[0][1:], &ack); err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- if ack.SeqNo != 42 {
- t.Fatalf("bad sequence no")
- }
-
- var aliveout alive
- if err := decode(parts[1][1:], &aliveout); err != nil {
- t.Fatalf("unexpected err %s", err)
- }
-
- if aliveout.Node != "rand" || aliveout.Incarnation != 10 {
- t.Fatalf("bad mesg")
- }
-
- doneCh <- struct{}{}
-}
-
-func TestEncryptDecryptState(t *testing.T) {
- state := []byte("this is our internal state...")
- config := &Config{
- SecretKey: []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15},
- ProtocolVersion: ProtocolVersionMax,
- }
-
- m, err := Create(config)
- if err != nil {
- t.Fatalf("err: %s", err)
- }
- defer m.Shutdown()
-
- crypt, err := m.encryptLocalState(state)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- // Create reader, seek past the type byte
- buf := bytes.NewReader(crypt)
- buf.Seek(1, 0)
-
- plain, err := m.decryptRemoteState(buf)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- if !reflect.DeepEqual(state, plain) {
- t.Fatalf("Decrypt failed: %v", plain)
- }
-}
-
-func testConfigNet(tb testing.TB, network byte) *Config {
- tb.Helper()
-
- config := DefaultLANConfig()
- config.BindAddr = getBindAddrNet(network).String()
- config.Name = config.BindAddr
- config.BindPort = 0 // choose free port
- config.RequireNodeNames = true
- config.Logger = log.New(os.Stderr, config.Name, log.LstdFlags)
- return config
-}
-
-func testConfig(tb testing.TB) *Config {
- return testConfigNet(tb, 0)
-}
-
-func GetMemberlist(tb testing.TB, f func(c *Config)) *Memberlist {
- c := testConfig(tb)
- c.BindPort = 0
- if f != nil {
- f(c)
- }
-
- m, err := NewMemberlist(c)
- require.NoError(tb, err)
- return m
-}
-
-func TestRawSendUdp_CRC(t *testing.T) {
- m := GetMemberlist(t, func(c *Config) {
- c.EnableCompression = false
- })
- defer m.Shutdown()
-
- udp := listenUDP(t)
- defer udp.Close()
-
- a := Address{
- Addr: udp.LocalAddr().String(),
- Name: "test",
- }
-
- // Pass a nil node with no nodes registered, should result in no checksum
- payload := []byte{3, 3, 3, 3}
- m.rawSendMsgPacket(a, nil, payload)
-
- in := make([]byte, 1500)
- n, _, err := udp.ReadFrom(in)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- in = in[0:n]
-
- if len(in) != 4 {
- t.Fatalf("bad: %v", in)
- }
-
- // Pass a non-nil node with PMax >= 5, should result in a checksum
- m.rawSendMsgPacket(a, &Node{PMax: 5}, payload)
-
- in = make([]byte, 1500)
- n, _, err = udp.ReadFrom(in)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- in = in[0:n]
-
- if len(in) != 9 {
- t.Fatalf("bad: %v", in)
- }
-
- // Register a node with PMax >= 5 to be looked up, should result in a checksum
- m.nodeMap["127.0.0.1"] = &nodeState{
- Node: Node{PMax: 5},
- }
- m.rawSendMsgPacket(a, nil, payload)
-
- in = make([]byte, 1500)
- n, _, err = udp.ReadFrom(in)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- in = in[0:n]
-
- if len(in) != 9 {
- t.Fatalf("bad: %v", in)
- }
-}
-
-func TestIngestPacket_CRC(t *testing.T) {
- m := GetMemberlist(t, func(c *Config) {
- c.EnableCompression = false
- })
- defer m.Shutdown()
-
- udp := listenUDP(t)
- defer udp.Close()
-
- a := Address{
- Addr: udp.LocalAddr().String(),
- Name: "test",
- }
-
- // Get a message with a checksum
- payload := []byte{3, 3, 3, 3}
- m.rawSendMsgPacket(a, &Node{PMax: 5}, payload)
-
- in := make([]byte, 1500)
- n, _, err := udp.ReadFrom(in)
- if err != nil {
- t.Fatalf("unexpected err %s", err)
- }
- in = in[0:n]
-
- if len(in) != 9 {
- t.Fatalf("bad: %v", in)
- }
-
- // Corrupt the checksum
- in[1] <<= 1
-
- logs := &bytes.Buffer{}
- logger := log.New(logs, "", 0)
- m.logger = logger
- m.ingestPacket(in, udp.LocalAddr(), time.Now())
-
- if !strings.Contains(logs.String(), "invalid checksum") {
- t.Fatalf("bad: %s", logs.String())
- }
-}
-
-func TestIngestPacket_ExportedFunc_EmptyMessage(t *testing.T) {
- m := GetMemberlist(t, func(c *Config) {
- c.EnableCompression = false
- })
- defer m.Shutdown()
-
- udp := listenUDP(t)
- defer udp.Close()
-
- emptyConn := &emptyReadNetConn{}
-
- logs := &bytes.Buffer{}
- logger := log.New(logs, "", 0)
- m.logger = logger
-
- type ingestionAwareTransport interface {
- IngestPacket(conn net.Conn, addr net.Addr, now time.Time, shouldClose bool) error
- }
-
- err := m.transport.(ingestionAwareTransport).IngestPacket(emptyConn, udp.LocalAddr(), time.Now(), true)
- require.Error(t, err)
- require.Contains(t, err.Error(), "packet too short")
-}
-
-type emptyReadNetConn struct {
- net.Conn
-}
-
-func (c *emptyReadNetConn) Read(b []byte) (n int, err error) {
- return 0, io.EOF
-}
-
-func (c *emptyReadNetConn) Close() error {
- return nil
-}
-
-func TestGossip_MismatchedKeys(t *testing.T) {
- // Create two agents with different gossip keys
- c1 := testConfig(t)
- c1.SecretKey = []byte("4W6DGn2VQVqDEceOdmuRTQ==")
- c1.BindPort = 56188
- c1.AdvertisePort = 56188
- m1, err := Create(c1)
- require.NoError(t, err)
- defer m1.Shutdown()
-
- c2 := testConfig(t)
- c2.BindPort = 56189
- c2.AdvertisePort = 56189
- c2.SecretKey = []byte("XhX/w702/JKKK7/7OtM9Ww==")
-
- m2, err := Create(c2)
- require.NoError(t, err)
- defer m2.Shutdown()
-
- // Make sure we get this error on the joining side
- m1JoinUrl := fmt.Sprintf("%s/%s:%d", m1.config.Name, m1.advertiseAddr, m1.advertisePort)
- _, err = m2.Join([]string{m1JoinUrl})
- if err == nil || !strings.Contains(err.Error(), "No installed keys could decrypt the message") {
- t.Fatalf("bad: %s", err)
- }
-}
-
-func listenUDP(t *testing.T) *net.UDPConn {
- var udp *net.UDPConn
- for port := 56199; port < 60000; port++ {
- udpAddr := fmt.Sprintf("127.0.0.1:%d", port)
- udpLn, err := net.ListenPacket("udp", udpAddr)
- if err == nil {
- udp = udpLn.(*net.UDPConn)
- break
- }
- }
- if udp == nil {
- t.Fatalf("no udp listener")
- }
- return udp
-}
-
-func TestHandleCommand(t *testing.T) {
- var buf bytes.Buffer
- m := Memberlist{
- logger: log.New(&buf, "", 0),
- }
- m.handleCommand(nil, &net.TCPAddr{Port: 12345}, time.Now())
- require.Contains(t, buf.String(), "missing message type byte")
-}
diff --git a/toolkit/memberlist/net_transport.go b/toolkit/memberlist/net_transport.go
deleted file mode 100644
index 63caa64d..00000000
--- a/toolkit/memberlist/net_transport.go
+++ /dev/null
@@ -1,353 +0,0 @@
-package memberlist
-
-import (
- "bytes"
- "fmt"
- "io"
- "log"
- "net"
- "sync"
- "sync/atomic"
- "time"
-
- "github.com/armon/go-metrics"
- sockaddr "github.com/hashicorp/go-sockaddr"
-)
-
-const (
- // udpPacketBufSize is used to buffer incoming packets during read
- // operations.
- udpPacketBufSize = 65536
-
- // udpRecvBufSize is a large buffer size that we attempt to set UDP
- // sockets to in order to handle a large volume of messages.
- udpRecvBufSize = 2 * 1024 * 1024
-)
-
-// NetTransportConfig is used to configure a net transport.
-type NetTransportConfig struct {
- // BindAddrs is a list of addresses to bind to for both TCP and UDP
- // communications.
- BindAddrs []string
-
- // BindPort is the port to listen on, for each address above.
- BindPort int
-
- // Logger is a logger for operator messages.
- Logger *log.Logger
-}
-
-// NetTransport is a Transport implementation that uses connectionless UDP for
-// packet operations, and ad-hoc TCP connections for stream operations.
-type NetTransport struct {
- config *NetTransportConfig
- packetCh chan *Packet
- streamCh chan net.Conn
- logger *log.Logger
- wg sync.WaitGroup
- tcpListeners []*net.TCPListener
- udpListeners []*net.UDPConn
- shutdown int32
-}
-
-var _ NodeAwareTransport = (*NetTransport)(nil)
-
-// NewNetTransport returns a net transport with the given configuration. On
-// success all the network listeners will be created and listening.
-func NewNetTransport(config *NetTransportConfig) (*NetTransport, error) {
- // If we reject the empty list outright we can assume that there's at
- // least one listener of each type later during operation.
- if len(config.BindAddrs) == 0 {
- return nil, fmt.Errorf("At least one bind address is required")
- }
-
- // Build out the new transport.
- var ok bool
- t := NetTransport{
- config: config,
- packetCh: make(chan *Packet),
- streamCh: make(chan net.Conn),
- logger: config.Logger,
- }
-
- // Clean up listeners if there's an error.
- defer func() {
- if !ok {
- t.Shutdown()
- }
- }()
-
- // Build all the TCP and UDP listeners.
- port := config.BindPort
- for _, addr := range config.BindAddrs {
- ip := net.ParseIP(addr)
-
- tcpAddr := &net.TCPAddr{IP: ip, Port: port}
- tcpLn, err := net.ListenTCP("tcp", tcpAddr)
- if err != nil {
- return nil, fmt.Errorf("Failed to start TCP listener on %q port %d: %v", addr, port, err)
- }
- t.tcpListeners = append(t.tcpListeners, tcpLn)
-
- // If the config port given was zero, use the first TCP listener
- // to pick an available port and then apply that to everything
- // else.
- if port == 0 {
- port = tcpLn.Addr().(*net.TCPAddr).Port
- }
-
- udpAddr := &net.UDPAddr{IP: ip, Port: port}
- udpLn, err := net.ListenUDP("udp", udpAddr)
- if err != nil {
- return nil, fmt.Errorf("Failed to start UDP listener on %q port %d: %v", addr, port, err)
- }
- if err := setUDPRecvBuf(udpLn); err != nil {
- return nil, fmt.Errorf("Failed to resize UDP buffer: %v", err)
- }
- t.udpListeners = append(t.udpListeners, udpLn)
- }
-
- // Fire them up now that we've been able to create them all.
- for i := 0; i < len(config.BindAddrs); i++ {
- t.wg.Add(2)
- go t.tcpListen(t.tcpListeners[i])
- go t.udpListen(t.udpListeners[i])
- }
-
- ok = true
- return &t, nil
-}
-
-// GetAutoBindPort returns the bind port that was automatically given by the
-// kernel, if a bind port of 0 was given.
-func (t *NetTransport) GetAutoBindPort() int {
- // We made sure there's at least one TCP listener, and that one's
- // port was applied to all the others for the dynamic bind case.
- return t.tcpListeners[0].Addr().(*net.TCPAddr).Port
-}
-
-// See Transport.
-func (t *NetTransport) FinalAdvertiseAddr(ip string, port int) (string, int, error) {
- var advertiseAddr string
- var advertisePort int
- if ip != "" {
- advertiseAddr = ip
- advertisePort = port
- } else {
- if t.config.BindAddrs[0] == "0.0.0.0" {
- // Otherwise, if we're not bound to a specific IP, let's
- // use a suitable private IP address.
- var err error
- ip, err = sockaddr.GetPrivateIP()
- if err != nil {
- return "", 0, fmt.Errorf("failed to get interface addresses: %v", err)
- }
- if ip == "" {
- return "", 0, fmt.Errorf("no private IP address found, and explicit IP not provided")
- }
- advertiseAddr = ip
- } else {
- // Use the IP that we're bound to, based on the first
- // TCP listener, which we already ensure is there.
- advertiseAddr = t.tcpListeners[0].Addr().(*net.TCPAddr).IP.String()
- }
-
- // Use the port we are bound to.
- advertisePort = t.GetAutoBindPort()
- }
-
- return advertiseAddr, advertisePort, nil
-}
-
-// See Transport.
-func (t *NetTransport) WriteTo(b []byte, addr string) (time.Time, error) {
- a := Address{Addr: addr, Name: ""}
- return t.WriteToAddress(b, a)
-}
-
-// See NodeAwareTransport.
-func (t *NetTransport) WriteToAddress(b []byte, a Address) (time.Time, error) {
- addr := a.Addr
-
- udpAddr, err := net.ResolveUDPAddr("udp", addr)
- if err != nil {
- return time.Time{}, err
- }
-
- // We made sure there's at least one UDP listener, so just use the
- // packet sending interface on the first one. Take the time after the
- // write call comes back, which will underestimate the time a little,
- // but help account for any delays before the write occurs.
- _, err = t.udpListeners[0].WriteTo(b, udpAddr)
- return time.Now(), err
-}
-
-// See Transport.
-func (t *NetTransport) PacketCh() <-chan *Packet {
- return t.packetCh
-}
-
-// See IngestionAwareTransport.
-func (t *NetTransport) IngestPacket(conn net.Conn, addr net.Addr, now time.Time, shouldClose bool) error {
- if shouldClose {
- defer conn.Close()
- }
-
- // Copy everything from the stream into packet buffer.
- var buf bytes.Buffer
- if _, err := io.Copy(&buf, conn); err != nil {
- return fmt.Errorf("failed to read packet: %v", err)
- }
-
- // Check the length - it needs to have at least one byte to be a proper
- // message. This is checked elsewhere for writes coming in directly from
- // the UDP socket.
- if n := buf.Len(); n < 1 {
- return fmt.Errorf("packet too short (%d bytes) %s", n, LogAddress(addr))
- }
-
- // Inject the packet.
- t.packetCh <- &Packet{
- Buf: buf.Bytes(),
- From: addr,
- Timestamp: now,
- }
- return nil
-}
-
-// See Transport.
-func (t *NetTransport) DialTimeout(addr string, timeout time.Duration) (net.Conn, error) {
- a := Address{Addr: addr, Name: ""}
- return t.DialAddressTimeout(a, timeout)
-}
-
-// See NodeAwareTransport.
-func (t *NetTransport) DialAddressTimeout(a Address, timeout time.Duration) (net.Conn, error) {
- addr := a.Addr
-
- dialer := net.Dialer{Timeout: timeout}
- return dialer.Dial("tcp", addr)
-}
-
-// See Transport.
-func (t *NetTransport) StreamCh() <-chan net.Conn {
- return t.streamCh
-}
-
-// See IngestionAwareTransport.
-func (t *NetTransport) IngestStream(conn net.Conn) error {
- t.streamCh <- conn
- return nil
-}
-
-// See Transport.
-func (t *NetTransport) Shutdown() error {
- // This will avoid log spam about errors when we shut down.
- atomic.StoreInt32(&t.shutdown, 1)
-
- // Rip through all the connections and shut them down.
- for _, conn := range t.tcpListeners {
- conn.Close()
- }
- for _, conn := range t.udpListeners {
- conn.Close()
- }
-
- // Block until all the listener threads have died.
- t.wg.Wait()
- return nil
-}
-
-// tcpListen is a long running goroutine that accepts incoming TCP connections
-// and hands them off to the stream channel.
-func (t *NetTransport) tcpListen(tcpLn *net.TCPListener) {
- defer t.wg.Done()
-
- // baseDelay is the initial delay after an AcceptTCP() error before attempting again
- const baseDelay = 5 * time.Millisecond
-
- // maxDelay is the maximum delay after an AcceptTCP() error before attempting again.
- // In the case that tcpListen() is error-looping, it will delay the shutdown check.
- // Therefore, changes to maxDelay may have an effect on the latency of shutdown.
- const maxDelay = 1 * time.Second
-
- var loopDelay time.Duration
- for {
- conn, err := tcpLn.AcceptTCP()
- if err != nil {
- if s := atomic.LoadInt32(&t.shutdown); s == 1 {
- break
- }
-
- if loopDelay == 0 {
- loopDelay = baseDelay
- } else {
- loopDelay *= 2
- }
-
- if loopDelay > maxDelay {
- loopDelay = maxDelay
- }
-
- t.logger.Printf("[ERR] memberlist: Error accepting TCP connection: %v", err)
- time.Sleep(loopDelay)
- continue
- }
- // No error, reset loop delay
- loopDelay = 0
-
- t.streamCh <- conn
- }
-}
-
-// udpListen is a long running goroutine that accepts incoming UDP packets and
-// hands them off to the packet channel.
-func (t *NetTransport) udpListen(udpLn *net.UDPConn) {
- defer t.wg.Done()
- for {
- // Do a blocking read into a fresh buffer. Grab a time stamp as
- // close as possible to the I/O.
- buf := make([]byte, udpPacketBufSize)
- n, addr, err := udpLn.ReadFrom(buf)
- ts := time.Now()
- if err != nil {
- if s := atomic.LoadInt32(&t.shutdown); s == 1 {
- break
- }
-
- t.logger.Printf("[ERR] memberlist: Error reading UDP packet: %v", err)
- continue
- }
-
- // Check the length - it needs to have at least one byte to be a
- // proper message.
- if n < 1 {
- t.logger.Printf("[ERR] memberlist: UDP packet too short (%d bytes) %s",
- len(buf), LogAddress(addr))
- continue
- }
-
- // Ingest the packet.
- metrics.IncrCounter([]string{"memberlist", "udp", "received"}, float32(n))
- t.packetCh <- &Packet{
- Buf: buf[:n],
- From: addr,
- Timestamp: ts,
- }
- }
-}
-
-// setUDPRecvBuf is used to resize the UDP receive window. The function
-// attempts to set the read buffer to `udpRecvBuf` but backs off until
-// the read buffer can be set.
-func setUDPRecvBuf(c *net.UDPConn) error {
- size := udpRecvBufSize
- var err error
- for size > 0 {
- if err = c.SetReadBuffer(size); err == nil {
- return nil
- }
- size = size / 2
- }
- return err
-}
diff --git a/toolkit/memberlist/ping_delegate.go b/toolkit/memberlist/ping_delegate.go
deleted file mode 100644
index 1566c8b3..00000000
--- a/toolkit/memberlist/ping_delegate.go
+++ /dev/null
@@ -1,14 +0,0 @@
-package memberlist
-
-import "time"
-
-// PingDelegate is used to notify an observer how long it took for a ping message to
-// complete a round trip. It can also be used for writing arbitrary byte slices
-// into ack messages. Note that in order to be meaningful for RTT estimates, this
-// delegate does not apply to indirect pings, nor fallback pings sent over TCP.
-type PingDelegate interface {
- // AckPayload is invoked when an ack is being sent; the returned bytes will be appended to the ack
- AckPayload() []byte
- // NotifyPing is invoked when an ack for a ping is received
- NotifyPingComplete(other *Node, rtt time.Duration, payload []byte)
-}
diff --git a/toolkit/memberlist/queue.go b/toolkit/memberlist/queue.go
deleted file mode 100644
index 9d7876d9..00000000
--- a/toolkit/memberlist/queue.go
+++ /dev/null
@@ -1,429 +0,0 @@
-package memberlist
-
-import (
- "math"
- "sync"
-
- "github.com/google/btree"
-)
-
-//go:generate mockgen -destination ./mock/mock_queue.go -package mock -source=./queue.go
-
-// TransmitLimitedQueue is used to queue messages to broadcast to
-// the cluster (via gossip) but limits the number of transmits per
-// message. It also prioritizes messages with lower transmit counts
-// (hence newer messages).
-type TransmitLimitedQueue struct {
- // NumNodes returns the number of nodes in the cluster. This is
- // used to determine the retransmit count, which is calculated
- // based on the log of this.
- NumNodes func() int
-
- // RetransmitMult is the multiplier used to determine the maximum
- // number of retransmissions attempted.
- RetransmitMult int
- RetransmitMultGetter func() int
-
- mu sync.Mutex
- tq *btree.BTree // stores *limitedBroadcast as btree.Item
- tm map[string]*limitedBroadcast
- idGen int64
-}
-
-type limitedBroadcast struct {
- transmits int // btree-key[0]: Number of transmissions attempted.
- msgLen int64 // btree-key[1]: copied from len(b.Message())
- id int64 // btree-key[2]: unique incrementing id stamped at submission time
- b Broadcast
-
- name string // set if Broadcast is a NamedBroadcast
-}
-
-// Less tests whether the current item is less than the given argument.
-//
-// This must provide a strict weak ordering.
-// If !a.Less(b) && !b.Less(a), we treat this to mean a == b (i.e. we can only
-// hold one of either a or b in the tree).
-//
-// default ordering is
-// - [transmits=0, ..., transmits=inf]
-// - [transmits=0:len=999, ..., transmits=0:len=2, ...]
-// - [transmits=0:len=999,id=999, ..., transmits=0:len=999:id=1, ...]
-func (b *limitedBroadcast) Less(than btree.Item) bool {
- o := than.(*limitedBroadcast)
- if b.transmits < o.transmits {
- return true
- } else if b.transmits > o.transmits {
- return false
- }
- if b.msgLen > o.msgLen {
- return true
- } else if b.msgLen < o.msgLen {
- return false
- }
- return b.id > o.id
-}
-
-// for testing; emits in transmit order if reverse=false
-func (q *TransmitLimitedQueue) orderedView(reverse bool) []*limitedBroadcast {
- q.mu.Lock()
- defer q.mu.Unlock()
-
- out := make([]*limitedBroadcast, 0, q.lenLocked())
- q.walkReadOnlyLocked(reverse, func(cur *limitedBroadcast) bool {
- out = append(out, cur)
- return true
- })
-
- return out
-}
-
-// walkReadOnlyLocked calls f for each item in the queue traversing it in
-// natural order (by Less) when reverse=false and the opposite when true. You
-// must hold the mutex.
-//
-// This method panics if you attempt to mutate the item during traversal. The
-// underlying btree should also not be mutated during traversal.
-func (q *TransmitLimitedQueue) walkReadOnlyLocked(reverse bool, f func(*limitedBroadcast) bool) {
- if q.lenLocked() == 0 {
- return
- }
-
- iter := func(item btree.Item) bool {
- cur := item.(*limitedBroadcast)
-
- prevTransmits := cur.transmits
- prevMsgLen := cur.msgLen
- prevID := cur.id
-
- keepGoing := f(cur)
-
- if prevTransmits != cur.transmits || prevMsgLen != cur.msgLen || prevID != cur.id {
- panic("edited queue while walking read only")
- }
-
- return keepGoing
- }
-
- if reverse {
- q.tq.Descend(iter) // end with transmit 0
- } else {
- q.tq.Ascend(iter) // start with transmit 0
- }
-}
-
-// Broadcast is something that can be broadcasted via gossip to
-// the memberlist cluster.
-type Broadcast interface {
- // Invalidates checks if enqueuing the current broadcast
- // invalidates a previous broadcast
- Invalidates(b Broadcast) bool
-
- // Returns a byte form of the message
- Message() []byte
-
- // Finished is invoked when the message will no longer
- // be broadcast, either due to invalidation or to the
- // transmit limit being reached
- Finished()
-}
-
-// NamedBroadcast is an optional extension of the Broadcast interface that
-// gives each message a unique string name, and that is used to optimize
-//
-// You shoud ensure that Invalidates() checks the same uniqueness as the
-// example below:
-//
-// func (b *foo) Invalidates(other Broadcast) bool {
-// nb, ok := other.(NamedBroadcast)
-// if !ok {
-// return false
-// }
-// return b.Name() == nb.Name()
-// }
-//
-// Invalidates() isn't currently used for NamedBroadcasts, but that may change
-// in the future.
-type NamedBroadcast interface {
- Broadcast
- // The unique identity of this broadcast message.
- Name() string
-}
-
-// UniqueBroadcast is an optional interface that indicates that each message is
-// intrinsically unique and there is no need to scan the broadcast queue for
-// duplicates.
-//
-// You should ensure that Invalidates() always returns false if implementing
-// this interface. Invalidates() isn't currently used for UniqueBroadcasts, but
-// that may change in the future.
-type UniqueBroadcast interface {
- Broadcast
- // UniqueBroadcast is just a marker method for this interface.
- UniqueBroadcast()
-}
-
-// QueueBroadcast is used to enqueue a broadcast
-func (q *TransmitLimitedQueue) QueueBroadcast(b Broadcast) {
- q.queueBroadcast(b, 0)
-}
-
-// lazyInit initializes internal data structures the first time they are
-// needed. You must already hold the mutex.
-func (q *TransmitLimitedQueue) lazyInit() {
- if q.tq == nil {
- q.tq = btree.New(32)
- }
- if q.tm == nil {
- q.tm = make(map[string]*limitedBroadcast)
- }
-}
-
-// queueBroadcast is like QueueBroadcast but you can use a nonzero value for
-// the initial transmit tier assigned to the message. This is meant to be used
-// for unit testing.
-func (q *TransmitLimitedQueue) queueBroadcast(b Broadcast, initialTransmits int) {
- q.mu.Lock()
- defer q.mu.Unlock()
-
- q.lazyInit()
-
- if q.idGen == math.MaxInt64 {
- // it's super duper unlikely to wrap around within the retransmit limit
- q.idGen = 1
- } else {
- q.idGen++
- }
- id := q.idGen
-
- lb := &limitedBroadcast{
- transmits: initialTransmits,
- msgLen: int64(len(b.Message())),
- id: id,
- b: b,
- }
- unique := false
- if nb, ok := b.(NamedBroadcast); ok {
- lb.name = nb.Name()
- } else if _, ok := b.(UniqueBroadcast); ok {
- unique = true
- }
-
- // Check if this message invalidates another.
- if lb.name != "" {
- if old, ok := q.tm[lb.name]; ok {
- old.b.Finished()
- q.deleteItem(old)
- }
- } else if !unique {
- // Slow path, hopefully nothing hot hits this.
- var remove []*limitedBroadcast
- q.tq.Ascend(func(item btree.Item) bool {
- cur := item.(*limitedBroadcast)
-
- // Special Broadcasts can only invalidate each other.
- switch cur.b.(type) {
- case NamedBroadcast:
- // noop
- case UniqueBroadcast:
- // noop
- default:
- if b.Invalidates(cur.b) {
- cur.b.Finished()
- remove = append(remove, cur)
- }
- }
- return true
- })
- for _, cur := range remove {
- q.deleteItem(cur)
- }
- }
-
- // Append to the relevant queue.
- q.addItem(lb)
-}
-
-// deleteItem removes the given item from the overall datastructure. You
-// must already hold the mutex.
-func (q *TransmitLimitedQueue) deleteItem(cur *limitedBroadcast) {
- _ = q.tq.Delete(cur)
- if cur.name != "" {
- delete(q.tm, cur.name)
- }
-
- if q.tq.Len() == 0 {
- // At idle there's no reason to let the id generator keep going
- // indefinitely.
- q.idGen = 0
- }
-}
-
-// addItem adds the given item into the overall datastructure. You must already
-// hold the mutex.
-func (q *TransmitLimitedQueue) addItem(cur *limitedBroadcast) {
- _ = q.tq.ReplaceOrInsert(cur)
- if cur.name != "" {
- q.tm[cur.name] = cur
- }
-}
-
-// getTransmitRange returns a pair of min/max values for transmit values
-// represented by the current queue contents. Both values represent actual
-// transmit values on the interval [0, len). You must already hold the mutex.
-func (q *TransmitLimitedQueue) getTransmitRange() (minTransmit, maxTransmit int) {
- if q.lenLocked() == 0 {
- return 0, 0
- }
- minItem, maxItem := q.tq.Min(), q.tq.Max()
- if minItem == nil || maxItem == nil {
- return 0, 0
- }
-
- min := minItem.(*limitedBroadcast).transmits
- max := maxItem.(*limitedBroadcast).transmits
-
- return min, max
-}
-
-// GetBroadcasts is used to get a number of broadcasts, up to a byte limit
-// and applying a per-message overhead as provided.
-func (q *TransmitLimitedQueue) GetBroadcasts(overhead, limit int) [][]byte {
- q.mu.Lock()
- defer q.mu.Unlock()
-
- // Fast path the default case
- if q.lenLocked() == 0 {
- return nil
- }
-
- mult := q.RetransmitMult
- if q.RetransmitMultGetter != nil {
- mult = q.RetransmitMultGetter()
- }
- transmitLimit := retransmitLimit(mult, q.NumNodes())
-
- var (
- bytesUsed int
- toSend [][]byte
- reinsert []*limitedBroadcast
- )
-
- // Visit fresher items first, but only look at stuff that will fit.
- // We'll go tier by tier, grabbing the largest items first.
- minTr, maxTr := q.getTransmitRange()
- for transmits := minTr; transmits <= maxTr; /*do not advance automatically*/ {
- free := int64(limit - bytesUsed - overhead)
- if free <= 0 {
- break // bail out early
- }
-
- // Search for the least element on a given tier (by transmit count) as
- // defined in the limitedBroadcast.Less function that will fit into our
- // remaining space.
- greaterOrEqual := &limitedBroadcast{
- transmits: transmits,
- msgLen: free,
- id: math.MaxInt64,
- }
- lessThan := &limitedBroadcast{
- transmits: transmits + 1,
- msgLen: math.MaxInt64,
- id: math.MaxInt64,
- }
- var keep *limitedBroadcast
- q.tq.AscendRange(greaterOrEqual, lessThan, func(item btree.Item) bool {
- cur := item.(*limitedBroadcast)
- // Check if this is within our limits
- if int64(len(cur.b.Message())) > free {
- // If this happens it's a bug in the datastructure or
- // surrounding use doing something like having len(Message())
- // change over time. There's enough going on here that it's
- // probably sane to just skip it and move on for now.
- return true
- }
- keep = cur
- return false
- })
- if keep == nil {
- // No more items of an appropriate size in the tier.
- transmits++
- continue
- }
-
- msg := keep.b.Message()
-
- // Add to slice to send
- bytesUsed += overhead + len(msg)
- toSend = append(toSend, msg)
-
- // Check if we should stop transmission
- q.deleteItem(keep)
- if keep.transmits+1 >= transmitLimit {
- keep.b.Finished()
- } else {
- // We need to bump this item down to another transmit tier, but
- // because it would be in the same direction that we're walking the
- // tiers, we will have to delay the reinsertion until we are
- // finished our search. Otherwise we'll possibly re-add the message
- // when we ascend to the next tier.
- keep.transmits++
- reinsert = append(reinsert, keep)
- }
- }
-
- for _, cur := range reinsert {
- q.addItem(cur)
- }
-
- return toSend
-}
-
-// NumQueued returns the number of queued messages
-func (q *TransmitLimitedQueue) NumQueued() int {
- q.mu.Lock()
- defer q.mu.Unlock()
- return q.lenLocked()
-}
-
-// lenLocked returns the length of the overall queue datastructure. You must
-// hold the mutex.
-func (q *TransmitLimitedQueue) lenLocked() int {
- if q.tq == nil {
- return 0
- }
- return q.tq.Len()
-}
-
-// Reset clears all the queued messages. Should only be used for tests.
-func (q *TransmitLimitedQueue) Reset() {
- q.mu.Lock()
- defer q.mu.Unlock()
-
- q.walkReadOnlyLocked(false, func(cur *limitedBroadcast) bool {
- cur.b.Finished()
- return true
- })
-
- q.tq = nil
- q.tm = nil
- q.idGen = 0
-}
-
-// Prune will retain the maxRetain latest messages, and the rest
-// will be discarded. This can be used to prevent unbounded queue sizes
-func (q *TransmitLimitedQueue) Prune(maxRetain int) {
- q.mu.Lock()
- defer q.mu.Unlock()
-
- // Do nothing if queue size is less than the limit
- for q.tq.Len() > maxRetain {
- item := q.tq.Max()
- if item == nil {
- break
- }
- cur := item.(*limitedBroadcast)
- cur.b.Finished()
- q.deleteItem(cur)
- }
-}
diff --git a/toolkit/memberlist/queue_test.go b/toolkit/memberlist/queue_test.go
deleted file mode 100644
index dcf3f245..00000000
--- a/toolkit/memberlist/queue_test.go
+++ /dev/null
@@ -1,228 +0,0 @@
-package memberlist
-
-import (
- "testing"
-
- "github.com/google/btree"
- "github.com/stretchr/testify/require"
-)
-
-func TestLimitedBroadcastLess(t *testing.T) {
- cases := []struct {
- Name string
- A *limitedBroadcast // lesser
- B *limitedBroadcast
- }{
- {
- "diff-transmits",
- &limitedBroadcast{transmits: 0, msgLen: 10, id: 100},
- &limitedBroadcast{transmits: 1, msgLen: 10, id: 100},
- },
- {
- "same-transmits--diff-len",
- &limitedBroadcast{transmits: 0, msgLen: 12, id: 100},
- &limitedBroadcast{transmits: 0, msgLen: 10, id: 100},
- },
- {
- "same-transmits--same-len--diff-id",
- &limitedBroadcast{transmits: 0, msgLen: 12, id: 100},
- &limitedBroadcast{transmits: 0, msgLen: 12, id: 90},
- },
- }
-
- for _, c := range cases {
- t.Run(c.Name, func(t *testing.T) {
- a, b := c.A, c.B
-
- require.True(t, a.Less(b))
-
- tree := btree.New(32)
-
- tree.ReplaceOrInsert(b)
- tree.ReplaceOrInsert(a)
-
- min := tree.Min().(*limitedBroadcast)
- require.Equal(t, a.transmits, min.transmits)
- require.Equal(t, a.msgLen, min.msgLen)
- require.Equal(t, a.id, min.id)
-
- max := tree.Max().(*limitedBroadcast)
- require.Equal(t, b.transmits, max.transmits)
- require.Equal(t, b.msgLen, max.msgLen)
- require.Equal(t, b.id, max.id)
- })
- }
-}
-
-func TestTransmitLimited_Queue(t *testing.T) {
- q := &TransmitLimitedQueue{RetransmitMult: 1, NumNodes: func() int { return 1 }}
- q.QueueBroadcast(&memberlistBroadcast{"test", nil, nil})
- q.QueueBroadcast(&memberlistBroadcast{"foo", nil, nil})
- q.QueueBroadcast(&memberlistBroadcast{"bar", nil, nil})
-
- if q.NumQueued() != 3 {
- t.Fatalf("bad len")
- }
- dump := q.orderedView(true)
- if dump[0].b.(*memberlistBroadcast).node != "test" {
- t.Fatalf("missing test")
- }
- if dump[1].b.(*memberlistBroadcast).node != "foo" {
- t.Fatalf("missing foo")
- }
- if dump[2].b.(*memberlistBroadcast).node != "bar" {
- t.Fatalf("missing bar")
- }
-
- // Should invalidate previous message
- q.QueueBroadcast(&memberlistBroadcast{"test", nil, nil})
-
- if q.NumQueued() != 3 {
- t.Fatalf("bad len")
- }
- dump = q.orderedView(true)
- if dump[0].b.(*memberlistBroadcast).node != "foo" {
- t.Fatalf("missing foo")
- }
- if dump[1].b.(*memberlistBroadcast).node != "bar" {
- t.Fatalf("missing bar")
- }
- if dump[2].b.(*memberlistBroadcast).node != "test" {
- t.Fatalf("missing test")
- }
-}
-
-func TestTransmitLimited_GetBroadcasts(t *testing.T) {
- q := &TransmitLimitedQueue{RetransmitMult: 3, NumNodes: func() int { return 10 }}
-
- // 18 bytes per message
- q.QueueBroadcast(&memberlistBroadcast{"test", []byte("1. this is a test."), nil})
- q.QueueBroadcast(&memberlistBroadcast{"foo", []byte("2. this is a test."), nil})
- q.QueueBroadcast(&memberlistBroadcast{"bar", []byte("3. this is a test."), nil})
- q.QueueBroadcast(&memberlistBroadcast{"baz", []byte("4. this is a test."), nil})
-
- // 2 byte overhead per message, should get all 4 messages
- all := q.GetBroadcasts(2, 80)
- require.Equal(t, 4, len(all), "missing messages: %v", prettyPrintMessages(all))
-
- // 3 byte overhead, should only get 3 messages back
- partial := q.GetBroadcasts(3, 80)
- require.Equal(t, 3, len(partial), "missing messages: %v", prettyPrintMessages(partial))
-}
-
-func TestTransmitLimited_GetBroadcasts_Limit(t *testing.T) {
- q := &TransmitLimitedQueue{RetransmitMult: 1, NumNodes: func() int { return 10 }}
-
- require.Equal(t, int64(0), q.idGen, "the id generator seed starts at zero")
- require.Equal(t, 2, retransmitLimit(q.RetransmitMult, q.NumNodes()), "sanity check transmit limits")
-
- // 18 bytes per message
- q.QueueBroadcast(&memberlistBroadcast{"test", []byte("1. this is a test."), nil})
- q.QueueBroadcast(&memberlistBroadcast{"foo", []byte("2. this is a test."), nil})
- q.QueueBroadcast(&memberlistBroadcast{"bar", []byte("3. this is a test."), nil})
- q.QueueBroadcast(&memberlistBroadcast{"baz", []byte("4. this is a test."), nil})
-
- require.Equal(t, int64(4), q.idGen, "we handed out 4 IDs")
-
- // 3 byte overhead, should only get 3 messages back
- partial1 := q.GetBroadcasts(3, 80)
- require.Equal(t, 3, len(partial1), "missing messages: %v", prettyPrintMessages(partial1))
-
- require.Equal(t, int64(4), q.idGen, "id generator doesn't reset until empty")
-
- partial2 := q.GetBroadcasts(3, 80)
- require.Equal(t, 3, len(partial2), "missing messages: %v", prettyPrintMessages(partial2))
-
- require.Equal(t, int64(4), q.idGen, "id generator doesn't reset until empty")
-
- // Only two not expired
- partial3 := q.GetBroadcasts(3, 80)
- require.Equal(t, 2, len(partial3), "missing messages: %v", prettyPrintMessages(partial3))
-
- require.Equal(t, int64(0), q.idGen, "id generator resets on empty")
-
- // Should get nothing
- partial5 := q.GetBroadcasts(3, 80)
- require.Equal(t, 0, len(partial5), "missing messages: %v", prettyPrintMessages(partial5))
-
- require.Equal(t, int64(0), q.idGen, "id generator resets on empty")
-}
-
-func prettyPrintMessages(msgs [][]byte) []string {
- var out []string
- for _, msg := range msgs {
- out = append(out, "'"+string(msg)+"'")
- }
- return out
-}
-
-func TestTransmitLimited_Prune(t *testing.T) {
- q := &TransmitLimitedQueue{RetransmitMult: 1, NumNodes: func() int { return 10 }}
-
- ch1 := make(chan struct{}, 1)
- ch2 := make(chan struct{}, 1)
-
- // 18 bytes per message
- q.QueueBroadcast(&memberlistBroadcast{"test", []byte("1. this is a test."), ch1})
- q.QueueBroadcast(&memberlistBroadcast{"foo", []byte("2. this is a test."), ch2})
- q.QueueBroadcast(&memberlistBroadcast{"bar", []byte("3. this is a test."), nil})
- q.QueueBroadcast(&memberlistBroadcast{"baz", []byte("4. this is a test."), nil})
-
- // Keep only 2
- q.Prune(2)
-
- require.Equal(t, 2, q.NumQueued())
-
- // Should notify the first two
- select {
- case <-ch1:
- default:
- t.Fatalf("expected invalidation")
- }
- select {
- case <-ch2:
- default:
- t.Fatalf("expected invalidation")
- }
-
- dump := q.orderedView(true)
-
- if dump[0].b.(*memberlistBroadcast).node != "bar" {
- t.Fatalf("missing bar")
- }
- if dump[1].b.(*memberlistBroadcast).node != "baz" {
- t.Fatalf("missing baz")
- }
-}
-
-func TestTransmitLimited_ordering(t *testing.T) {
- q := &TransmitLimitedQueue{RetransmitMult: 1, NumNodes: func() int { return 10 }}
-
- insert := func(name string, transmits int) {
- q.queueBroadcast(&memberlistBroadcast{name, []byte(name), make(chan struct{})}, transmits)
- }
-
- insert("node0", 0)
- insert("node1", 10)
- insert("node2", 3)
- insert("node3", 4)
- insert("node4", 7)
-
- dump := q.orderedView(true)
-
- if dump[0].transmits != 10 {
- t.Fatalf("bad val %v, %d", dump[0].b.(*memberlistBroadcast).node, dump[0].transmits)
- }
- if dump[1].transmits != 7 {
- t.Fatalf("bad val %v, %d", dump[7].b.(*memberlistBroadcast).node, dump[7].transmits)
- }
- if dump[2].transmits != 4 {
- t.Fatalf("bad val %v, %d", dump[2].b.(*memberlistBroadcast).node, dump[2].transmits)
- }
- if dump[3].transmits != 3 {
- t.Fatalf("bad val %v, %d", dump[3].b.(*memberlistBroadcast).node, dump[3].transmits)
- }
- if dump[4].transmits != 0 {
- t.Fatalf("bad val %v, %d", dump[4].b.(*memberlistBroadcast).node, dump[4].transmits)
- }
-}
diff --git a/toolkit/memberlist/security.go b/toolkit/memberlist/security.go
deleted file mode 100644
index 22e87bd5..00000000
--- a/toolkit/memberlist/security.go
+++ /dev/null
@@ -1,199 +0,0 @@
-package memberlist
-
-import (
- "bytes"
- "crypto/aes"
- "crypto/cipher"
- "crypto/rand"
- "fmt"
- "io"
-)
-
-/*
-Encrypted messages are prefixed with an encryptionVersion byte
-that is used for us to be able to properly encode/decode. We
-currently support the following versions:
-
- 0 - AES-GCM 128, using PKCS7 padding
- 1 - AES-GCM 128, no padding. Padding not needed, caused bloat.
-*/
-type encryptionVersion uint8
-
-const (
- minEncryptionVersion encryptionVersion = 0
- maxEncryptionVersion encryptionVersion = 1
-)
-
-const (
- versionSize = 1
- nonceSize = 12
- tagSize = 16
- maxPadOverhead = 16
- blockSize = aes.BlockSize
-)
-
-// pkcs7encode is used to pad a byte buffer to a specific block size using
-// the PKCS7 algorithm. "Ignores" some bytes to compensate for IV
-func pkcs7encode(buf *bytes.Buffer, ignore, blockSize int) {
- n := buf.Len() - ignore
- more := blockSize - (n % blockSize)
- for i := 0; i < more; i++ {
- buf.WriteByte(byte(more))
- }
-}
-
-// pkcs7decode is used to decode a buffer that has been padded
-func pkcs7decode(buf []byte, blockSize int) []byte {
- if len(buf) == 0 {
- panic("Cannot decode a PKCS7 buffer of zero length")
- }
- n := len(buf)
- last := buf[n-1]
- n -= int(last)
- return buf[:n]
-}
-
-// encryptOverhead returns the maximum possible overhead of encryption by version
-func encryptOverhead(vsn encryptionVersion) int {
- switch vsn {
- case 0:
- return 45 // Version: 1, IV: 12, Padding: 16, Tag: 16
- case 1:
- return 29 // Version: 1, IV: 12, Tag: 16
- default:
- panic("unsupported version")
- }
-}
-
-// encryptedLength is used to compute the buffer size needed
-// for a message of given length
-func encryptedLength(vsn encryptionVersion, inp int) int {
- // If we are on version 1, there is no padding
- if vsn >= 1 {
- return versionSize + nonceSize + inp + tagSize
- }
-
- // Determine the padding size
- padding := blockSize - (inp % blockSize)
-
- // Sum the extra parts to get total size
- return versionSize + nonceSize + inp + padding + tagSize
-}
-
-// encryptPayload is used to encrypt a message with a given key.
-// We make use of AES-128 in GCM mode. New byte buffer is the version,
-// nonce, ciphertext and tag
-func encryptPayload(vsn encryptionVersion, key []byte, msg []byte, data []byte, dst *bytes.Buffer) error {
- // Get the AES block cipher
- aesBlock, err := aes.NewCipher(key)
- if err != nil {
- return err
- }
-
- // Get the GCM cipher mode
- gcm, err := cipher.NewGCM(aesBlock)
- if err != nil {
- return err
- }
-
- // Grow the buffer to make room for everything
- offset := dst.Len()
- dst.Grow(encryptedLength(vsn, len(msg)))
-
- // Write the encryption version
- dst.WriteByte(byte(vsn))
-
- // Add a random nonce
- _, err = io.CopyN(dst, rand.Reader, nonceSize)
- if err != nil {
- return err
- }
- afterNonce := dst.Len()
-
- // Ensure we are correctly padded (only version 0)
- if vsn == 0 {
- io.Copy(dst, bytes.NewReader(msg))
- pkcs7encode(dst, offset+versionSize+nonceSize, aes.BlockSize)
- }
-
- // Encrypt message using GCM
- slice := dst.Bytes()[offset:]
- nonce := slice[versionSize : versionSize+nonceSize]
-
- // Message source depends on the encryption version.
- // Version 0 uses padding, version 1 does not
- var src []byte
- if vsn == 0 {
- src = slice[versionSize+nonceSize:]
- } else {
- src = msg
- }
- out := gcm.Seal(nil, nonce, src, data)
-
- // Truncate the plaintext, and write the cipher text
- dst.Truncate(afterNonce)
- dst.Write(out)
- return nil
-}
-
-// decryptMessage performs the actual decryption of ciphertext. This is in its
-// own function to allow it to be called on all keys easily.
-func decryptMessage(key, msg []byte, data []byte) ([]byte, error) {
- // Get the AES block cipher
- aesBlock, err := aes.NewCipher(key)
- if err != nil {
- return nil, err
- }
-
- // Get the GCM cipher mode
- gcm, err := cipher.NewGCM(aesBlock)
- if err != nil {
- return nil, err
- }
-
- // Decrypt the message
- nonce := msg[versionSize : versionSize+nonceSize]
- ciphertext := msg[versionSize+nonceSize:]
- plain, err := gcm.Open(nil, nonce, ciphertext, data)
- if err != nil {
- return nil, err
- }
-
- // Success!
- return plain, nil
-}
-
-// decryptPayload is used to decrypt a message with a given key,
-// and verify it's contents. Any padding will be removed, and a
-// slice to the plaintext is returned. Decryption is done IN PLACE!
-func decryptPayload(keys [][]byte, msg []byte, data []byte) ([]byte, error) {
- // Ensure we have at least one byte
- if len(msg) == 0 {
- return nil, fmt.Errorf("Cannot decrypt empty payload")
- }
-
- // Verify the version
- vsn := encryptionVersion(msg[0])
- if vsn > maxEncryptionVersion {
- return nil, fmt.Errorf("Unsupported encryption version %d", msg[0])
- }
-
- // Ensure the length is sane
- if len(msg) < encryptedLength(vsn, 0) {
- return nil, fmt.Errorf("Payload is too small to decrypt: %d", len(msg))
- }
-
- for _, key := range keys {
- plain, err := decryptMessage(key, msg, data)
- if err == nil {
- // Remove the PKCS7 padding for vsn 0
- if vsn == 0 {
- return pkcs7decode(plain, aes.BlockSize), nil
- } else {
- return plain, nil
- }
- }
- }
-
- return nil, fmt.Errorf("No installed keys could decrypt the message")
-}
diff --git a/toolkit/memberlist/security_test.go b/toolkit/memberlist/security_test.go
deleted file mode 100644
index 15fa4aa8..00000000
--- a/toolkit/memberlist/security_test.go
+++ /dev/null
@@ -1,70 +0,0 @@
-package memberlist
-
-import (
- "bytes"
- "reflect"
- "testing"
-)
-
-func TestPKCS7(t *testing.T) {
- for i := 0; i <= 255; i++ {
- // Make a buffer of size i
- buf := []byte{}
- for j := 0; j < i; j++ {
- buf = append(buf, byte(i))
- }
-
- // Copy to bytes buffer
- inp := bytes.NewBuffer(nil)
- inp.Write(buf)
-
- // Pad this out
- pkcs7encode(inp, 0, 16)
-
- // Unpad
- dec := pkcs7decode(inp.Bytes(), 16)
-
- // Ensure equivilence
- if !reflect.DeepEqual(buf, dec) {
- t.Fatalf("mismatch: %v %v", buf, dec)
- }
- }
-
-}
-
-func TestEncryptDecrypt_V0(t *testing.T) {
- encryptDecryptVersioned(0, t)
-}
-
-func TestEncryptDecrypt_V1(t *testing.T) {
- encryptDecryptVersioned(1, t)
-}
-
-func encryptDecryptVersioned(vsn encryptionVersion, t *testing.T) {
- k1 := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
- plaintext := []byte("this is a plain text message")
- extra := []byte("random data")
-
- var buf bytes.Buffer
- err := encryptPayload(vsn, k1, plaintext, extra, &buf)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- expLen := encryptedLength(vsn, len(plaintext))
- if buf.Len() != expLen {
- t.Fatalf("output length is unexpected %d %d %d", len(plaintext), buf.Len(), expLen)
- }
-
- msg, err := decryptPayload([][]byte{k1}, buf.Bytes(), extra)
- if err != nil {
- t.Fatalf("err: %v", err)
- }
-
- cmp := bytes.Compare(msg, plaintext)
- if cmp != 0 {
- t.Errorf("len %d %v", len(msg), msg)
- t.Errorf("len %d %v", len(plaintext), plaintext)
- t.Fatalf("encrypt/decrypt failed! %d '%s' '%s'", cmp, msg, plaintext)
- }
-}
diff --git a/toolkit/memberlist/state.go b/toolkit/memberlist/state.go
deleted file mode 100644
index 82acf6b6..00000000
--- a/toolkit/memberlist/state.go
+++ /dev/null
@@ -1,1438 +0,0 @@
-package memberlist
-
-import (
- "bytes"
- "fmt"
- "math"
- "math/rand"
- "net"
- "strings"
- "sync/atomic"
- "time"
-
- "github.com/shirou/gopsutil/cpu"
-
- metrics "github.com/armon/go-metrics"
-)
-
-type NodeStateType int
-
-const (
- StateAlive NodeStateType = iota
- StateSuspect
- StateDead
- StateLeft
-)
-
-// Node represents a node in the cluster.
-type Node struct {
- Name string
- Addr string
- Port uint16
- Meta []byte // Metadata from the delegate for this node.
- State NodeStateType // State of the node.
- PMin uint8 // Minimum protocol version this understands
- PMax uint8 // Maximum protocol version this understands
- PCur uint8 // Current version node is speaking
- DMin uint8 // Min protocol version for the delegate to understand
- DMax uint8 // Max protocol version for the delegate to understand
- DCur uint8 // Current version delegate is speaking
- Weight int // node weight for load balancing
-}
-
-// Address returns the host:port form of a node's address, suitable for use
-// with a transport.
-func (n *Node) Address() string {
- return joinHostPort(n.Addr, n.Port)
-}
-
-// FullAddress returns the node name and host:port form of a node's address,
-// suitable for use with a transport.
-func (n *Node) FullAddress() Address {
- return Address{
- Addr: joinHostPort(n.Addr, n.Port),
- Name: n.Name,
- }
-}
-
-// String returns the node name
-func (n *Node) String() string {
- return n.Name
-}
-
-// NodeState is used to manage our state view of another node
-type nodeState struct {
- Node
- Incarnation uint32 // Last known incarnation number
- State NodeStateType // Current state
- StateChange time.Time // Time last state change happened
- Weight int // node weight for load balancing
- WeightAt int64 // UTC timestamp which node weight calculated at
-}
-
-func NewNodeState(node Node, state NodeStateType) *nodeState {
- return &nodeState{Node: node, State: state}
-}
-
-// Address returns the host:port form of a node's address, suitable for use
-// with a transport.
-func (n *nodeState) Address() string {
- return n.Node.Address()
-}
-
-// FullAddress returns the node name and host:port form of a node's address,
-// suitable for use with a transport.
-func (n *nodeState) FullAddress() Address {
- return n.Node.FullAddress()
-}
-
-func (n *nodeState) DeadOrLeft() bool {
- return n.State == StateDead || n.State == StateLeft
-}
-
-// ackHandler is used to register handlers for incoming acks and nacks.
-type ackHandler struct {
- ackFn func([]byte, time.Time)
- nackFn func()
- timer *time.Timer
-}
-
-// NoPingResponseError is used to indicate a 'ping' packet was
-// successfully issued but no response was received
-type NoPingResponseError struct {
- node string
-}
-
-func (f NoPingResponseError) Error() string {
- return fmt.Sprintf("No response from node %s", f.node)
-}
-
-// Schedule is used to ensure the Tick is performed periodically. This
-// function is safe to call multiple times. If the memberlist is already
-// scheduled, then it won't do anything.
-func (m *Memberlist) schedule() {
- m.tickerLock.Lock()
- defer m.tickerLock.Unlock()
-
- // If we already have tickers, then don't do anything, since we're
- // scheduled
- if len(m.tickers) > 0 {
- return
- }
-
- // Create the stop tick channel, a blocking channel. We close this
- // when we should stop the tickers.
- stopCh := make(chan struct{})
-
- // Create a new probeTicker
- if m.config.ProbeInterval > 0 {
- t := time.NewTicker(m.config.ProbeInterval)
- go m.triggerFuncDynamic(func() time.Duration {
- return m.config.ProbeInterval
- }, t, stopCh, m.probe)
- m.tickers = append(m.tickers, t)
- }
-
- // Create a push pull ticker if needed
- if m.config.PushPullInterval > 0 {
- go m.pushPullTrigger(stopCh)
- }
-
- // Create a gossip ticker if needed
- if m.config.GossipInterval > 0 && m.config.GossipNodes > 0 {
- t := time.NewTicker(m.config.GossipInterval)
- go m.triggerFuncDynamic(func() time.Duration {
- return m.config.GossipInterval
- }, t, stopCh, m.gossip)
- m.tickers = append(m.tickers, t)
- }
-
- // Create node weight ticker if needed
- if m.config.WeightInterval > 0 {
- t := time.NewTicker(m.config.WeightInterval)
- go m.triggerFunc(m.config.WeightInterval, t.C, stopCh, m.weight)
- m.tickers = append(m.tickers, t)
- }
-
- // If we made any tickers, then record the stopTick channel for
- // later.
- if len(m.tickers) > 0 {
- m.stopTick = stopCh
- }
-}
-
-// triggerFunc is used to trigger a function call each time a
-// message is received until a stop tick arrives.
-func (m *Memberlist) triggerFunc(stagger time.Duration, C <-chan time.Time, stop <-chan struct{}, f func()) {
- // Use a random stagger to avoid syncronizing
- randStagger := time.Duration(uint64(rand.Int63()) % uint64(stagger))
- select {
- case <-time.After(randStagger):
- case <-stop:
- return
- }
- for {
- select {
- case <-C:
- f()
- case <-stop:
- return
- }
- }
-}
-
-func (m *Memberlist) triggerFuncDynamic(getter func() time.Duration, t *time.Ticker, stop <-chan struct{}, f func()) {
- stagger := getter()
- randStagger := time.Duration(uint64(rand.Int63()) % uint64(stagger))
- select {
- case <-time.After(randStagger):
- case <-stop:
- return
- }
- for {
- select {
- case <-t.C:
- t.Reset(getter())
- f()
- case <-stop:
- return
- }
- }
-}
-
-// pushPullTrigger is used to periodically trigger a push/pull until
-// a stop tick arrives. We don't use triggerFunc since the push/pull
-// timer is dynamically scaled based on cluster size to avoid network
-// saturation
-func (m *Memberlist) pushPullTrigger(stop <-chan struct{}) {
- interval := m.config.PushPullInterval
-
- // Use a random stagger to avoid syncronizing
- randStagger := time.Duration(uint64(rand.Int63()) % uint64(interval))
- select {
- case <-time.After(randStagger):
- case <-stop:
- return
- }
-
- // Tick using a dynamic timer
- for {
- tickTime := pushPullScale(m.config.PushPullInterval, m.estNumNodes())
- select {
- case <-time.After(tickTime):
- m.pushPull()
- case <-stop:
- return
- }
- }
-}
-
-// Deschedule is used to stop the background maintenance. This is safe
-// to call multiple times.
-func (m *Memberlist) deschedule() {
- m.tickerLock.Lock()
- defer m.tickerLock.Unlock()
-
- // If we have no tickers, then we aren't scheduled.
- if len(m.tickers) == 0 {
- return
- }
-
- // Close the stop channel so all the ticker listeners stop.
- close(m.stopTick)
-
- // Explicitly stop all the tickers themselves so they don't take
- // up any more resources, and get rid of the list.
- for _, t := range m.tickers {
- t.Stop()
- }
- m.tickers = nil
-}
-
-// Tick is used to perform a single round of failure detection and gossip
-func (m *Memberlist) probe() {
- // Track the number of indexes we've considered probing
- numCheck := 0
-START:
- m.nodeLock.RLock()
-
- // Make sure we don't wrap around infinitely
- if numCheck >= len(m.nodes) {
- m.nodeLock.RUnlock()
- return
- }
-
- // Handle the wrap around case
- if m.probeIndex >= len(m.nodes) {
- m.nodeLock.RUnlock()
- m.resetNodes()
- m.probeIndex = 0
- numCheck++
- goto START
- }
-
- // Determine if we should probe this node
- skip := false
- var node nodeState
-
- node = *m.nodes[m.probeIndex]
- if node.Name == m.config.Name {
- skip = true
- } else if node.DeadOrLeft() {
- skip = true
- }
-
- // Potentially skip
- m.nodeLock.RUnlock()
- m.probeIndex++
- if skip {
- numCheck++
- goto START
- }
-
- // Probe the specific node
- m.probeNode(&node)
-}
-
-// probeNodeByAddr just safely calls probeNode given only the address of the node (for tests)
-func (m *Memberlist) probeNodeByAddr(addr string) {
- m.nodeLock.RLock()
- n := m.nodeMap[addr]
- m.nodeLock.RUnlock()
-
- m.probeNode(n)
-}
-
-// failedRemote checks the error and decides if it indicates a failure on the
-// other end.
-func failedRemote(err error) bool {
- switch t := err.(type) {
- case *net.OpError:
- if strings.HasPrefix(t.Net, "tcp") {
- switch t.Op {
- case "dial", "read", "write":
- return true
- }
- }
- }
- return false
-}
-
-// probeNode handles a single round of failure checking on a node.
-func (m *Memberlist) probeNode(node *nodeState) {
- defer metrics.MeasureSince([]string{"memberlist", "probeNode"}, time.Now())
-
- // We use our health awareness to scale the overall probe interval, so we
- // slow down if we detect problems. The ticker that calls us can handle
- // us running over the base interval, and will skip missed ticks.
- probeInterval := m.awareness.ScaleTimeout(m.config.ProbeInterval)
- if probeInterval > m.config.ProbeInterval {
- metrics.IncrCounter([]string{"memberlist", "degraded", "probe"}, 1)
- }
-
- // Prepare a ping message and setup an ack handler.
- selfAddr, selfPort := m.getAdvertise()
- ping := ping{
- SeqNo: m.nextSeqNo(),
- Node: node.Name,
- SourceAddr: selfAddr,
- SourcePort: selfPort,
- SourceNode: m.config.Name,
- }
- ackCh := make(chan ackMessage, m.config.IndirectChecks+1)
- nackCh := make(chan struct{}, m.config.IndirectChecks+1)
- m.setProbeChannels(ping.SeqNo, ackCh, nackCh, probeInterval)
-
- // Mark the sent time here, which should be after any pre-processing but
- // before system calls to do the actual send. This probably over-reports
- // a bit, but it's the best we can do. We had originally put this right
- // after the I/O, but that would sometimes give negative RTT measurements
- // which was not desirable.
- sent := time.Now()
-
- // Send a ping to the node. If this node looks like it's suspect or dead,
- // also tack on a suspect message so that it has a chance to refute as
- // soon as possible.
- deadline := sent.Add(probeInterval)
- addr := node.Address()
-
- // Arrange for our self-awareness to get updated.
- var awarenessDelta int
- defer func() {
- m.awareness.ApplyDelta(awarenessDelta)
- }()
- if node.State == StateAlive {
- if err := m.encodeAndSendMsg(node.FullAddress(), pingMsg, &ping); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to send ping: %s", err)
- if failedRemote(err) {
- goto HANDLE_REMOTE_FAILURE
- } else {
- if _, ok := err.(*net.DNSError); ok {
- // Update our self-awareness based on the results of this failed probe.
- // If we don't have peers who will send nacks then we penalize for any
- // failed probe as a simple health metric. If we do have peers to nack
- // verify, then we can use that as a more sophisticated measure of self-
- // health because we assume them to be working, and they can help us
- // decide if the probed node was really dead or if it was something wrong
- // with ourselves.
- awarenessDelta = 1
- s := suspect{Incarnation: node.Incarnation, Node: node.Name, From: m.config.Name}
- m.suspectNode(&s)
- }
- return
- }
- }
- } else {
- var msgs [][]byte
- if buf, err := encode(pingMsg, &ping); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to encode ping message: %s", err)
- return
- } else {
- msgs = append(msgs, buf.Bytes())
- }
- s := suspect{Incarnation: node.Incarnation, Node: node.Name, From: m.config.Name}
- if buf, err := encode(suspectMsg, &s); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to encode suspect message: %s", err)
- return
- } else {
- msgs = append(msgs, buf.Bytes())
- }
-
- compound := makeCompoundMessage(msgs)
- if err := m.rawSendMsgPacket(node.FullAddress(), &node.Node, compound.Bytes()); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to send compound ping and suspect message to %s: %s", addr, err)
- if failedRemote(err) {
- goto HANDLE_REMOTE_FAILURE
- } else {
- return
- }
- }
- }
-
- // Arrange for our self-awareness to get updated. At this point we've
- // sent the ping, so any return statement means the probe succeeded
- // which will improve our health until we get to the failure scenarios
- // at the end of this function, which will alter this delta variable
- // accordingly.
- awarenessDelta = -1
-
- // Wait for response or round-trip-time.
- select {
- case v := <-ackCh:
- if v.Complete == true {
- rtt := v.Timestamp.Sub(sent)
- m.logger.Printf("[DEBUG] memberlist: ping remote node %s success in %s", node.Node.Name, rtt.String())
- if m.config.Ping != nil {
- m.config.Ping.NotifyPingComplete(&node.Node, rtt, v.Payload)
- }
- return
- }
-
- // As an edge case, if we get a timeout, we need to re-enqueue it
- // here to break out of the select below.
- if v.Complete == false {
- ackCh <- v
- }
- case <-time.After(m.config.ProbeTimeout):
- // Note that we don't scale this timeout based on awareness and
- // the health score. That's because we don't really expect waiting
- // longer to help get UDP through. Since health does extend the
- // probe interval it will give the TCP fallback more time, which
- // is more active in dealing with lost packets, and it gives more
- // time to wait for indirect acks/nacks.
- m.logger.Printf("[DEBUG] memberlist: Failed ping: %s (timeout reached)", node.Name)
- }
-
-HANDLE_REMOTE_FAILURE:
- // Get some random live nodes.
- m.nodeLock.RLock()
- kNodes := kRandomNodes(m.config.IndirectChecks, m.nodes, func(n *nodeState) bool {
- return n.Name == m.config.Name ||
- n.Name == node.Name ||
- n.State != StateAlive
- })
- m.nodeLock.RUnlock()
-
- // Attempt an indirect ping.
- expectedNacks := 0
- selfAddr, selfPort = m.getAdvertise()
- ind := indirectPingReq{
- SeqNo: ping.SeqNo,
- Target: node.Addr,
- Port: node.Port,
- Node: node.Name,
- SourceAddr: selfAddr,
- SourcePort: selfPort,
- SourceNode: m.config.Name,
- }
- for _, peer := range kNodes {
- // We only expect nack to be sent from peers who understand
- // version 4 of the protocol.
- if ind.Nack = peer.PMax >= 4; ind.Nack {
- expectedNacks++
- }
-
- if err := m.encodeAndSendMsg(peer.FullAddress(), indirectPingMsg, &ind); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to send indirect ping: %s", err)
- }
- }
-
- // Also make an attempt to contact the node directly over TCP. This
- // helps prevent confused clients who get isolated from UDP traffic
- // but can still speak TCP (which also means they can possibly report
- // misinformation to other nodes via anti-entropy), avoiding flapping in
- // the cluster.
- //
- // This is a little unusual because we will attempt a TCP ping to any
- // member who understands version 3 of the protocol, regardless of
- // which protocol version we are speaking. That's why we've included a
- // config option to turn this off if desired.
- fallbackCh := make(chan bool, 1)
-
- disableTcpPings := m.config.DisableTcpPings ||
- (m.config.DisableTcpPingsForNode != nil && m.config.DisableTcpPingsForNode(node.Name))
- if (!disableTcpPings) && (node.PMax >= 3) {
- go func() {
- defer close(fallbackCh)
- didContact, err := m.sendPingAndWaitForAck(node.FullAddress(), ping, deadline)
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Failed fallback ping: %s", err)
- } else {
- fallbackCh <- didContact
- }
- }()
- } else {
- close(fallbackCh)
- }
-
- // Wait for the acks or timeout. Note that we don't check the fallback
- // channel here because we want to issue a warning below if that's the
- // *only* way we hear back from the peer, so we have to let this time
- // out first to allow the normal UDP-based acks to come in.
- select {
- case v := <-ackCh:
- if v.Complete == true {
- return
- }
- }
-
- // Finally, poll the fallback channel. The timeouts are set such that
- // the channel will have something or be closed without having to wait
- // any additional time here.
- for didContact := range fallbackCh {
- if didContact {
- m.logger.Printf("[WARN] memberlist: Was able to connect to %s but other probes failed, network may be misconfigured", node.Name)
- return
- }
- }
- // Update our self-awareness based on the results of this failed probe.
- // If we don't have peers who will send nacks then we penalize for any
- // failed probe as a simple health metric. If we do have peers to nack
- // verify, then we can use that as a more sophisticated measure of self-
- // health because we assume them to be working, and they can help us
- // decide if the probed node was really dead or if it was something wrong
- // with ourselves.
- awarenessDelta = 0
- if expectedNacks > 0 {
- if nackCount := len(nackCh); nackCount < expectedNacks {
- awarenessDelta += (expectedNacks - nackCount)
- }
- } else {
- awarenessDelta += 1
- }
-
- // No acks received from target, suspect it as failed.
- m.logger.Printf("[DEBUG] memberlist: Suspect %s has failed, no acks received", node.Name)
- s := suspect{Incarnation: node.Incarnation, Node: node.Name, From: m.config.Name}
- m.suspectNode(&s)
-}
-
-// Ping initiates a ping to the node with the specified name.
-func (m *Memberlist) Ping(node string, addr net.Addr) (time.Duration, error) {
- // Prepare a ping message and setup an ack handler.
- selfAddr, selfPort := m.getAdvertise()
- ping := ping{
- SeqNo: m.nextSeqNo(),
- Node: node,
- SourceAddr: selfAddr,
- SourcePort: selfPort,
- SourceNode: m.config.Name,
- }
- ackCh := make(chan ackMessage, m.config.IndirectChecks+1)
- m.setProbeChannels(ping.SeqNo, ackCh, nil, m.config.ProbeInterval)
-
- a := Address{Addr: addr.String(), Name: node}
-
- // Send a ping to the node.
- if err := m.encodeAndSendMsg(a, pingMsg, &ping); err != nil {
- return 0, err
- }
-
- // Mark the sent time here, which should be after any pre-processing and
- // system calls to do the actual send. This probably under-reports a bit,
- // but it's the best we can do.
- sent := time.Now()
-
- // Wait for response or timeout.
- select {
- case v := <-ackCh:
- if v.Complete == true {
- return v.Timestamp.Sub(sent), nil
- }
- case <-time.After(m.config.ProbeTimeout):
- // Timeout, return an error below.
- }
-
- m.logger.Printf("[DEBUG] memberlist: Failed UDP ping: %v (timeout reached)", node)
- return 0, NoPingResponseError{ping.Node}
-}
-
-// resetNodes is used when the tick wraps around. It will reap the
-// dead nodes and shuffle the node list.
-func (m *Memberlist) resetNodes() {
- m.nodeLock.Lock()
- defer m.nodeLock.Unlock()
-
- // Move dead nodes, but respect gossip to the dead interval
- deadIdx := moveDeadNodes(m.nodes, m.config.GossipToTheDeadTime)
-
- // Deregister the dead nodes
- for i := deadIdx; i < len(m.nodes); i++ {
- delete(m.nodeMap, m.nodes[i].Name)
- m.nodes[i] = nil
- }
-
- // Trim the nodes to exclude the dead nodes
- m.nodes = m.nodes[0:deadIdx]
-
- // Update numNodes after we've trimmed the dead nodes
- atomic.StoreUint32(&m.numNodes, uint32(deadIdx))
-
- // Shuffle live nodes
- shuffleNodes(m.nodes)
-}
-
-// gossip is invoked every GossipInterval period to broadcast our gossip
-// messages to a few random nodes.
-func (m *Memberlist) gossip() {
- defer metrics.MeasureSince([]string{"memberlist", "gossip"}, time.Now())
-
- // Get some random live, suspect, or recently dead nodes
- m.nodeLock.RLock()
- kNodes := kRandomNodes(m.config.GossipNodes, m.nodes, func(n *nodeState) bool {
- if n.Name == m.config.Name {
- return true
- }
-
- switch n.State {
- case StateAlive, StateSuspect:
- return false
-
- case StateDead:
- return time.Since(n.StateChange) > m.config.GossipToTheDeadTime
-
- default:
- return true
- }
- })
- m.nodeLock.RUnlock()
-
- // Compute the bytes available
- bytesAvail := m.config.UDPBufferSize - compoundHeaderOverhead
- if m.config.EncryptionEnabled() {
- bytesAvail -= encryptOverhead(m.encryptionVersion())
- }
-
- for _, node := range kNodes {
- // Get any pending broadcasts
- msgs := m.getBroadcasts(compoundOverhead, bytesAvail)
- if len(msgs) == 0 {
- return
- }
-
- addr := node.Address()
- if len(msgs) == 1 {
- // Send single message as is
- if err := m.rawSendMsgPacket(node.FullAddress(), &node, msgs[0]); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", addr, err)
- }
- } else {
- // Otherwise create and send a compound message
- compound := makeCompoundMessage(msgs)
- if err := m.rawSendMsgPacket(node.FullAddress(), &node, compound.Bytes()); err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to send gossip to %s: %s", addr, err)
- }
- }
- }
-}
-
-// weight is invoked every WeightInterval period to calculate local node weight and
-// enqueue a message carrying the result
-func (m *Memberlist) weight() {
- defer metrics.MeasureSince([]string{"memberlist", "weight"}, time.Now())
-
- // Weight = (AwarenessMaxMultiplier - AwarenessScore) * 0.5 + AwarenessMaxMultiplier * CPUIdlePercent * 0.5
- percent, err := cpu.Percent(0, false)
- if err != nil {
- m.logger.Printf("[ERR] memberlist: Failed to get cpu busy percent: %s", err)
- return
- }
- cpuIdlePercent := 100 - percent[0]
- result := int(math.Round(float64(m.config.AwarenessMaxMultiplier-m.awareness.GetHealthScore())*0.6 +
- float64(m.config.AwarenessMaxMultiplier)*cpuIdlePercent/100*0.4))
-
- w := weight{Incarnation: m.incarnation, Node: m.config.Name, From: m.config.Name, Weight: result, WeightAt: time.Now().UTC().UnixNano() / 1000000}
- m.encodeWeightMsgAndBroadcast(m.config.Name, w)
- m.logger.Printf("[DEBUG] memberlist: enqueued latest weight of local node %s: %d", m.config.Name, result)
-}
-
-// pushPull is invoked periodically to randomly perform a complete state
-// exchange. Used to ensure a high level of convergence, but is also
-// reasonably expensive as the entire state of this node is exchanged
-// with the other node.
-func (m *Memberlist) pushPull() {
- // Get a random live node
- m.nodeLock.RLock()
- nodes := kRandomNodes(1, m.nodes, func(n *nodeState) bool {
- return n.Name == m.config.Name ||
- n.State != StateAlive
- })
- m.nodeLock.RUnlock()
-
- // If no nodes, bail
- if len(nodes) == 0 {
- return
- }
- node := nodes[0]
-
- // Attempt a push pull
- if err := m.pushPullNode(node.FullAddress(), false); err != nil {
- m.logger.Printf("[ERR] memberlist: Push/Pull with %s failed: %s", node.Name, err)
- }
-}
-
-// pushPullNode does a complete state exchange with a specific node.
-func (m *Memberlist) pushPullNode(a Address, join bool) error {
- defer metrics.MeasureSince([]string{"memberlist", "pushPullNode"}, time.Now())
-
- // Attempt to send and receive with the node
- remote, userState, err := m.sendAndReceiveState(a, join)
- if err != nil {
- return err
- }
-
- if err := m.mergeRemoteState(join, remote, userState); err != nil {
- return err
- }
- return nil
-}
-
-// verifyProtocol verifies that all the remote nodes can speak with our
-// nodes and vice versa on both the core protocol as well as the
-// delegate protocol level.
-//
-// The verification works by finding the maximum minimum and
-// minimum maximum understood protocol and delegate versions. In other words,
-// it finds the common denominator of protocol and delegate version ranges
-// for the entire cluster.
-//
-// After this, it goes through the entire cluster (local and remote) and
-// verifies that everyone's speaking protocol versions satisfy this range.
-// If this passes, it means that every node can understand each other.
-func (m *Memberlist) verifyProtocol(remote []pushNodeState) error {
- m.nodeLock.RLock()
- defer m.nodeLock.RUnlock()
-
- // Maximum minimum understood and minimum maximum understood for both
- // the protocol and delegate versions. We use this to verify everyone
- // can be understood.
- var maxpmin, minpmax uint8
- var maxdmin, mindmax uint8
- minpmax = math.MaxUint8
- mindmax = math.MaxUint8
-
- for _, rn := range remote {
- // If the node isn't alive, then skip it
- if rn.State != StateAlive {
- continue
- }
-
- // Skip nodes that don't have versions set, it just means
- // their version is zero.
- if len(rn.Vsn) == 0 {
- continue
- }
-
- if rn.Vsn[0] > maxpmin {
- maxpmin = rn.Vsn[0]
- }
-
- if rn.Vsn[1] < minpmax {
- minpmax = rn.Vsn[1]
- }
-
- if rn.Vsn[3] > maxdmin {
- maxdmin = rn.Vsn[3]
- }
-
- if rn.Vsn[4] < mindmax {
- mindmax = rn.Vsn[4]
- }
- }
-
- for _, n := range m.nodes {
- // Ignore non-alive nodes
- if n.State != StateAlive {
- continue
- }
-
- if n.PMin > maxpmin {
- maxpmin = n.PMin
- }
-
- if n.PMax < minpmax {
- minpmax = n.PMax
- }
-
- if n.DMin > maxdmin {
- maxdmin = n.DMin
- }
-
- if n.DMax < mindmax {
- mindmax = n.DMax
- }
- }
-
- // Now that we definitively know the minimum and maximum understood
- // version that satisfies the whole cluster, we verify that every
- // node in the cluster satisifies this.
- for _, n := range remote {
- var nPCur, nDCur uint8
- if len(n.Vsn) > 0 {
- nPCur = n.Vsn[2]
- nDCur = n.Vsn[5]
- }
-
- if nPCur < maxpmin || nPCur > minpmax {
- return fmt.Errorf(
- "Node '%s' protocol version (%d) is incompatible: [%d, %d]",
- n.Name, nPCur, maxpmin, minpmax)
- }
-
- if nDCur < maxdmin || nDCur > mindmax {
- return fmt.Errorf(
- "Node '%s' delegate protocol version (%d) is incompatible: [%d, %d]",
- n.Name, nDCur, maxdmin, mindmax)
- }
- }
-
- for _, n := range m.nodes {
- nPCur := n.PCur
- nDCur := n.DCur
-
- if nPCur < maxpmin || nPCur > minpmax {
- return fmt.Errorf(
- "Node '%s' protocol version (%d) is incompatible: [%d, %d]",
- n.Name, nPCur, maxpmin, minpmax)
- }
-
- if nDCur < maxdmin || nDCur > mindmax {
- return fmt.Errorf(
- "Node '%s' delegate protocol version (%d) is incompatible: [%d, %d]",
- n.Name, nDCur, maxdmin, mindmax)
- }
- }
-
- return nil
-}
-
-// nextSeqNo returns a usable sequence number in a thread safe way
-func (m *Memberlist) nextSeqNo() uint32 {
- return atomic.AddUint32(&m.sequenceNum, 1)
-}
-
-// nextIncarnation returns the next incarnation number in a thread safe way
-func (m *Memberlist) nextIncarnation() uint32 {
- return atomic.AddUint32(&m.incarnation, 1)
-}
-
-// skipIncarnation adds the positive offset to the incarnation number.
-func (m *Memberlist) skipIncarnation(offset uint32) uint32 {
- return atomic.AddUint32(&m.incarnation, offset)
-}
-
-// estNumNodes is used to get the current estimate of the number of nodes
-func (m *Memberlist) estNumNodes() int {
- return int(atomic.LoadUint32(&m.numNodes))
-}
-
-type ackMessage struct {
- Complete bool
- Payload []byte
- Timestamp time.Time
-}
-
-// setProbeChannels is used to attach the ackCh to receive a message when an ack
-// with a given sequence number is received. The `complete` field of the message
-// will be false on timeout. Any nack messages will cause an empty struct to be
-// passed to the nackCh, which can be nil if not needed.
-func (m *Memberlist) setProbeChannels(seqNo uint32, ackCh chan ackMessage, nackCh chan struct{}, timeout time.Duration) {
- // Create handler functions for acks and nacks
- ackFn := func(payload []byte, timestamp time.Time) {
- select {
- case ackCh <- ackMessage{true, payload, timestamp}:
- default:
- }
- }
- nackFn := func() {
- select {
- case nackCh <- struct{}{}:
- default:
- }
- }
-
- // Add the handlers
- ah := &ackHandler{ackFn, nackFn, nil}
- m.ackLock.Lock()
- m.ackHandlers[seqNo] = ah
- m.ackLock.Unlock()
-
- // Setup a reaping routing
- ah.timer = time.AfterFunc(timeout, func() {
- m.ackLock.Lock()
- delete(m.ackHandlers, seqNo)
- m.ackLock.Unlock()
- select {
- case ackCh <- ackMessage{false, nil, time.Now()}:
- default:
- }
- })
-}
-
-// setAckHandler is used to attach a handler to be invoked when an ack with a
-// given sequence number is received. If a timeout is reached, the handler is
-// deleted. This is used for indirect pings so does not configure a function
-// for nacks.
-func (m *Memberlist) setAckHandler(seqNo uint32, ackFn func([]byte, time.Time), timeout time.Duration) {
- // Add the handler
- ah := &ackHandler{ackFn, nil, nil}
- m.ackLock.Lock()
- m.ackHandlers[seqNo] = ah
- m.ackLock.Unlock()
-
- // Setup a reaping routing
- ah.timer = time.AfterFunc(timeout, func() {
- m.ackLock.Lock()
- delete(m.ackHandlers, seqNo)
- m.ackLock.Unlock()
- })
-}
-
-// Invokes an ack handler if any is associated, and reaps the handler immediately
-func (m *Memberlist) invokeAckHandler(ack ackResp, timestamp time.Time) {
- m.ackLock.Lock()
- ah, ok := m.ackHandlers[ack.SeqNo]
- delete(m.ackHandlers, ack.SeqNo)
- m.ackLock.Unlock()
- if !ok {
- return
- }
- ah.timer.Stop()
- ah.ackFn(ack.Payload, timestamp)
-}
-
-// Invokes nack handler if any is associated.
-func (m *Memberlist) invokeNackHandler(nack nackResp) {
- m.ackLock.Lock()
- ah, ok := m.ackHandlers[nack.SeqNo]
- m.ackLock.Unlock()
- if !ok || ah.nackFn == nil {
- return
- }
- ah.nackFn()
-}
-
-// refute gossips an alive message in response to incoming information that we
-// are suspect or dead. It will make sure the incarnation number beats the given
-// accusedInc value, or you can supply 0 to just get the next incarnation number.
-// This alters the node state that's passed in so this MUST be called while the
-// nodeLock is held.
-func (m *Memberlist) refute(me *nodeState, accusedInc uint32) {
- // Make sure the incarnation number beats the accusation.
- inc := m.nextIncarnation()
- if accusedInc >= inc {
- inc = m.skipIncarnation(accusedInc - inc + 1)
- }
- me.Incarnation = inc
-
- // Decrease our health because we are being asked to refute a problem.
- m.awareness.ApplyDelta(1)
-
- // Format and broadcast an alive message.
- a := alive{
- Incarnation: inc,
- Node: me.Name,
- Addr: me.Addr,
- Port: me.Port,
- Meta: me.Meta,
- Vsn: []uint8{
- me.PMin, me.PMax, me.PCur,
- me.DMin, me.DMax, me.DCur,
- },
- }
- m.encodeAndBroadcast(me.Addr, aliveMsg, a)
-}
-
-// aliveNode is invoked by the network layer when we get a message about a
-// live node.
-func (m *Memberlist) aliveNode(a *alive, notify chan struct{}, bootstrap bool) {
- m.nodeLock.Lock()
- defer m.nodeLock.Unlock()
- state, ok := m.nodeMap[a.Node]
-
- // It is possible that during a Leave(), there is already an aliveMsg
- // in-queue to be processed but blocked by the locks above. If we let
- // that aliveMsg process, it'll cause us to re-join the cluster. This
- // ensures that we don't.
- if m.hasLeft() && a.Node == m.config.Name {
- return
- }
-
- if len(a.Vsn) >= 3 {
- pMin := a.Vsn[0]
- pMax := a.Vsn[1]
- pCur := a.Vsn[2]
- if pMin == 0 || pMax == 0 || pMin > pMax {
- m.logger.Printf("[WARN] memberlist: Ignoring an alive message for '%s' (%v:%d) because protocol version(s) are wrong: %d <= %d <= %d should be >0", a.Node, a.Addr, a.Port, pMin, pCur, pMax)
- return
- }
- }
-
- // Invoke the Alive delegate if any. This can be used to filter out
- // alive messages based on custom logic. For example, using a cluster name.
- // Using a merge delegate is not enough, as it is possible for passive
- // cluster merging to still occur.
- if m.config.Alive != nil {
- if len(a.Vsn) < 6 {
- m.logger.Printf("[WARN] memberlist: ignoring alive message for '%s' (%v:%d) because Vsn is not present",
- a.Node, a.Addr, a.Port)
- return
- }
- node := &Node{
- Name: a.Node,
- Addr: a.Addr,
- Port: a.Port,
- Meta: a.Meta,
- PMin: a.Vsn[0],
- PMax: a.Vsn[1],
- PCur: a.Vsn[2],
- DMin: a.Vsn[3],
- DMax: a.Vsn[4],
- DCur: a.Vsn[5],
- }
- if err := m.config.Alive.NotifyAlive(node); err != nil {
- m.logger.Printf("[WARN] memberlist: ignoring alive message for '%s': %s",
- a.Node, err)
- return
- }
- }
-
- // Check if we've never seen this node before, and if not, then
- // store this node in our node map.
- var updatesNode bool
- if !ok {
- errCon := m.config.AddrAllowed(a.Addr)
- if errCon != nil {
- m.logger.Printf("[WARN] memberlist: Rejected node %s (%v): %s", a.Node, a.Addr, errCon)
- return
- }
- state = &nodeState{
- Node: Node{
- Name: a.Node,
- Addr: a.Addr,
- Port: a.Port,
- Meta: a.Meta,
- },
- State: StateDead,
- }
- if len(a.Vsn) > 5 {
- state.PMin = a.Vsn[0]
- state.PMax = a.Vsn[1]
- state.PCur = a.Vsn[2]
- state.DMin = a.Vsn[3]
- state.DMax = a.Vsn[4]
- state.DCur = a.Vsn[5]
- }
-
- // Add to map
- m.nodeMap[a.Node] = state
-
- // Get a random offset. This is important to ensure
- // the failure detection bound is low on average. If all
- // nodes did an append, failure detection bound would be
- // very high.
- n := len(m.nodes)
- offset := randomOffset(n)
-
- // Add at the end and swap with the node at the offset
- m.nodes = append(m.nodes, state)
- m.nodes[offset], m.nodes[n] = m.nodes[n], m.nodes[offset]
-
- // Update numNodes after we've added a new node
- atomic.AddUint32(&m.numNodes, 1)
- } else {
- // Check if this address is different from the existing node unless the old node is dead.
- if state.Addr != a.Addr || state.Port != a.Port {
- errCon := m.config.AddrAllowed(a.Addr)
- if errCon != nil {
- m.logger.Printf("[WARN] memberlist: Rejected IP update from %v to %v for node %s: %s", a.Node, state.Addr, a.Addr, errCon)
- return
- }
- // If DeadNodeReclaimTime is configured, check if enough time has elapsed since the node died.
- canReclaim := (m.config.DeadNodeReclaimTime > 0 &&
- time.Since(state.StateChange) > m.config.DeadNodeReclaimTime)
-
- // Allow the address to be updated if a dead node is being replaced.
- if state.State == StateLeft || (state.State == StateDead && canReclaim) {
- m.logger.Printf("[INFO] memberlist: Updating address for left or failed node %s from %v:%d to %v:%d",
- state.Name, state.Addr, state.Port, a.Addr, a.Port)
- updatesNode = true
- } else {
- m.logger.Printf("[ERR] memberlist: Conflicting address for %s. Mine: %v:%d Theirs: %v:%d Old state: %v",
- state.Name, state.Addr, state.Port, a.Addr, a.Port, state.State)
-
- // Inform the conflict delegate if provided
- if m.config.Conflict != nil {
- other := Node{
- Name: a.Node,
- Addr: a.Addr,
- Port: a.Port,
- Meta: a.Meta,
- }
- m.config.Conflict.NotifyConflict(&state.Node, &other)
- }
- return
- }
- }
- }
-
- // Bail if the incarnation number is older, and this is not about us
- isLocalNode := state.Name == m.config.Name
- if a.Incarnation <= state.Incarnation && !isLocalNode && !updatesNode {
- return
- }
-
- // Bail if strictly less and this is about us
- if a.Incarnation < state.Incarnation && isLocalNode {
- return
- }
-
- // Clear out any suspicion timer that may be in effect.
- delete(m.nodeTimers, a.Node)
-
- // Store the old state and meta data
- oldState := state.State
- oldMeta := state.Meta
-
- // If this is us we need to refute, otherwise re-broadcast
- if !bootstrap && isLocalNode {
- // Compute the version vector
- versions := []uint8{
- state.PMin, state.PMax, state.PCur,
- state.DMin, state.DMax, state.DCur,
- }
-
- // If the Incarnation is the same, we need special handling, since it
- // possible for the following situation to happen:
- // 1) Start with configuration C, join cluster
- // 2) Hard fail / Kill / Shutdown
- // 3) Restart with configuration C', join cluster
- //
- // In this case, other nodes and the local node see the same incarnation,
- // but the values may not be the same. For this reason, we always
- // need to do an equality check for this Incarnation. In most cases,
- // we just ignore, but we may need to refute.
- //
- if a.Incarnation == state.Incarnation &&
- bytes.Equal(a.Meta, state.Meta) &&
- bytes.Equal(a.Vsn, versions) {
- return
- }
- m.refute(state, a.Incarnation)
- m.logger.Printf("[WARN] memberlist: Refuting an alive message for '%s' (%v:%d) meta:(%v VS %v), vsn:(%v VS %v)", a.Node, a.Addr, a.Port, a.Meta, state.Meta, a.Vsn, versions)
- } else {
- m.encodeBroadcastNotify(a.Node, aliveMsg, a, notify)
-
- // Update protocol versions if it arrived
- if len(a.Vsn) > 0 {
- state.PMin = a.Vsn[0]
- state.PMax = a.Vsn[1]
- state.PCur = a.Vsn[2]
- state.DMin = a.Vsn[3]
- state.DMax = a.Vsn[4]
- state.DCur = a.Vsn[5]
- }
-
- // Update the state and incarnation number
- state.Incarnation = a.Incarnation
- state.Meta = a.Meta
- state.Addr = a.Addr
- state.Port = a.Port
- if state.State != StateAlive {
- state.State = StateAlive
- state.StateChange = time.Now()
- }
- }
-
- // Update metrics
- metrics.IncrCounter([]string{"memberlist", "msg", "alive"}, 1)
-
- // Notify the delegate of any relevant updates
- if m.config.Events != nil {
- if oldState == StateDead || oldState == StateLeft {
- // if Dead/Left -> Alive, notify of join
- state.Node.State = state.State
- m.config.Events.NotifyJoin(&state.Node)
- } else if oldState == StateSuspect {
- state.Node.State = state.State
- m.config.Events.NotifySuspectSateChange(&state.Node)
- } else if !bytes.Equal(oldMeta, state.Meta) {
- // if Meta changed, trigger an update notification
- m.config.Events.NotifyUpdate(&state.Node)
- }
- }
-}
-
-// suspectNode is invoked by the network layer when we get a message
-// about a suspect node
-func (m *Memberlist) suspectNode(s *suspect) {
- m.nodeLock.Lock()
- defer m.nodeLock.Unlock()
- state, ok := m.nodeMap[s.Node]
-
- // If we've never heard about this node before, ignore it
- if !ok {
- return
- }
-
- // Ignore old incarnation numbers
- if s.Incarnation < state.Incarnation {
- return
- }
-
- // See if there's a suspicion timer we can confirm. If the info is new
- // to us we will go ahead and re-gossip it. This allows for multiple
- // independent confirmations to flow even when a node probes a node
- // that's already suspect.
- if timer, ok := m.nodeTimers[s.Node]; ok {
- if timer.Confirm(s.From) {
- m.encodeAndBroadcast(s.Node, suspectMsg, s)
- }
- return
- }
-
- // Ignore non-alive nodes
- if state.State != StateAlive {
- return
- }
-
- // If this is us we need to refute, otherwise re-broadcast
- if state.Name == m.config.Name {
- m.refute(state, s.Incarnation)
- m.logger.Printf("[WARN] memberlist: Refuting a suspect message (from: %s)", s.From)
- return // Do not mark ourself suspect
- } else {
- m.encodeAndBroadcast(s.Node, suspectMsg, s)
- }
-
- // Update metrics
- metrics.IncrCounter([]string{"memberlist", "msg", "suspect"}, 1)
-
- // Update the state
- state.Incarnation = s.Incarnation
- state.State = StateSuspect
- changeTime := time.Now()
- state.StateChange = changeTime
-
- // Setup a suspicion timer. Given that we don't have any known phase
- // relationship with our peers, we set up k such that we hit the nominal
- // timeout two probe intervals short of what we expect given the suspicion
- // multiplier.
- k := m.config.SuspicionMult - 2
-
- // If there aren't enough nodes to give the expected confirmations, just
- // set k to 0 to say that we don't expect any. Note we subtract 2 from n
- // here to take out ourselves and the node being probed.
- n := m.estNumNodes()
- if n-2 < k {
- k = 0
- }
-
- // Compute the timeouts based on the size of the cluster.
- min := suspicionTimeout(m.config.SuspicionMult, n, m.config.ProbeInterval)
- max := time.Duration(m.config.SuspicionMaxTimeoutMult) * min
- fn := func(numConfirmations int) {
- var d *dead
-
- m.nodeLock.Lock()
- state, ok := m.nodeMap[s.Node]
- timeout := ok && state.State == StateSuspect && state.StateChange == changeTime
- if timeout {
- d = &dead{Incarnation: state.Incarnation, Node: state.Name, From: m.config.Name}
- }
- m.nodeLock.Unlock()
-
- if timeout {
- if k > 0 && numConfirmations < k {
- metrics.IncrCounter([]string{"memberlist", "degraded", "timeout"}, 1)
- }
-
- m.logger.Printf("[INFO] memberlist: Marking %s as failed, suspect timeout reached (%d peer confirmations)",
- state.Name, numConfirmations)
-
- m.deadNode(d)
- }
- }
- m.nodeTimers[s.Node] = newSuspicion(s.From, k, min, max, fn)
- if m.config.Events != nil {
- state.Node.State = state.State
- m.config.Events.NotifySuspectSateChange(&state.Node)
- }
-}
-
-// deadNode is invoked by the network layer when we get a message
-// about a dead node
-func (m *Memberlist) deadNode(d *dead) {
- m.nodeLock.Lock()
- defer m.nodeLock.Unlock()
- state, ok := m.nodeMap[d.Node]
-
- // If we've never heard about this node before, ignore it
- if !ok {
- return
- }
-
- // Ignore old incarnation numbers
- if d.Incarnation < state.Incarnation {
- return
- }
-
- // Clear out any suspicion timer that may be in effect.
- delete(m.nodeTimers, d.Node)
-
- // Ignore if node is already dead
- if state.DeadOrLeft() {
- return
- }
-
- // Check if this is us
- if state.Name == m.config.Name {
- // If we are not leaving we need to refute
- if !m.hasLeft() {
- m.refute(state, d.Incarnation)
- m.logger.Printf("[WARN] memberlist: Refuting a dead message (from: %s)", d.From)
- return // Do not mark ourself dead
- }
-
- // If we are leaving, we broadcast and wait
- m.encodeBroadcastNotify(d.Node, deadMsg, d, m.leaveBroadcast)
- } else {
- m.encodeAndBroadcast(d.Node, deadMsg, d)
- }
-
- // Update metrics
- metrics.IncrCounter([]string{"memberlist", "msg", "dead"}, 1)
-
- // Update the state
- state.Incarnation = d.Incarnation
-
- // If the dead message was send by the node itself, mark it is left
- // instead of dead.
- if d.Node == d.From {
- state.State = StateLeft
- } else {
- state.State = StateDead
- }
- state.StateChange = time.Now()
-
- // Notify of death
- if m.config.Events != nil {
- m.config.Events.NotifyLeave(&state.Node)
- }
-}
-
-// weightNode is invoked by the network layer when we get a message
-// about node weight
-func (m *Memberlist) weightNode(s *weight) {
- m.nodeLock.Lock()
- defer m.nodeLock.Unlock()
- state, ok := m.nodeMap[s.Node]
-
- // If we've never heard about this node before, ignore it
- if !ok {
- return
- }
-
- // Ignore old incarnation numbers
- if s.Incarnation < state.Incarnation {
- return
- }
-
- // Ignore non-alive nodes or this is about us
- if state.State != StateAlive || state.Name == m.config.Name {
- return
- }
-
- // Ignore old weight messages
- if s.WeightAt <= state.WeightAt {
- return
- }
-
- m.encodeWeightMsgAndBroadcast(s.Node, s)
-
- // Update metrics
- metrics.IncrCounter([]string{"memberlist", "msg", "weight"}, 1)
-
- // Update the state
- old := state.Weight
- state.Weight = s.Weight
- state.WeightAt = s.WeightAt
- if state.Weight != old {
- if m.config.Events != nil {
- state.Node.Weight = state.Weight
- m.config.Events.NotifyWeight(&state.Node)
- }
- m.logger.Printf("[DEBUG] memberlist: updated weight (calculated at %s) of node %s from %d to %d",
- time.Unix(s.WeightAt/1000, (s.WeightAt%1000)*1000000).Local().Format("2006-01-02T15:04:05-0700"), state.Name, old, state.Weight)
- }
-}
-
-// mergeState is invoked by the network layer when we get a Push/Pull
-// state transfer
-func (m *Memberlist) mergeState(remote []pushNodeState) {
- for _, r := range remote {
- switch r.State {
- case StateAlive:
- a := alive{
- Incarnation: r.Incarnation,
- Node: r.Name,
- Addr: r.Addr,
- Port: r.Port,
- Meta: r.Meta,
- Vsn: r.Vsn,
- }
- m.aliveNode(&a, nil, false)
-
- case StateLeft:
- d := dead{Incarnation: r.Incarnation, Node: r.Name, From: r.Name}
- m.deadNode(&d)
- case StateDead:
- // If the remote node believes a node is dead, we prefer to
- // suspect that node instead of declaring it dead instantly
- fallthrough
- case StateSuspect:
- s := suspect{Incarnation: r.Incarnation, Node: r.Name, From: m.config.Name}
- m.suspectNode(&s)
- }
- }
-}
diff --git a/toolkit/memberlist/state_test.go b/toolkit/memberlist/state_test.go
deleted file mode 100644
index 16ea0ca4..00000000
--- a/toolkit/memberlist/state_test.go
+++ /dev/null
@@ -1,2443 +0,0 @@
-package memberlist
-
-import (
- "fmt"
- "log"
- "net"
- "os"
- "sync"
- "sync/atomic"
- "testing"
- "time"
-)
-
-var bindLock sync.Mutex
-var bindNum byte = 10
-
-func getBindAddrNet(network byte) net.IP {
- bindLock.Lock()
- defer bindLock.Unlock()
-
- //result := net.IPv4(127, 0, network, bindNum)
- //bindNum++
- //if bindNum > 255 {
- // bindNum = 10
- //}
-
- result := net.IPv4(127, 0, 0, 1)
-
- return result
-}
-
-func getBindAddr() net.IP {
- return getBindAddrNet(0)
-}
-
-func HostMemberlist(host string, t *testing.T, f func(*Config)) *Memberlist {
- t.Helper()
-
- c := DefaultLANConfig()
- c.Name = host
- c.BindAddr = host
- c.BindPort = 0 // choose a free port
- c.Logger = log.New(os.Stderr, host, log.LstdFlags)
- if f != nil {
- f(c)
- }
-
- m, err := NewMemberlist(c)
- if err != nil {
- t.Fatalf("failed to get memberlist: %s", err)
- }
- return m
-}
-
-func TestMemberList_Probe(t *testing.T) {
- addr1 := getBindAddr()
- addr2 := getBindAddr()
-
- m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
- c.ProbeTimeout = time.Millisecond
- c.ProbeInterval = 10 * time.Millisecond
- c.BindPort = 56102
- c.AdvertisePort = 56102
- c.Name = "c1"
- })
- defer m1.Shutdown()
-
- m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
- c.BindPort = 56101
- c.AdvertisePort = 56101
- c.Name = "c2"
- })
- defer m2.Shutdown()
-
- a1 := alive{
- Node: m1.config.Name,
- Addr: addr1.String(),
- Port: uint16(m1.config.BindPort),
- Incarnation: 1,
- Vsn: m1.config.BuildVsnArray(),
- }
- m1.aliveNode(&a1, nil, true)
- a2 := alive{
- Node: m2.config.Name,
- Addr: addr2.String(),
- Port: uint16(m2.config.BindPort),
- Incarnation: 1,
- Vsn: m2.config.BuildVsnArray(),
- }
- m1.aliveNode(&a2, nil, false)
-
- // should ping addr2
- m1.probe()
-
- // Should not be marked suspect
- //n := m1.nodeMap[m2.config.Name]
- //if n.State != StateAlive {
- // t.Fatalf("Expect node to be alive")
- //}
- //
- //// Should increment seqno
- //if m1.sequenceNum != 1 {
- // t.Fatalf("bad seqno %v", m2.sequenceNum)
- //}
-}
-
-func TestMemberList_ProbeNode_Suspect(t *testing.T) {
- addr1 := getBindAddr()
- addr2 := getBindAddr()
- addr3 := getBindAddr()
- addr4 := getBindAddr()
- ip1 := addr1
- ip2 := addr2
- ip3 := addr3
- ip4 := addr4
-
- m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
- c.ProbeTimeout = time.Millisecond
- c.ProbeInterval = 10 * time.Millisecond
- c.BindPort = 56100
- c.AdvertisePort = 56100
- c.Name = "c1"
- })
- defer m1.Shutdown()
-
- m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
- c.BindPort = 56099
- c.AdvertisePort = 56099
- c.Name = "c2"
- })
- defer m2.Shutdown()
- m3 := HostMemberlist(addr3.String(), t, func(c *Config) {
- c.BindPort = 56098
- c.AdvertisePort = 56098
- c.Name = "c3"
- })
- defer m3.Shutdown()
- a1 := alive{Node: m1.config.Name, Addr: ip1.String(), Port: m1.advertisePort, Incarnation: 1, Vsn: m1.config.BuildVsnArray()}
- m1.aliveNode(&a1, nil, true)
- a2 := alive{Node: m2.config.Name, Addr: ip2.String(), Port: m2.advertisePort, Incarnation: 1, Vsn: m2.config.BuildVsnArray()}
- m1.aliveNode(&a2, nil, false)
- a3 := alive{Node: m3.config.Name, Addr: ip3.String(), Port: m3.advertisePort, Incarnation: 1, Vsn: m3.config.BuildVsnArray()}
- m1.aliveNode(&a3, nil, false)
- a4 := alive{Node: "c4", Addr: ip4.String(), Port: uint16(56097), Incarnation: 1, Vsn: m1.config.BuildVsnArray()}
- m1.aliveNode(&a4, nil, false)
-
- n := m1.nodeMap["c4"]
- m1.probeNode(n)
-
- // Should be marked suspect.
- if n.State != StateSuspect {
- t.Fatalf("Expect node to be suspect")
- }
- time.Sleep(10 * time.Millisecond)
-
- // One of the peers should have attempted an indirect probe.
- if s2, s3 := atomic.LoadUint32(&m2.sequenceNum), atomic.LoadUint32(&m3.sequenceNum); s2 != 1 && s3 != 1 {
- t.Fatalf("bad seqnos, expected both to be 1: %v, %v", s2, s3)
- }
-}
-
-func TestMemberList_ProbeNode_Suspect_Dogpile(t *testing.T) {
- freePorts := []int{
- 56096,
- 56095,
- 56094,
- 56093,
- 56092,
- 56091,
- 56090,
- 56089,
- 56088,
- }
- nodes := []string{
- "c1",
- "c2",
- "c3",
- "c4",
- "c5",
- "c6",
- "c7",
- "c8",
- "c9",
- }
- cases := []struct {
- name string
- numPeers int
- confirmations int
- expected time.Duration
- nodeName string
- bindPort int
- }{
- {"n=2, k=3 (max timeout disabled)", 1, 0, 500 * time.Millisecond, "c1", 56096},
- {"n=3, k=3", 2, 0, 500 * time.Millisecond, "c2", 56095},
- {"n=4, k=3", 3, 0, 500 * time.Millisecond, "c3", 56094},
- {"n=5, k=3 (max timeout starts to take effect)", 4, 0, 1000 * time.Millisecond, "c4", 56093},
- {"n=6, k=3", 5, 0, 1000 * time.Millisecond, "c5", 56092},
- {"n=6, k=3 (confirmations start to lower timeout)", 5, 1, 750 * time.Millisecond, "c6", 56091},
- {"n=6, k=3", 5, 2, 604 * time.Millisecond, "c7", 56090},
- {"n=6, k=3 (timeout driven to nominal value)", 5, 3, 500 * time.Millisecond, "c8", 56089},
- {"n=6, k=3", 5, 4, 500 * time.Millisecond, "c9", 56088},
- }
-
- for i, c := range cases {
- var ports []int
- ports = append(ports, freePorts[:i]...)
- ports = append(ports, freePorts[i+1:]...)
- var peernames []string
- peernames = append(peernames, nodes[:i]...)
- peernames = append(peernames, nodes[i+1:]...)
- t.Run(c.name, func(t *testing.T) {
- // Create the main memberlist under test.
- addr := getBindAddr()
-
- m := HostMemberlist(addr.String(), t, func(conf *Config) {
- conf.ProbeTimeout = time.Millisecond
- conf.ProbeInterval = 100 * time.Millisecond
- conf.SuspicionMult = 5
- conf.SuspicionMaxTimeoutMult = 2
- conf.BindPort = c.bindPort
- conf.AdvertisePort = c.bindPort
- conf.Name = c.nodeName
- })
- defer m.Shutdown()
-
- a := alive{Node: addr.String(), Addr: addr.String(), Port: uint16(m.config.BindPort), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
- m.aliveNode(&a, nil, true)
-
- // Make all but one peer be an real, alive instance.
- var peers []*Memberlist
- for j := 0; j < c.numPeers-1; j++ {
- peerAddr := getBindAddr()
-
- peer := HostMemberlist(peerAddr.String(), t, func(c *Config) {
- c.BindPort = ports[j]
- c.AdvertisePort = ports[j]
- c.Name = peernames[j]
- })
- defer peer.Shutdown()
-
- peers = append(peers, peer)
-
- a = alive{Node: peer.config.Name, Addr: peerAddr.String(), Port: peer.advertisePort, Incarnation: 1, Vsn: m.config.BuildVsnArray()}
- m.aliveNode(&a, nil, false)
- }
-
- // Just use a bogus address for the last peer so it doesn't respond
- // to pings, but tell the memberlist it's alive.
- badPeerAddr := getBindAddr()
- badPeerName := "c10"
- a = alive{Node: badPeerName, Addr: badPeerAddr.String(), Port: uint16(56087), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
- m.aliveNode(&a, nil, false)
-
- // Force a probe, which should start us into the suspect state.
- m.probeNodeByAddr(badPeerName)
-
- if m.getNodeState(badPeerName) != StateSuspect {
- t.Fatalf("case %d: expected node to be suspect", i)
- }
-
- // Add the requested number of confirmations.
- for j := 0; j < c.confirmations; j++ {
- from := fmt.Sprintf("peer%d", j)
- s := suspect{Node: badPeerName, Incarnation: 1, From: from}
- m.suspectNode(&s)
- }
-
- // Wait until right before the timeout and make sure the timer
- // hasn't fired.
- fudge := 25 * time.Millisecond
- time.Sleep(c.expected - fudge)
-
- if m.getNodeState(badPeerName) != StateSuspect {
- t.Fatalf("case %d: expected node to still be suspect", i)
- }
-
- // Wait through the timeout and a little after to make sure the
- // timer fires.
- time.Sleep(2 * fudge)
-
- if m.getNodeState(badPeerName) != StateDead {
- t.Fatalf("case %d: expected node to be dead", i)
- }
- })
- }
-}
-
-/*
-func TestMemberList_ProbeNode_FallbackTCP(t *testing.T) {
- addr1 := getBindAddr()
- addr2 := getBindAddr()
- addr3 := getBindAddr()
- addr4 := getBindAddr()
- ip1 := addr1
- ip2 := addr2
- ip3 := addr3
- ip4 := addr4
-
- var probeTimeMax time.Duration
- m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
- c.ProbeTimeout = 10 * time.Millisecond
- c.ProbeInterval = 200 * time.Millisecond
- probeTimeMax = c.ProbeInterval + 20*time.Millisecond
- })
- defer m1.Shutdown()
-
- bindPort := m1.config.BindPort
-
- m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
- c.BindPort = bindPort
- })
- defer m2.Shutdown()
-
- m3 := HostMemberlist(addr3.String(), t, func(c *Config) {
- c.BindPort = bindPort
- })
- defer m3.Shutdown()
-
- m4 := HostMemberlist(addr4.String(), t, func(c *Config) {
- c.BindPort = bindPort
- })
- defer m4.Shutdown()
-
- a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1}
- m1.aliveNode(&a1, nil, true)
- a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1}
- m1.aliveNode(&a2, nil, false)
- a3 := alive{Node: addr3.String(), Addr: ip3.String(), Port: uint16(bindPort), Incarnation: 1}
- m1.aliveNode(&a3, nil, false)
-
- // Make sure m4 is configured with the same protocol version as m1 so
- // the TCP fallback behavior is enabled.
- a4 := alive{
- Node: addr4.String(),
- Addr: ip4.String(),
- Port: uint16(bindPort),
- Incarnation: 1,
- Vsn: []uint8{
- ProtocolVersionMin,
- ProtocolVersionMax,
- m1.config.ProtocolVersion,
- m1.config.DelegateProtocolMin,
- m1.config.DelegateProtocolMax,
- m1.config.DelegateProtocolVersion,
- },
- }
- m1.aliveNode(&a4, nil, false)
-
- // Isolate m4 from UDP traffic by re-opening its listener on the wrong
- // port. This should force the TCP fallback path to be used.
- var err error
- if err = m4.udpListener.Close(); err != nil {
- t.Fatalf("err: %v", err)
- }
- udpAddr := &net.UDPAddr{IP: ip4.String(), Port: 9999}
- if m4.udpListener, err = net.ListenUDP("udp", udpAddr); err != nil {
- t.Fatalf("err: %v", err)
- }
-
- // Have node m1 probe m4.
- n := m1.nodeMap[addr4.String()]
- startProbe := time.Now()
- m1.probeNode(n)
- probeTime := time.Now().Sub(startProbe)
-
- // Should be marked alive because of the TCP fallback ping.
- if n.State != stateAlive {
- t.Fatalf("expect node to be alive")
- }
-
- // Make sure TCP activity completed in a timely manner.
- if probeTime > probeTimeMax {
- t.Fatalf("took to long to probe, %9.6f", probeTime.Seconds())
- }
-
- // Confirm at least one of the peers attempted an indirect probe.
- time.Sleep(probeTimeMax)
- if m2.sequenceNum != 1 && m3.sequenceNum != 1 {
- t.Fatalf("bad seqnos %v, %v", m2.sequenceNum, m3.sequenceNum)
- }
-
- // Now shutdown all inbound TCP traffic to make sure the TCP fallback
- // path properly fails when the node is really unreachable.
- if err = m4.tcpListener.Close(); err != nil {
- t.Fatalf("err: %v", err)
- }
- tcpAddr := &net.TCPAddr{IP: ip4.String(), Port: 9999}
- if m4.tcpListener, err = net.ListenTCP("tcp", tcpAddr); err != nil {
- t.Fatalf("err: %v", err)
- }
-
- // Probe again, this time there should be no contact.
- startProbe = time.Now()
- m1.probeNode(n)
- probeTime = time.Now().Sub(startProbe)
-
- // Node should be reported suspect.
- if n.State != stateSuspect {
- t.Fatalf("expect node to be suspect")
- }
-
- // Make sure TCP activity didn't cause us to wait too long before
- // timing out.
- if probeTime > probeTimeMax {
- t.Fatalf("took to long to probe, %9.6f", probeTime.Seconds())
- }
-
- // Confirm at least one of the peers attempted an indirect probe.
- time.Sleep(probeTimeMax)
- if m2.sequenceNum != 2 && m3.sequenceNum != 2 {
- t.Fatalf("bad seqnos %v, %v", m2.sequenceNum, m3.sequenceNum)
- }
-}
-
-func TestMemberList_ProbeNode_FallbackTCP_Disabled(t *testing.T) {
- addr1 := getBindAddr()
- addr2 := getBindAddr()
- addr3 := getBindAddr()
- addr4 := getBindAddr()
- ip1 := addr1
- ip2 := addr2
- ip3 := addr3
- ip4 := addr4
-
- var probeTimeMax time.Duration
- m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
- c.ProbeTimeout = 10 * time.Millisecond
- c.ProbeInterval = 200 * time.Millisecond
- probeTimeMax = c.ProbeInterval + 20*time.Millisecond
- })
- defer m1.Shutdown()
-
- bindPort := m1.config.BindPort
-
- m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
- c.BindPort = bindPort
- })
- defer m2.Shutdown()
-
- m3 := HostMemberlist(addr3.String(), t, func(c *Config) {
- c.BindPort = bindPort
- })
- defer m3.Shutdown()
-
- m4 := HostMemberlist(addr4.String(), t, func(c *Config) {
- c.BindPort = bindPort
- })
- defer m4.Shutdown()
-
- a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1}
- m1.aliveNode(&a1, nil, true)
- a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1}
- m1.aliveNode(&a2, nil, false)
- a3 := alive{Node: addr3.String(), Addr: ip3.String(), Port: uint16(bindPort), Incarnation: 1}
- m1.aliveNode(&a3, nil, false)
-
- // Make sure m4 is configured with the same protocol version as m1 so
- // the TCP fallback behavior is enabled.
- a4 := alive{
- Node: addr4.String(),
- Addr: ip4.String(),
- Port: uint16(bindPort),
- Incarnation: 1,
- Vsn: []uint8{
- ProtocolVersionMin,
- ProtocolVersionMax,
- m1.config.ProtocolVersion,
- m1.config.DelegateProtocolMin,
- m1.config.DelegateProtocolMax,
- m1.config.DelegateProtocolVersion,
- },
- }
- m1.aliveNode(&a4, nil, false)
-
- // Isolate m4 from UDP traffic by re-opening its listener on the wrong
- // port. This should force the TCP fallback path to be used.
- var err error
- if err = m4.udpListener.Close(); err != nil {
- t.Fatalf("err: %v", err)
- }
- udpAddr := &net.UDPAddr{IP: ip4.String(), Port: 9999}
- if m4.udpListener, err = net.ListenUDP("udp", udpAddr); err != nil {
- t.Fatalf("err: %v", err)
- }
-
- // Disable the TCP pings using the config mechanism.
- m1.config.DisableTcpPings = true
-
- // Have node m1 probe m4.
- n := m1.nodeMap[addr4.String()]
- startProbe := time.Now()
- m1.probeNode(n)
- probeTime := time.Now().Sub(startProbe)
-
- // Node should be reported suspect.
- if n.State != stateSuspect {
- t.Fatalf("expect node to be suspect")
- }
-
- // Make sure TCP activity didn't cause us to wait too long before
- // timing out.
- if probeTime > probeTimeMax {
- t.Fatalf("took to long to probe, %9.6f", probeTime.Seconds())
- }
-
- // Confirm at least one of the peers attempted an indirect probe.
- time.Sleep(probeTimeMax)
- if m2.sequenceNum != 1 && m3.sequenceNum != 1 {
- t.Fatalf("bad seqnos %v, %v", m2.sequenceNum, m3.sequenceNum)
- }
-}
-
-func TestMemberList_ProbeNode_FallbackTCP_OldProtocol(t *testing.T) {
- addr1 := getBindAddr()
- addr2 := getBindAddr()
- addr3 := getBindAddr()
- addr4 := getBindAddr()
- ip1 := addr1
- ip2 := addr2
- ip3 := addr3
- ip4 := addr4
-
- var probeTimeMax time.Duration
- m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
- c.ProbeTimeout = 10 * time.Millisecond
- c.ProbeInterval = 200 * time.Millisecond
- probeTimeMax = c.ProbeInterval + 20*time.Millisecond
- })
- defer m1.Shutdown()
-
- bindPort := m1.config.BindPort
-
- m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
- c.BindPort = bindPort
- })
- defer m2.Shutdown()
-
- m3 := HostMemberlist(addr3.String(), t, func(c *Config) {
- c.BindPort = bindPort
- })
- defer m3.Shutdown()
-
- m4 := HostMemberlist(addr4.String(), t, func(c *Config) {
- c.BindPort = bindPort
- })
- defer m4.Shutdown()
-
- a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1}
- m1.aliveNode(&a1, nil, true)
- a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1}
- m1.aliveNode(&a2, nil, false)
- a3 := alive{Node: addr3.String(), Addr: ip3.String(), Port: uint16(bindPort), Incarnation: 1}
- m1.aliveNode(&a3, nil, false)
-
- // Set up m4 so that it doesn't understand a version of the protocol
- // that supports TCP pings.
- a4 := alive{
- Node: addr4.String(),
- Addr: ip4.String(),
- Port: uint16(bindPort),
- Incarnation: 1,
- Vsn: []uint8{
- ProtocolVersionMin,
- ProtocolVersion2Compatible,
- ProtocolVersion2Compatible,
- m1.config.DelegateProtocolMin,
- m1.config.DelegateProtocolMax,
- m1.config.DelegateProtocolVersion,
- },
- }
- m1.aliveNode(&a4, nil, false)
-
- // Isolate m4 from UDP traffic by re-opening its listener on the wrong
- // port. This should force the TCP fallback path to be used.
- var err error
- if err = m4.udpListener.Close(); err != nil {
- t.Fatalf("err: %v", err)
- }
- udpAddr := &net.UDPAddr{IP: ip4.String(), Port: 9999}
- if m4.udpListener, err = net.ListenUDP("udp", udpAddr); err != nil {
- t.Fatalf("err: %v", err)
- }
-
- // Have node m1 probe m4.
- n := m1.nodeMap[addr4.String()]
- startProbe := time.Now()
- m1.probeNode(n)
- probeTime := time.Now().Sub(startProbe)
-
- // Node should be reported suspect.
- if n.State != stateSuspect {
- t.Fatalf("expect node to be suspect")
- }
-
- // Make sure TCP activity didn't cause us to wait too long before
- // timing out.
- if probeTime > probeTimeMax {
- t.Fatalf("took to long to probe, %9.6f", probeTime.Seconds())
- }
-
- // Confirm at least one of the peers attempted an indirect probe.
- time.Sleep(probeTimeMax)
- if m2.sequenceNum != 1 && m3.sequenceNum != 1 {
- t.Fatalf("bad seqnos %v, %v", m2.sequenceNum, m3.sequenceNum)
- }
-}
-*/
-//
-//func TestMemberList_ProbeNode_Awareness_Degraded(t *testing.T) {
-// addr1 := getBindAddr()
-// addr2 := getBindAddr()
-// addr3 := getBindAddr()
-// addr4 := getBindAddr()
-// ip1 := addr1
-// ip2 := addr2
-// ip3 := addr3
-// ip4 := addr4
-//
-// var probeTimeMin time.Duration
-// m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
-// c.ProbeTimeout = 10 * time.Millisecond
-// c.ProbeInterval = 200 * time.Millisecond
-// probeTimeMin = 2*c.ProbeInterval - 50*time.Millisecond
-// })
-// defer m1.Shutdown()
-//
-// bindPort := m1.config.BindPort
-//
-// m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// c.ProbeTimeout = 10 * time.Millisecond
-// c.ProbeInterval = 200 * time.Millisecond
-// })
-// defer m2.Shutdown()
-//
-// m3 := HostMemberlist(addr3.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// c.ProbeTimeout = 10 * time.Millisecond
-// c.ProbeInterval = 200 * time.Millisecond
-// })
-// defer m3.Shutdown()
-//
-// a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m1.config.BuildVsnArray()}
-// m1.aliveNode(&a1, nil, true)
-// a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m2.config.BuildVsnArray()}
-// m1.aliveNode(&a2, nil, false)
-// a3 := alive{Node: addr3.String(), Addr: ip3.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m3.config.BuildVsnArray()}
-// m1.aliveNode(&a3, nil, false)
-//
-// vsn4 := []uint8{
-// ProtocolVersionMin, ProtocolVersionMax, ProtocolVersionMin,
-// 1, 1, 1,
-// }
-// // Node 4 never gets started.
-// a4 := alive{Node: addr4.String(), Addr: ip4.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: vsn4}
-// m1.aliveNode(&a4, nil, false)
-//
-// // Start the health in a degraded state.
-// m1.awareness.ApplyDelta(1)
-// if score := m1.GetHealthScore(); score != 1 {
-// t.Fatalf("bad: %d", score)
-// }
-//
-// // Have node m1 probe m4.
-// n := m1.nodeMap[addr4.String()]
-// startProbe := time.Now()
-// m1.probeNode(n)
-// probeTime := time.Now().Sub(startProbe)
-//
-// // Node should be reported suspect.
-// if n.State != StateSuspect {
-// t.Fatalf("expect node to be suspect")
-// }
-//
-// // Make sure we timed out approximately on time (note that we accounted
-// // for the slowed-down failure detector in the probeTimeMin calculation.
-// if probeTime < probeTimeMin {
-// t.Fatalf("probed too quickly, %9.6f", probeTime.Seconds())
-// }
-//
-// // Confirm at least one of the peers attempted an indirect probe.
-// if m2.sequenceNum != 1 && m3.sequenceNum != 1 {
-// t.Fatalf("bad seqnos %v, %v", m2.sequenceNum, m3.sequenceNum)
-// }
-//
-// // We should have gotten all the nacks, so our score should remain the
-// // same, since we didn't get a successful probe.
-// if score := m1.GetHealthScore(); score != 1 {
-// t.Fatalf("bad: %d", score)
-// }
-//}
-//
-//func TestMemberList_ProbeNode_Wrong_VSN(t *testing.T) {
-// addr1 := getBindAddr()
-// addr2 := getBindAddr()
-// addr3 := getBindAddr()
-// addr4 := getBindAddr()
-// ip1 := addr1
-// ip2 := addr2
-// ip3 := addr3
-// ip4 := addr4
-//
-// m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
-// c.ProbeTimeout = 10 * time.Millisecond
-// c.ProbeInterval = 200 * time.Millisecond
-// })
-// defer m1.Shutdown()
-//
-// bindPort := m1.config.BindPort
-//
-// m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// c.ProbeTimeout = 10 * time.Millisecond
-// c.ProbeInterval = 200 * time.Millisecond
-// })
-// defer m2.Shutdown()
-//
-// m3 := HostMemberlist(addr3.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// c.ProbeTimeout = 10 * time.Millisecond
-// c.ProbeInterval = 200 * time.Millisecond
-// })
-// defer m3.Shutdown()
-//
-// a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m1.config.BuildVsnArray()}
-// m1.aliveNode(&a1, nil, true)
-// a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m2.config.BuildVsnArray()}
-// m1.aliveNode(&a2, nil, false)
-// a3 := alive{Node: addr3.String(), Addr: ip3.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m3.config.BuildVsnArray()}
-// m1.aliveNode(&a3, nil, false)
-//
-// vsn4 := []uint8{
-// 0, 0, 0,
-// 0, 0, 0,
-// }
-// // Node 4 never gets started.
-// a4 := alive{Node: addr4.String(), Addr: ip4.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: vsn4}
-// m1.aliveNode(&a4, nil, false)
-//
-// // Start the health in a degraded state.
-// m1.awareness.ApplyDelta(1)
-// if score := m1.GetHealthScore(); score != 1 {
-// t.Fatalf("bad: %d", score)
-// }
-//
-// // Have node m1 probe m4.
-// n, ok := m1.nodeMap[addr4.String()]
-// if ok || n != nil {
-// t.Fatalf("expect node a4 to be not taken into account, because of its wrong version")
-// }
-//}
-//
-//func TestMemberList_ProbeNode_Awareness_Improved(t *testing.T) {
-// addr1 := getBindAddr()
-// addr2 := getBindAddr()
-// ip1 := addr1
-// ip2 := addr2
-//
-// m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
-// c.ProbeTimeout = 10 * time.Millisecond
-// c.ProbeInterval = 200 * time.Millisecond
-// })
-// defer m1.Shutdown()
-//
-// bindPort := m1.config.BindPort
-//
-// m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// })
-// defer m2.Shutdown()
-//
-// a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m1.config.BuildVsnArray()}
-// m1.aliveNode(&a1, nil, true)
-// a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m2.config.BuildVsnArray()}
-// m1.aliveNode(&a2, nil, false)
-//
-// // Start the health in a degraded state.
-// m1.awareness.ApplyDelta(1)
-// if score := m1.GetHealthScore(); score != 1 {
-// t.Fatalf("bad: %d", score)
-// }
-//
-// // Have node m1 probe m2.
-// n := m1.nodeMap[addr2.String()]
-// m1.probeNode(n)
-//
-// // Node should be reported alive.
-// if n.State != StateAlive {
-// t.Fatalf("expect node to be suspect")
-// }
-//
-// // Our score should have improved since we did a good probe.
-// if score := m1.GetHealthScore(); score != 0 {
-// t.Fatalf("bad: %d", score)
-// }
-//}
-//
-//func TestMemberList_ProbeNode_Awareness_MissedNack(t *testing.T) {
-// addr1 := getBindAddr()
-// addr2 := getBindAddr()
-// addr3 := getBindAddr()
-// addr4 := getBindAddr()
-// ip1 := addr1
-// ip2 := addr2
-// ip3 := addr3
-// ip4 := addr4
-//
-// var probeTimeMax time.Duration
-// m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
-// c.ProbeTimeout = 10 * time.Millisecond
-// c.ProbeInterval = 200 * time.Millisecond
-// probeTimeMax = c.ProbeInterval + 50*time.Millisecond
-// })
-// defer m1.Shutdown()
-//
-// bindPort := m1.config.BindPort
-//
-// m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// c.ProbeTimeout = 10 * time.Millisecond
-// c.ProbeInterval = 200 * time.Millisecond
-// })
-// defer m2.Shutdown()
-//
-// a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m1.config.BuildVsnArray()}
-// m1.aliveNode(&a1, nil, true)
-// a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m1.config.BuildVsnArray()}
-// m1.aliveNode(&a2, nil, false)
-//
-// vsn := m1.config.BuildVsnArray()
-// // Node 3 and node 4 never get started.
-// a3 := alive{Node: addr3.String(), Addr: ip3.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: vsn}
-// m1.aliveNode(&a3, nil, false)
-// a4 := alive{Node: addr4.String(), Addr: ip4.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: vsn}
-// m1.aliveNode(&a4, nil, false)
-//
-// // Make sure health looks good.
-// if score := m1.GetHealthScore(); score != 0 {
-// t.Fatalf("bad: %d", score)
-// }
-//
-// // Have node m1 probe m4.
-// n := m1.nodeMap[addr4.String()]
-// startProbe := time.Now()
-// m1.probeNode(n)
-// probeTime := time.Now().Sub(startProbe)
-//
-// // Node should be reported suspect.
-//
-// m1.nodeLock.Lock()
-// if n.State != StateSuspect {
-// t.Fatalf("expect node to be suspect")
-// }
-// m1.nodeLock.Unlock()
-//
-// // Make sure we timed out approximately on time.
-// if probeTime > probeTimeMax {
-// t.Fatalf("took to long to probe, %9.6f", probeTime.Seconds())
-// }
-//
-// // We should have gotten dinged for the missed nack. Note that the code under
-// // test is waiting for probeTimeMax and then doing some other work before it
-// // updates the awareness, so we need to wait some extra time. Rather than just
-// // add longer and longer sleeps, we'll retry a few times.
-// iretry.Run(t, func(r *iretry.R) {
-// if score := m1.GetHealthScore(); score != 1 {
-// r.Fatalf("expected health score to decrement on missed nack. want %d, "+
-// "got: %d", 1, score)
-// }
-// })
-//}
-//
-//func TestMemberList_ProbeNode_Awareness_OldProtocol(t *testing.T) {
-// addr1 := getBindAddr()
-// addr2 := getBindAddr()
-// addr3 := getBindAddr()
-// addr4 := getBindAddr()
-// ip1 := addr1
-// ip2 := addr2
-// ip3 := addr3
-// ip4 := addr4
-//
-// var probeTimeMax time.Duration
-// m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
-// c.ProbeTimeout = 10 * time.Millisecond
-// c.ProbeInterval = 200 * time.Millisecond
-// probeTimeMax = c.ProbeInterval + 20*time.Millisecond
-// })
-// defer m1.Shutdown()
-//
-// bindPort := m1.config.BindPort
-//
-// m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// })
-// defer m2.Shutdown()
-//
-// m3 := HostMemberlist(addr3.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// })
-// defer m3.Shutdown()
-//
-// a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1}
-// m1.aliveNode(&a1, nil, true)
-// a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1}
-// m1.aliveNode(&a2, nil, false)
-// a3 := alive{Node: addr3.String(), Addr: ip3.String(), Port: uint16(bindPort), Incarnation: 1}
-// m1.aliveNode(&a3, nil, false)
-//
-// // Node 4 never gets started.
-// a4 := alive{Node: addr4.String(), Addr: ip4.String(), Port: uint16(bindPort), Incarnation: 1}
-// m1.aliveNode(&a4, nil, false)
-//
-// // Make sure health looks good.
-// if score := m1.GetHealthScore(); score != 0 {
-// t.Fatalf("bad: %d", score)
-// }
-//
-// // Have node m1 probe m4.
-// n := m1.nodeMap[addr4.String()]
-// startProbe := time.Now()
-// m1.probeNode(n)
-// probeTime := time.Now().Sub(startProbe)
-//
-// // Node should be reported suspect.
-// if n.State != StateSuspect {
-// t.Fatalf("expect node to be suspect")
-// }
-//
-// // Make sure we timed out approximately on time.
-// if probeTime > probeTimeMax {
-// t.Fatalf("took to long to probe, %9.6f", probeTime.Seconds())
-// }
-//
-// // Confirm at least one of the peers attempted an indirect probe.
-// time.Sleep(probeTimeMax)
-// if m2.sequenceNum != 1 && m3.sequenceNum != 1 {
-// t.Fatalf("bad seqnos %v, %v", m2.sequenceNum, m3.sequenceNum)
-// }
-//
-// // Since we are using the old protocol here, we should have gotten dinged
-// // for a failed health check.
-// if score := m1.GetHealthScore(); score != 1 {
-// t.Fatalf("bad: %d", score)
-// }
-//}
-//
-//func TestMemberList_ProbeNode_Buddy(t *testing.T) {
-// addr1 := getBindAddr()
-// addr2 := getBindAddr()
-// ip1 := addr1
-// ip2 := addr2
-//
-// m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
-// c.ProbeTimeout = time.Millisecond
-// c.ProbeInterval = 10 * time.Millisecond
-// })
-// defer m1.Shutdown()
-//
-// bindPort := m1.config.BindPort
-//
-// m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// })
-// defer m2.Shutdown()
-//
-// a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m1.config.BuildVsnArray()}
-// a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m2.config.BuildVsnArray()}
-//
-// m1.aliveNode(&a1, nil, true)
-// m1.aliveNode(&a2, nil, false)
-// m2.aliveNode(&a2, nil, true)
-//
-// // Force the state to suspect so we piggyback a suspect message with the ping.
-// // We should see this get refuted later, and the ping will succeed.
-// n := m1.nodeMap[addr2.String()]
-// n.State = StateSuspect
-// m1.probeNode(n)
-//
-// // Make sure a ping was sent.
-// if m1.sequenceNum != 1 {
-// t.Fatalf("bad seqno %v", m1.sequenceNum)
-// }
-//
-// // Check a broadcast is queued.
-// if num := m2.broadcasts.NumQueued(); num != 1 {
-// t.Fatalf("expected only one queued message: %d", num)
-// }
-//
-// // Should be alive msg.
-// if messageType(m2.broadcasts.orderedView(true)[0].b.Message()[0]) != aliveMsg {
-// t.Fatalf("expected queued alive msg")
-// }
-//}
-//
-//func TestMemberList_ProbeNode(t *testing.T) {
-// addr1 := getBindAddr()
-// addr2 := getBindAddr()
-// ip1 := addr1
-// ip2 := addr2
-//
-// m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
-// c.ProbeTimeout = time.Millisecond
-// c.ProbeInterval = 10 * time.Millisecond
-// })
-// defer m1.Shutdown()
-//
-// bindPort := m1.config.BindPort
-//
-// m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// })
-// defer m2.Shutdown()
-//
-// a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1}
-// m1.aliveNode(&a1, nil, true)
-// a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1}
-// m1.aliveNode(&a2, nil, false)
-//
-// n := m1.nodeMap[addr2.String()]
-// m1.probeNode(n)
-//
-// // Should be marked alive
-// if n.State != StateAlive {
-// t.Fatalf("Expect node to be alive")
-// }
-//
-// // Should increment seqno
-// if m1.sequenceNum != 1 {
-// t.Fatalf("bad seqno %v", m1.sequenceNum)
-// }
-//}
-//
-//func TestMemberList_Ping(t *testing.T) {
-// addr1 := getBindAddr()
-// addr2 := getBindAddr()
-// ip1 := addr1
-// ip2 := addr2
-//
-// m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
-// c.ProbeTimeout = 1 * time.Second
-// c.ProbeInterval = 10 * time.Second
-// })
-// defer m1.Shutdown()
-//
-// bindPort := m1.config.BindPort
-//
-// m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// })
-// defer m2.Shutdown()
-//
-// a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1}
-// m1.aliveNode(&a1, nil, true)
-// a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1}
-// m1.aliveNode(&a2, nil, false)
-//
-// // Do a legit ping.
-// n := m1.nodeMap[addr2.String()]
-// addr, err := net.ResolveUDPAddr("udp", net.JoinHostPort(addr2.String(), strconv.Itoa(bindPort)))
-// if err != nil {
-// t.Fatalf("err: %v", err)
-// }
-// rtt, err := m1.Ping(n.Name, addr)
-// if err != nil {
-// t.Fatalf("err: %v", err)
-// }
-// if !(rtt > 0) {
-// t.Fatalf("bad: %v", rtt)
-// }
-//
-// // This ping has a bad node name so should timeout.
-// _, err = m1.Ping("bad", addr)
-// if _, ok := err.(NoPingResponseError); !ok || err == nil {
-// t.Fatalf("bad: %v", err)
-// }
-//}
-//
-//func TestMemberList_ResetNodes(t *testing.T) {
-// m := GetMemberlist(t, func(c *Config) {
-// c.GossipToTheDeadTime = 100 * time.Millisecond
-// })
-// defer m.Shutdown()
-//
-// a1 := alive{Node: "test1", Addr: string([]byte{127, 0, 0, 1}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a1, nil, false)
-// a2 := alive{Node: "test2", Addr: string([]byte{127, 0, 0, 2}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a2, nil, false)
-// a3 := alive{Node: "test3", Addr: string([]byte{127, 0, 0, 3}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a3, nil, false)
-// d := dead{Node: "test2", Incarnation: 1}
-// m.deadNode(&d)
-//
-// m.resetNodes()
-// if len(m.nodes) != 3 {
-// t.Fatalf("Bad length")
-// }
-// if _, ok := m.nodeMap["test2"]; !ok {
-// t.Fatalf("test2 should not be unmapped")
-// }
-//
-// time.Sleep(200 * time.Millisecond)
-// m.resetNodes()
-// if len(m.nodes) != 2 {
-// t.Fatalf("Bad length")
-// }
-// if _, ok := m.nodeMap["test2"]; ok {
-// t.Fatalf("test2 should be unmapped")
-// }
-//}
-//
-//func TestMemberList_NextSeq(t *testing.T) {
-// m := &Memberlist{}
-// if m.nextSeqNo() != 1 {
-// t.Fatalf("bad sequence no")
-// }
-// if m.nextSeqNo() != 2 {
-// t.Fatalf("bad sequence no")
-// }
-//}
-//
-//func ackHandlerExists(t *testing.T, m *Memberlist, idx uint32) bool {
-// t.Helper()
-//
-// m.ackLock.Lock()
-// _, ok := m.ackHandlers[idx]
-// m.ackLock.Unlock()
-//
-// return ok
-//}
-//
-//func TestMemberList_setProbeChannels(t *testing.T) {
-// m := &Memberlist{ackHandlers: make(map[uint32]*ackHandler)}
-//
-// ch := make(chan ackMessage, 1)
-// m.setProbeChannels(0, ch, nil, 10*time.Millisecond)
-//
-// require.True(t, ackHandlerExists(t, m, 0), "missing handler")
-//
-// time.Sleep(20 * time.Millisecond)
-//
-// require.False(t, ackHandlerExists(t, m, 0), "non-reaped handler")
-//}
-//
-//func TestMemberList_setAckHandler(t *testing.T) {
-// m := &Memberlist{ackHandlers: make(map[uint32]*ackHandler)}
-//
-// f := func([]byte, time.Time) {}
-// m.setAckHandler(0, f, 10*time.Millisecond)
-//
-// require.True(t, ackHandlerExists(t, m, 0), "missing handler")
-//
-// time.Sleep(20 * time.Millisecond)
-//
-// require.False(t, ackHandlerExists(t, m, 0), "non-reaped handler")
-//}
-//
-//func TestMemberList_invokeAckHandler(t *testing.T) {
-// m := &Memberlist{ackHandlers: make(map[uint32]*ackHandler)}
-//
-// // Does nothing
-// m.invokeAckHandler(ackResp{}, time.Now())
-//
-// var b bool
-// f := func(payload []byte, timestamp time.Time) { b = true }
-// m.setAckHandler(0, f, 10*time.Millisecond)
-//
-// // Should set b
-// m.invokeAckHandler(ackResp{0, nil}, time.Now())
-// if !b {
-// t.Fatalf("b not set")
-// }
-//
-// require.False(t, ackHandlerExists(t, m, 0), "non-reaped handler")
-//}
-//
-//func TestMemberList_invokeAckHandler_Channel_Ack(t *testing.T) {
-// m := &Memberlist{ackHandlers: make(map[uint32]*ackHandler)}
-//
-// ack := ackResp{0, []byte{0, 0, 0}}
-//
-// // Does nothing
-// m.invokeAckHandler(ack, time.Now())
-//
-// ackCh := make(chan ackMessage, 1)
-// nackCh := make(chan struct{}, 1)
-// m.setProbeChannels(0, ackCh, nackCh, 10*time.Millisecond)
-//
-// // Should send message
-// m.invokeAckHandler(ack, time.Now())
-//
-// select {
-// case v := <-ackCh:
-// if v.Complete != true {
-// t.Fatalf("Bad value")
-// }
-// if bytes.Compare(v.Payload, ack.Payload) != 0 {
-// t.Fatalf("wrong payload. expected: %v; actual: %v", ack.Payload, v.Payload)
-// }
-//
-// case <-nackCh:
-// t.Fatalf("should not get a nack")
-//
-// default:
-// t.Fatalf("message not sent")
-// }
-//
-// require.False(t, ackHandlerExists(t, m, 0), "non-reaped handler")
-//}
-//
-//func TestMemberList_invokeAckHandler_Channel_Nack(t *testing.T) {
-// m := &Memberlist{ackHandlers: make(map[uint32]*ackHandler)}
-//
-// nack := nackResp{0}
-//
-// // Does nothing.
-// m.invokeNackHandler(nack)
-//
-// ackCh := make(chan ackMessage, 1)
-// nackCh := make(chan struct{}, 1)
-// m.setProbeChannels(0, ackCh, nackCh, 10*time.Millisecond)
-//
-// // Should send message.
-// m.invokeNackHandler(nack)
-//
-// select {
-// case <-ackCh:
-// t.Fatalf("should not get an ack")
-//
-// case <-nackCh:
-// // Good.
-//
-// default:
-// t.Fatalf("message not sent")
-// }
-//
-// // Getting a nack doesn't reap the handler so that we can still forward
-// // an ack up to the reap time, if we get one.
-// require.True(t, ackHandlerExists(t, m, 0), "handler should not be reaped")
-//
-// ack := ackResp{0, []byte{0, 0, 0}}
-// m.invokeAckHandler(ack, time.Now())
-//
-// select {
-// case v := <-ackCh:
-// if v.Complete != true {
-// t.Fatalf("Bad value")
-// }
-// if bytes.Compare(v.Payload, ack.Payload) != 0 {
-// t.Fatalf("wrong payload. expected: %v; actual: %v", ack.Payload, v.Payload)
-// }
-//
-// case <-nackCh:
-// t.Fatalf("should not get a nack")
-//
-// default:
-// t.Fatalf("message not sent")
-// }
-//
-// require.False(t, ackHandlerExists(t, m, 0), "non-reaped handler")
-//}
-//
-//func TestMemberList_AliveNode_NewNode(t *testing.T) {
-// ch := make(chan NodeEvent, 1)
-// m := GetMemberlist(t, func(c *Config) {
-// c.Events = &ChannelEventDelegate{ch}
-// })
-// defer m.Shutdown()
-//
-// a := alive{Node: "test", Addr: string([]byte{127, 0, 0, 1}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, false)
-//
-// if len(m.nodes) != 1 {
-// t.Fatalf("should add node")
-// }
-//
-// state, ok := m.nodeMap["test"]
-// if !ok {
-// t.Fatalf("should map node")
-// }
-//
-// if state.Incarnation != 1 {
-// t.Fatalf("bad incarnation")
-// }
-// if state.State != StateAlive {
-// t.Fatalf("bad state")
-// }
-// if time.Now().Sub(state.StateChange) > time.Second {
-// t.Fatalf("bad change delta")
-// }
-//
-// // Check for a join message
-// select {
-// case e := <-ch:
-// if e.Node.Name != "test" {
-// t.Fatalf("bad node name")
-// }
-// default:
-// t.Fatalf("no join message")
-// }
-//
-// // Check a broad cast is queued
-// if m.broadcasts.NumQueued() != 1 {
-// t.Fatalf("expected queued message")
-// }
-//}
-//
-//func TestMemberList_AliveNode_SuspectNode(t *testing.T) {
-// ch := make(chan NodeEvent, 1)
-// ted := &toggledEventDelegate{
-// real: &ChannelEventDelegate{ch},
-// }
-// m := GetMemberlist(t, func(c *Config) {
-// c.Events = ted
-// })
-// defer m.Shutdown()
-//
-// a := alive{Node: "test", Addr: string([]byte{127, 0, 0, 1}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, false)
-//
-// // Listen only after first join
-// ted.Toggle(true)
-//
-// // Make suspect
-// state := m.nodeMap["test"]
-// state.State = StateSuspect
-// state.StateChange = state.StateChange.Add(-time.Hour)
-//
-// // Old incarnation number, should not change
-// m.aliveNode(&a, nil, false)
-// if state.State != StateSuspect {
-// t.Fatalf("update with old incarnation!")
-// }
-//
-// // Should reset to alive now
-// a.Incarnation = 2
-// m.aliveNode(&a, nil, false)
-// if state.State != StateAlive {
-// t.Fatalf("no update with new incarnation!")
-// }
-//
-// if time.Now().Sub(state.StateChange) > time.Second {
-// t.Fatalf("bad change delta")
-// }
-//
-// // Check for a no join message
-// select {
-// case <-ch:
-// t.Fatalf("got bad join message")
-// default:
-// }
-//
-// // Check a broad cast is queued
-// if m.broadcasts.NumQueued() != 1 {
-// t.Fatalf("expected queued message")
-// }
-//}
-//
-//func TestMemberList_AliveNode_Idempotent(t *testing.T) {
-// ch := make(chan NodeEvent, 1)
-// ted := &toggledEventDelegate{
-// real: &ChannelEventDelegate{ch},
-// }
-// m := GetMemberlist(t, func(c *Config) {
-// c.Events = ted
-// })
-// defer m.Shutdown()
-//
-// a := alive{Node: "test", Addr: string([]byte{127, 0, 0, 1}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, false)
-//
-// // Listen only after first join
-// ted.Toggle(true)
-//
-// // Make suspect
-// state := m.nodeMap["test"]
-// stateTime := state.StateChange
-//
-// // Should reset to alive now
-// a.Incarnation = 2
-// m.aliveNode(&a, nil, false)
-// if state.State != StateAlive {
-// t.Fatalf("non idempotent")
-// }
-//
-// if stateTime != state.StateChange {
-// t.Fatalf("should not change state")
-// }
-//
-// // Check for a no join message
-// select {
-// case <-ch:
-// t.Fatalf("got bad join message")
-// default:
-// }
-//
-// // Check a broad cast is queued
-// if m.broadcasts.NumQueued() != 1 {
-// t.Fatalf("expected only one queued message")
-// }
-//}
-//
-//type toggledEventDelegate struct {
-// mu sync.Mutex
-// real EventDelegate
-// enabled bool
-//}
-//
-//func (d *toggledEventDelegate) Toggle(enabled bool) {
-// d.mu.Lock()
-// defer d.mu.Unlock()
-// d.enabled = enabled
-//}
-//
-//// NotifyJoin is invoked when a node is detected to have joined.
-//// The Node argument must not be modified.
-//func (d *toggledEventDelegate) NotifyJoin(n *Node) {
-// d.mu.Lock()
-// defer d.mu.Unlock()
-// if d.enabled {
-// d.real.NotifyJoin(n)
-// }
-//}
-//
-//// NotifyLeave is invoked when a node is detected to have left.
-//// The Node argument must not be modified.
-//func (d *toggledEventDelegate) NotifyLeave(n *Node) {
-// d.mu.Lock()
-// defer d.mu.Unlock()
-// if d.enabled {
-// d.real.NotifyLeave(n)
-// }
-//}
-//
-//// NotifyUpdate is invoked when a node is detected to have
-//// updated, usually involving the meta data. The Node argument
-//// must not be modified.
-//func (d *toggledEventDelegate) NotifyUpdate(n *Node) {
-// d.mu.Lock()
-// defer d.mu.Unlock()
-// if d.enabled {
-// d.real.NotifyUpdate(n)
-// }
-//}
-//
-//func (d *toggledEventDelegate) NotifyWeight(n *Node) {
-// d.mu.Lock()
-// defer d.mu.Unlock()
-// if d.enabled {
-// d.real.NotifyWeight(n)
-// }
-//}
-//
-//func (d *toggledEventDelegate) NotifySuspectSateChange(n *Node) {
-// d.mu.Lock()
-// defer d.mu.Unlock()
-// if d.enabled {
-// d.real.NotifySuspectSateChange(n)
-// }
-//}
-//
-//// Serf Bug: GH-58, Meta data does not update
-//func TestMemberList_AliveNode_ChangeMeta(t *testing.T) {
-// ch := make(chan NodeEvent, 1)
-// ted := &toggledEventDelegate{
-// real: &ChannelEventDelegate{ch},
-// }
-//
-// m := GetMemberlist(t, func(c *Config) {
-// c.Events = ted
-// })
-// defer m.Shutdown()
-//
-// a := alive{
-// Node: "test",
-// Addr: string([]byte{127, 0, 0, 1}),
-// Meta: []byte("val1"),
-// Incarnation: 1,
-// Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, false)
-//
-// // Listen only after first join
-// ted.Toggle(true)
-//
-// // Make suspect
-// state := m.nodeMap["test"]
-//
-// // Should reset to alive now
-// a.Incarnation = 2
-// a.Meta = []byte("val2")
-// m.aliveNode(&a, nil, false)
-//
-// // Check updates
-// if bytes.Compare(state.Meta, a.Meta) != 0 {
-// t.Fatalf("meta did not update")
-// }
-//
-// // Check for a NotifyUpdate
-// select {
-// case e := <-ch:
-// if e.Event != NodeUpdate {
-// t.Fatalf("bad event: %v", e)
-// }
-// if !reflect.DeepEqual(*e.Node, state.Node) {
-// t.Fatalf("expected %v, got %v", *e.Node, state.Node)
-// }
-// if bytes.Compare(e.Node.Meta, a.Meta) != 0 {
-// t.Fatalf("meta did not update")
-// }
-// default:
-// t.Fatalf("missing event!")
-// }
-//
-//}
-//
-//func TestMemberList_AliveNode_Refute(t *testing.T) {
-// m := GetMemberlist(t, nil)
-// defer m.Shutdown()
-//
-// a := alive{Node: m.config.Name, Addr: string([]byte{127, 0, 0, 1}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, true)
-//
-// // Clear queue
-// m.broadcasts.Reset()
-//
-// // Conflicting alive
-// s := alive{
-// Node: m.config.Name,
-// Addr: string([]byte{127, 0, 0, 1}),
-// Incarnation: 2,
-// Meta: []byte("foo"),
-// Vsn: m.config.BuildVsnArray(),
-// }
-// m.aliveNode(&s, nil, false)
-//
-// state := m.nodeMap[m.config.Name]
-// if state.State != StateAlive {
-// t.Fatalf("should still be alive")
-// }
-// if state.Meta != nil {
-// t.Fatalf("meta should still be nil")
-// }
-//
-// // Check a broad cast is queued
-// if num := m.broadcasts.NumQueued(); num != 1 {
-// t.Fatalf("expected only one queued message: %d",
-// num)
-// }
-//
-// // Should be alive mesg
-// if messageType(m.broadcasts.orderedView(true)[0].b.Message()[0]) != aliveMsg {
-// t.Fatalf("expected queued alive msg")
-// }
-//}
-//
-//func TestMemberList_AliveNode_Conflict(t *testing.T) {
-// m := GetMemberlist(t, func(c *Config) {
-// c.DeadNodeReclaimTime = 10 * time.Millisecond
-// })
-// defer m.Shutdown()
-//
-// nodeName := "test"
-// a := alive{Node: nodeName, Addr: string([]byte{127, 0, 0, 1}), Port: 8000, Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, true)
-//
-// // Clear queue
-// m.broadcasts.Reset()
-//
-// // Conflicting alive
-// s := alive{
-// Node: nodeName,
-// Addr: string([]byte{127, 0, 0, 2}),
-// Port: 9000,
-// Incarnation: 2,
-// Meta: []byte("foo"),
-// Vsn: m.config.BuildVsnArray(),
-// }
-// m.aliveNode(&s, nil, false)
-//
-// state := m.nodeMap[nodeName]
-// if state.State != StateAlive {
-// t.Fatalf("should still be alive")
-// }
-// if state.Meta != nil {
-// t.Fatalf("meta should still be nil")
-// }
-// if state.Addr == string([]byte{127, 0, 0, 2}) {
-// t.Fatalf("address should not be updated")
-// }
-// if state.Port == 9000 {
-// t.Fatalf("port should not be updated")
-// }
-//
-// // Check a broad cast is queued
-// if num := m.broadcasts.NumQueued(); num != 0 {
-// t.Fatalf("expected 0 queued messages: %d", num)
-// }
-//
-// // Change the node to dead
-// d := dead{Node: nodeName, Incarnation: 2}
-// m.deadNode(&d)
-// m.broadcasts.Reset()
-//
-// state = m.nodeMap[nodeName]
-// if state.State != StateDead {
-// t.Fatalf("should be dead")
-// }
-//
-// time.Sleep(m.config.DeadNodeReclaimTime)
-//
-// // New alive node
-// s2 := alive{
-// Node: nodeName,
-// Addr: string([]byte{127, 0, 0, 2}),
-// Port: 9000,
-// Incarnation: 3,
-// Meta: []byte("foo"),
-// Vsn: m.config.BuildVsnArray(),
-// }
-// m.aliveNode(&s2, nil, false)
-//
-// state = m.nodeMap[nodeName]
-// if state.State != StateAlive {
-// t.Fatalf("should still be alive")
-// }
-// if !bytes.Equal(state.Meta, []byte("foo")) {
-// t.Fatalf("meta should be updated")
-// }
-// if state.Addr != string([]byte{127, 0, 0, 2}) {
-// t.Fatalf("address should be updated")
-// }
-// if state.Port != 9000 {
-// t.Fatalf("port should be updated")
-// }
-//}
-//
-//func TestMemberList_SuspectNode_NoNode(t *testing.T) {
-// m := GetMemberlist(t, nil)
-// defer m.Shutdown()
-//
-// s := suspect{Node: "test", Incarnation: 1}
-// m.suspectNode(&s)
-// if len(m.nodes) != 0 {
-// t.Fatalf("don't expect nodes")
-// }
-//}
-//
-//func TestMemberList_SuspectNode(t *testing.T) {
-// m := GetMemberlist(t, func(c *Config) {
-// c.ProbeInterval = time.Millisecond
-// c.SuspicionMult = 1
-// })
-// defer m.Shutdown()
-//
-// a := alive{Node: "test", Addr: string([]byte{127, 0, 0, 1}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, false)
-//
-// m.changeNode("test", func(state *nodeState) {
-// state.StateChange = state.StateChange.Add(-time.Hour)
-// })
-//
-// s := suspect{Node: "test", Incarnation: 1}
-// m.suspectNode(&s)
-//
-// if m.getNodeState("test") != StateSuspect {
-// t.Fatalf("Bad state")
-// }
-//
-// change := m.getNodeStateChange("test")
-// if time.Now().Sub(change) > time.Second {
-// t.Fatalf("bad change delta")
-// }
-//
-// // Check a broad cast is queued
-// if m.broadcasts.NumQueued() != 1 {
-// t.Fatalf("expected only one queued message")
-// }
-//
-// // Check its a suspect message
-// if messageType(m.broadcasts.orderedView(true)[0].b.Message()[0]) != suspectMsg {
-// t.Fatalf("expected queued suspect msg")
-// }
-//
-// // Wait for the timeout
-// time.Sleep(10 * time.Millisecond)
-//
-// if m.getNodeState("test") != StateDead {
-// t.Fatalf("Bad state")
-// }
-//
-// newChange := m.getNodeStateChange("test")
-// if time.Now().Sub(newChange) > time.Second {
-// t.Fatalf("bad change delta")
-// }
-// if !newChange.After(change) {
-// t.Fatalf("should increment time")
-// }
-//
-// // Check a broad cast is queued
-// if m.broadcasts.NumQueued() != 1 {
-// t.Fatalf("expected only one queued message")
-// }
-//
-// // Check its a suspect message
-// if messageType(m.broadcasts.orderedView(true)[0].b.Message()[0]) != deadMsg {
-// t.Fatalf("expected queued dead msg")
-// }
-//}
-//
-//func TestMemberList_SuspectNode_DoubleSuspect(t *testing.T) {
-// m := GetMemberlist(t, nil)
-// defer m.Shutdown()
-//
-// a := alive{Node: "test", Addr: string([]byte{127, 0, 0, 1}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, false)
-//
-// state := m.nodeMap["test"]
-// state.StateChange = state.StateChange.Add(-time.Hour)
-//
-// s := suspect{Node: "test", Incarnation: 1}
-// m.suspectNode(&s)
-//
-// if state.State != StateSuspect {
-// t.Fatalf("Bad state")
-// }
-//
-// change := state.StateChange
-// if time.Now().Sub(change) > time.Second {
-// t.Fatalf("bad change delta")
-// }
-//
-// // clear the broadcast queue
-// m.broadcasts.Reset()
-//
-// // Suspect again
-// m.suspectNode(&s)
-//
-// if state.StateChange != change {
-// t.Fatalf("unexpected state change")
-// }
-//
-// // Check a broad cast is queued
-// if m.broadcasts.NumQueued() != 0 {
-// t.Fatalf("expected only one queued message")
-// }
-//
-//}
-//
-//func TestMemberList_SuspectNode_OldSuspect(t *testing.T) {
-// m := GetMemberlist(t, nil)
-// defer m.Shutdown()
-//
-// a := alive{Node: "test", Addr: string([]byte{127, 0, 0, 1}), Incarnation: 10, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, false)
-//
-// state := m.nodeMap["test"]
-// state.StateChange = state.StateChange.Add(-time.Hour)
-//
-// // Clear queue
-// m.broadcasts.Reset()
-//
-// s := suspect{Node: "test", Incarnation: 1}
-// m.suspectNode(&s)
-//
-// if state.State != StateAlive {
-// t.Fatalf("Bad state")
-// }
-//
-// // Check a broad cast is queued
-// if m.broadcasts.NumQueued() != 0 {
-// t.Fatalf("expected only one queued message")
-// }
-//}
-//
-//func TestMemberList_SuspectNode_Refute(t *testing.T) {
-// m := GetMemberlist(t, nil)
-// defer m.Shutdown()
-//
-// a := alive{Node: m.config.Name, Addr: string([]byte{127, 0, 0, 1}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, true)
-//
-// // Clear queue
-// m.broadcasts.Reset()
-//
-// // Make sure health is in a good state
-// if score := m.GetHealthScore(); score != 0 {
-// t.Fatalf("bad: %d", score)
-// }
-//
-// s := suspect{Node: m.config.Name, Incarnation: 1}
-// m.suspectNode(&s)
-//
-// state := m.nodeMap[m.config.Name]
-// if state.State != StateAlive {
-// t.Fatalf("should still be alive")
-// }
-//
-// // Check a broad cast is queued
-// if m.broadcasts.NumQueued() != 1 {
-// t.Fatalf("expected only one queued message")
-// }
-//
-// // Should be alive mesg
-// if messageType(m.broadcasts.orderedView(true)[0].b.Message()[0]) != aliveMsg {
-// t.Fatalf("expected queued alive msg")
-// }
-//
-// // Health should have been dinged
-// if score := m.GetHealthScore(); score != 1 {
-// t.Fatalf("bad: %d", score)
-// }
-//}
-//
-//func TestMemberList_DeadNode_NoNode(t *testing.T) {
-// m := GetMemberlist(t, nil)
-// defer m.Shutdown()
-//
-// d := dead{Node: "test", Incarnation: 1}
-// m.deadNode(&d)
-// if len(m.nodes) != 0 {
-// t.Fatalf("don't expect nodes")
-// }
-//}
-//
-//func TestMemberList_DeadNodeLeft(t *testing.T) {
-// ch := make(chan NodeEvent, 1)
-//
-// m := GetMemberlist(t, func(c *Config) {
-// c.Events = &ChannelEventDelegate{ch}
-// })
-// defer m.Shutdown()
-//
-// nodeName := "node1"
-// s1 := alive{
-// Node: nodeName,
-// Addr: string([]byte{127, 0, 0, 1}),
-// Port: 8000,
-// Incarnation: 1,
-// Vsn: m.config.BuildVsnArray(),
-// }
-// m.aliveNode(&s1, nil, false)
-//
-// // Read the join event
-// <-ch
-//
-// d := dead{Node: nodeName, From: nodeName, Incarnation: 1}
-// m.deadNode(&d)
-//
-// // Read the dead event
-// <-ch
-//
-// state := m.nodeMap[nodeName]
-// if state.State != StateLeft {
-// t.Fatalf("Bad state")
-// }
-//
-// // Check a broad cast is queued
-// if m.broadcasts.NumQueued() != 1 {
-// t.Fatalf("expected only one queued message")
-// }
-//
-// // Check its a dead message
-// if messageType(m.broadcasts.orderedView(true)[0].b.Message()[0]) != deadMsg {
-// t.Fatalf("expected queued dead msg")
-// }
-//
-// // Clear queue
-// // m.broadcasts.Reset()
-//
-// // New alive node
-// s2 := alive{
-// Node: nodeName,
-// Addr: string([]byte{127, 0, 0, 2}),
-// Port: 9000,
-// Incarnation: 3,
-// Meta: []byte("foo"),
-// Vsn: m.config.BuildVsnArray(),
-// }
-// m.aliveNode(&s2, nil, false)
-//
-// // Read the join event
-// <-ch
-//
-// state = m.nodeMap[nodeName]
-// if state.State != StateAlive {
-// t.Fatalf("should still be alive")
-// }
-// if !bytes.Equal(state.Meta, []byte("foo")) {
-// t.Fatalf("meta should be updated")
-// }
-// if state.Addr != string([]byte{127, 0, 0, 2}) {
-// t.Fatalf("address should be updated")
-// }
-// if state.Port != 9000 {
-// t.Fatalf("port should be updated")
-// }
-//}
-//
-//func TestMemberList_DeadNode(t *testing.T) {
-// ch := make(chan NodeEvent, 1)
-//
-// m := GetMemberlist(t, func(c *Config) {
-// c.Events = &ChannelEventDelegate{ch}
-// })
-// defer m.Shutdown()
-//
-// a := alive{Node: "test", Addr: string([]byte{127, 0, 0, 1}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, false)
-//
-// // Read the join event
-// <-ch
-//
-// state := m.nodeMap["test"]
-// state.StateChange = state.StateChange.Add(-time.Hour)
-//
-// d := dead{Node: "test", Incarnation: 1}
-// m.deadNode(&d)
-//
-// if state.State != StateDead {
-// t.Fatalf("Bad state")
-// }
-//
-// change := state.StateChange
-// if time.Now().Sub(change) > time.Second {
-// t.Fatalf("bad change delta")
-// }
-//
-// select {
-// case leave := <-ch:
-// if leave.Event != NodeLeave || leave.Node.Name != "test" {
-// t.Fatalf("bad node name")
-// }
-// default:
-// t.Fatalf("no leave message")
-// }
-//
-// // Check a broad cast is queued
-// if m.broadcasts.NumQueued() != 1 {
-// t.Fatalf("expected only one queued message")
-// }
-//
-// // Check its a dead message
-// if messageType(m.broadcasts.orderedView(true)[0].b.Message()[0]) != deadMsg {
-// t.Fatalf("expected queued dead msg")
-// }
-//}
-//
-//func TestMemberList_DeadNode_Double(t *testing.T) {
-// ch := make(chan NodeEvent, 1)
-// m := GetMemberlist(t, nil)
-// defer m.Shutdown()
-//
-// a := alive{Node: "test", Addr: string([]byte{127, 0, 0, 1}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, false)
-//
-// state := m.nodeMap["test"]
-// state.StateChange = state.StateChange.Add(-time.Hour)
-//
-// d := dead{Node: "test", Incarnation: 1}
-// m.deadNode(&d)
-//
-// // Clear queue
-// m.broadcasts.Reset()
-//
-// // Notify after the first dead
-// m.config.Events = &ChannelEventDelegate{ch}
-//
-// // Should do nothing
-// d.Incarnation = 2
-// m.deadNode(&d)
-//
-// select {
-// case <-ch:
-// t.Fatalf("should not get leave")
-// default:
-// }
-//
-// // Check a broad cast is queued
-// if m.broadcasts.NumQueued() != 0 {
-// t.Fatalf("expected only one queued message")
-// }
-//}
-//
-//func TestMemberList_DeadNode_OldDead(t *testing.T) {
-// m := GetMemberlist(t, nil)
-// defer m.Shutdown()
-//
-// a := alive{Node: "test", Addr: string([]byte{127, 0, 0, 1}), Incarnation: 10, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, false)
-//
-// state := m.nodeMap["test"]
-// state.StateChange = state.StateChange.Add(-time.Hour)
-//
-// d := dead{Node: "test", Incarnation: 1}
-// m.deadNode(&d)
-//
-// if state.State != StateAlive {
-// t.Fatalf("Bad state")
-// }
-//}
-//
-//func TestMemberList_DeadNode_AliveReplay(t *testing.T) {
-// m := GetMemberlist(t, nil)
-// defer m.Shutdown()
-//
-// a := alive{Node: "test", Addr: string([]byte{127, 0, 0, 1}), Incarnation: 10, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, false)
-//
-// d := dead{Node: "test", Incarnation: 10}
-// m.deadNode(&d)
-//
-// // Replay alive at same incarnation
-// m.aliveNode(&a, nil, false)
-//
-// // Should remain dead
-// state, ok := m.nodeMap["test"]
-// if ok && state.State != StateDead {
-// t.Fatalf("Bad state")
-// }
-//}
-//
-//func TestMemberList_DeadNode_Refute(t *testing.T) {
-// m := GetMemberlist(t, nil)
-// defer m.Shutdown()
-//
-// a := alive{Node: m.config.Name, Addr: string([]byte{127, 0, 0, 1}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a, nil, true)
-//
-// // Clear queue
-// m.broadcasts.Reset()
-//
-// // Make sure health is in a good state
-// if score := m.GetHealthScore(); score != 0 {
-// t.Fatalf("bad: %d", score)
-// }
-//
-// d := dead{Node: m.config.Name, Incarnation: 1}
-// m.deadNode(&d)
-//
-// state := m.nodeMap[m.config.Name]
-// if state.State != StateAlive {
-// t.Fatalf("should still be alive")
-// }
-//
-// // Check a broad cast is queued
-// if m.broadcasts.NumQueued() != 1 {
-// t.Fatalf("expected only one queued message")
-// }
-//
-// // Should be alive mesg
-// if messageType(m.broadcasts.orderedView(true)[0].b.Message()[0]) != aliveMsg {
-// t.Fatalf("expected queued alive msg")
-// }
-//
-// // We should have been dinged
-// if score := m.GetHealthScore(); score != 1 {
-// t.Fatalf("bad: %d", score)
-// }
-//}
-//
-//func TestMemberList_MergeState(t *testing.T) {
-// m := GetMemberlist(t, nil)
-// defer m.Shutdown()
-//
-// a1 := alive{Node: "test1", Addr: string([]byte{127, 0, 0, 1}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a1, nil, false)
-// a2 := alive{Node: "test2", Addr: string([]byte{127, 0, 0, 2}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a2, nil, false)
-// a3 := alive{Node: "test3", Addr: string([]byte{127, 0, 0, 3}), Incarnation: 1, Vsn: m.config.BuildVsnArray()}
-// m.aliveNode(&a3, nil, false)
-//
-// s := suspect{Node: "test1", Incarnation: 1}
-// m.suspectNode(&s)
-//
-// remote := []pushNodeState{
-// pushNodeState{
-// Name: "test1",
-// Addr: string([]byte{127, 0, 0, 1}),
-// Incarnation: 2,
-// State: StateAlive,
-// },
-// pushNodeState{
-// Name: "test2",
-// Addr: string([]byte{127, 0, 0, 2}),
-// Incarnation: 1,
-// State: StateSuspect,
-// },
-// pushNodeState{
-// Name: "test3",
-// Addr: string([]byte{127, 0, 0, 3}),
-// Incarnation: 1,
-// State: StateDead,
-// },
-// pushNodeState{
-// Name: "test4",
-// Addr: string([]byte{127, 0, 0, 4}),
-// Incarnation: 2,
-// State: StateAlive,
-// },
-// }
-//
-// // Listen for changes
-// eventCh := make(chan NodeEvent, 1)
-// m.config.Events = &ChannelEventDelegate{eventCh}
-//
-// // Merge remote state
-// m.mergeState(remote)
-//
-// // Check the states
-// state := m.nodeMap["test1"]
-// if state.State != StateAlive || state.Incarnation != 2 {
-// t.Fatalf("Bad state %v", state)
-// }
-//
-// state = m.nodeMap["test2"]
-// if state.State != StateSuspect || state.Incarnation != 1 {
-// t.Fatalf("Bad state %v", state)
-// }
-//
-// state = m.nodeMap["test3"]
-// if state.State != StateSuspect {
-// t.Fatalf("Bad state %v", state)
-// }
-//
-// state = m.nodeMap["test4"]
-// if state.State != StateAlive || state.Incarnation != 2 {
-// t.Fatalf("Bad state %v", state)
-// }
-//
-// // Check the channels
-// select {
-// case e := <-eventCh:
-// if e.Event != NodeJoin || e.Node.Name != "test4" {
-// t.Fatalf("bad node %v", e)
-// }
-// default:
-// t.Fatalf("Expect join")
-// }
-//
-// select {
-// case e := <-eventCh:
-// t.Fatalf("Unexpect event: %v", e)
-// default:
-// }
-//}
-//
-//func TestMemberlist_Gossip(t *testing.T) {
-// ch := make(chan NodeEvent, 3)
-//
-// addr1 := getBindAddr()
-// addr2 := getBindAddr()
-// addr3 := getBindAddr()
-// ip1 := addr1
-// ip2 := addr2
-// ip3 := addr3
-//
-// m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
-// // Set the gossip interval fast enough to get a reasonable test,
-// // but slow enough to avoid "sendto: operation not permitted"
-// c.GossipInterval = 10 * time.Millisecond
-// })
-// defer m1.Shutdown()
-//
-// bindPort := m1.config.BindPort
-//
-// m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// c.Events = &ChannelEventDelegate{ch}
-// // Set the gossip interval fast enough to get a reasonable test,
-// // but slow enough to avoid "sendto: operation not permitted"
-// c.GossipInterval = 10 * time.Millisecond
-// })
-// defer m2.Shutdown()
-//
-// m3 := HostMemberlist(addr2.String(), t, func(c *Config) {
-// })
-// defer m3.Shutdown()
-//
-// a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m1.config.BuildVsnArray()}
-// m1.aliveNode(&a1, nil, true)
-// a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m2.config.BuildVsnArray()}
-// m1.aliveNode(&a2, nil, false)
-// a3 := alive{Node: addr3.String(), Addr: ip3.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m3.config.BuildVsnArray()}
-// m1.aliveNode(&a3, nil, false)
-//
-// // Gossip should send all this to m2. Retry a few times because it's UDP and
-// // timing and stuff makes this flaky without.
-// retry(t, 15, 250*time.Millisecond, func(failf func(string, ...interface{})) {
-// m1.gossip()
-//
-// time.Sleep(3 * time.Millisecond)
-//
-// if len(ch) < 3 {
-// failf("expected 3 messages from gossip but only got %d", len(ch))
-// }
-// })
-//}
-//
-//func TestMemberlist_GossipToDead(t *testing.T) {
-// ch := make(chan NodeEvent, 2)
-//
-// addr1 := getBindAddr()
-// addr2 := getBindAddr()
-// ip1 := addr1
-// ip2 := addr2
-//
-// m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
-// c.GossipInterval = time.Millisecond
-// c.GossipToTheDeadTime = 100 * time.Millisecond
-// })
-// defer m1.Shutdown()
-//
-// bindPort := m1.config.BindPort
-//
-// m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// c.Events = &ChannelEventDelegate{ch}
-// })
-//
-// defer m2.Shutdown()
-//
-// a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m1.config.BuildVsnArray()}
-// m1.aliveNode(&a1, nil, true)
-// a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m2.config.BuildVsnArray()}
-// m1.aliveNode(&a2, nil, false)
-//
-// // Shouldn't send anything to m2 here, node has been dead for 2x the GossipToTheDeadTime
-// m1.nodeMap[addr2.String()].State = StateDead
-// m1.nodeMap[addr2.String()].StateChange = time.Now().Add(-200 * time.Millisecond)
-// m1.gossip()
-//
-// select {
-// case <-ch:
-// t.Fatalf("shouldn't get gossip")
-// case <-time.After(50 * time.Millisecond):
-// }
-//
-// // Should gossip to m2 because its state has changed within GossipToTheDeadTime
-// m1.nodeMap[addr2.String()].StateChange = time.Now().Add(-20 * time.Millisecond)
-//
-// retry(t, 5, 10*time.Millisecond, func(failf func(string, ...interface{})) {
-// m1.gossip()
-//
-// time.Sleep(3 * time.Millisecond)
-//
-// if len(ch) < 2 {
-// failf("expected 2 messages from gossip")
-// }
-// })
-//}
-//
-//func TestMemberlist_FailedRemote(t *testing.T) {
-// type test struct {
-// name string
-// err error
-// expected bool
-// }
-// tests := []test{
-// {"nil error", nil, false},
-// {"normal error", fmt.Errorf(""), false},
-// {"net.OpError for file", &net.OpError{Net: "file"}, false},
-// {"net.OpError for udp", &net.OpError{Net: "udp"}, false},
-// {"net.OpError for udp4", &net.OpError{Net: "udp4"}, false},
-// {"net.OpError for udp6", &net.OpError{Net: "udp6"}, false},
-// {"net.OpError for tcp", &net.OpError{Net: "tcp"}, false},
-// {"net.OpError for tcp4", &net.OpError{Net: "tcp4"}, false},
-// {"net.OpError for tcp6", &net.OpError{Net: "tcp6"}, false},
-// {"net.OpError for tcp with dial", &net.OpError{Net: "tcp", Op: "dial"}, true},
-// {"net.OpError for tcp with write", &net.OpError{Net: "tcp", Op: "write"}, true},
-// {"net.OpError for tcp with read", &net.OpError{Net: "tcp", Op: "read"}, true},
-// }
-//
-// for _, test := range tests {
-// t.Run(test.name, func(t *testing.T) {
-// actual := failedRemote(test.err)
-// if actual != test.expected {
-// t.Fatalf("expected %t, got %t", test.expected, actual)
-// }
-// })
-// }
-//}
-//
-//func TestMemberlist_PushPull(t *testing.T) {
-// addr1 := getBindAddr()
-// addr2 := getBindAddr()
-// ip1 := addr1
-// ip2 := addr2
-//
-// ch := make(chan NodeEvent, 3)
-//
-// m1 := HostMemberlist(addr1.String(), t, func(c *Config) {
-// c.GossipInterval = 10 * time.Second
-// c.PushPullInterval = time.Millisecond
-// })
-// defer m1.Shutdown()
-//
-// bindPort := m1.config.BindPort
-//
-// m2 := HostMemberlist(addr2.String(), t, func(c *Config) {
-// c.BindPort = bindPort
-// c.GossipInterval = 10 * time.Second
-// c.Events = &ChannelEventDelegate{ch}
-// })
-// defer m2.Shutdown()
-//
-// a1 := alive{Node: addr1.String(), Addr: ip1.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m1.config.BuildVsnArray()}
-// m1.aliveNode(&a1, nil, true)
-// a2 := alive{Node: addr2.String(), Addr: ip2.String(), Port: uint16(bindPort), Incarnation: 1, Vsn: m2.config.BuildVsnArray()}
-// m1.aliveNode(&a2, nil, false)
-//
-// // Gossip should send all this to m2. It's UDP though so retry a few times
-// retry(t, 5, 10*time.Millisecond, func(failf func(string, ...interface{})) {
-// m1.pushPull()
-//
-// time.Sleep(3 * time.Millisecond)
-//
-// if len(ch) < 2 {
-// failf("expected 2 messages from pushPull")
-// }
-// })
-//}
-//
-//func TestVerifyProtocol(t *testing.T) {
-// cases := []struct {
-// Anodes [][3]uint8
-// Bnodes [][3]uint8
-// expected bool
-// }{
-// // Both running identical everything
-// {
-// Anodes: [][3]uint8{
-// {0, 0, 0},
-// },
-// Bnodes: [][3]uint8{
-// {0, 0, 0},
-// },
-// expected: true,
-// },
-//
-// // One can understand newer, but speaking same protocol
-// {
-// Anodes: [][3]uint8{
-// {0, 0, 0},
-// },
-// Bnodes: [][3]uint8{
-// {0, 1, 0},
-// },
-// expected: true,
-// },
-//
-// // One is speaking outside the range
-// {
-// Anodes: [][3]uint8{
-// {0, 0, 0},
-// },
-// Bnodes: [][3]uint8{
-// {1, 1, 1},
-// },
-// expected: false,
-// },
-//
-// // Transitively outside the range
-// {
-// Anodes: [][3]uint8{
-// {0, 1, 0},
-// {0, 2, 1},
-// },
-// Bnodes: [][3]uint8{
-// {1, 3, 1},
-// },
-// expected: false,
-// },
-//
-// // Multi-node
-// {
-// Anodes: [][3]uint8{
-// {0, 3, 2},
-// {0, 2, 0},
-// },
-// Bnodes: [][3]uint8{
-// {0, 2, 1},
-// {0, 5, 0},
-// },
-// expected: true,
-// },
-// }
-//
-// for _, tc := range cases {
-// aCore := make([][6]uint8, len(tc.Anodes))
-// aApp := make([][6]uint8, len(tc.Anodes))
-// for i, n := range tc.Anodes {
-// aCore[i] = [6]uint8{n[0], n[1], n[2], 0, 0, 0}
-// aApp[i] = [6]uint8{0, 0, 0, n[0], n[1], n[2]}
-// }
-//
-// bCore := make([][6]uint8, len(tc.Bnodes))
-// bApp := make([][6]uint8, len(tc.Bnodes))
-// for i, n := range tc.Bnodes {
-// bCore[i] = [6]uint8{n[0], n[1], n[2], 0, 0, 0}
-// bApp[i] = [6]uint8{0, 0, 0, n[0], n[1], n[2]}
-// }
-//
-// // Test core protocol verification
-// testVerifyProtocolSingle(t, aCore, bCore, tc.expected)
-// testVerifyProtocolSingle(t, bCore, aCore, tc.expected)
-//
-// // Test app protocol verification
-// testVerifyProtocolSingle(t, aApp, bApp, tc.expected)
-// testVerifyProtocolSingle(t, bApp, aApp, tc.expected)
-// }
-//}
-//
-//func testVerifyProtocolSingle(t *testing.T, A [][6]uint8, B [][6]uint8, expect bool) {
-// m := GetMemberlist(t, nil)
-// defer m.Shutdown()
-//
-// m.nodes = make([]*nodeState, len(A))
-// for i, n := range A {
-// m.nodes[i] = &nodeState{
-// Node: Node{
-// PMin: n[0],
-// PMax: n[1],
-// PCur: n[2],
-// DMin: n[3],
-// DMax: n[4],
-// DCur: n[5],
-// },
-// }
-// }
-//
-// remote := make([]pushNodeState, len(B))
-// for i, n := range B {
-// remote[i] = pushNodeState{
-// Name: fmt.Sprintf("node %d", i),
-// Vsn: []uint8{n[0], n[1], n[2], n[3], n[4], n[5]},
-// }
-// }
-//
-// err := m.verifyProtocol(remote)
-// if (err == nil) != expect {
-// t.Fatalf("bad:\nA: %v\nB: %v\nErr: %s", A, B, err)
-// }
-//}
diff --git a/toolkit/memberlist/suspicion.go b/toolkit/memberlist/suspicion.go
deleted file mode 100644
index f8aa9e20..00000000
--- a/toolkit/memberlist/suspicion.go
+++ /dev/null
@@ -1,130 +0,0 @@
-package memberlist
-
-import (
- "math"
- "sync/atomic"
- "time"
-)
-
-// suspicion manages the suspect timer for a node and provides an interface
-// to accelerate the timeout as we get more independent confirmations that
-// a node is suspect.
-type suspicion struct {
- // n is the number of independent confirmations we've seen. This must
- // be updated using atomic instructions to prevent contention with the
- // timer callback.
- n int32
-
- // k is the number of independent confirmations we'd like to see in
- // order to drive the timer to its minimum value.
- k int32
-
- // min is the minimum timer value.
- min time.Duration
-
- // max is the maximum timer value.
- max time.Duration
-
- // start captures the timestamp when we began the timer. This is used
- // so we can calculate durations to feed the timer during updates in
- // a way the achieves the overall time we'd like.
- start time.Time
-
- // timer is the underlying timer that implements the timeout.
- timer *time.Timer
-
- // f is the function to call when the timer expires. We hold on to this
- // because there are cases where we call it directly.
- timeoutFn func()
-
- // confirmations is a map of "from" nodes that have confirmed a given
- // node is suspect. This prevents double counting.
- confirmations map[string]struct{}
-}
-
-// newSuspicion returns a timer started with the max time, and that will drive
-// to the min time after seeing k or more confirmations. The from node will be
-// excluded from confirmations since we might get our own suspicion message
-// gossiped back to us. The minimum time will be used if no confirmations are
-// called for (k <= 0).
-func newSuspicion(from string, k int, min time.Duration, max time.Duration, fn func(int)) *suspicion {
- s := &suspicion{
- k: int32(k),
- min: min,
- max: max,
- confirmations: make(map[string]struct{}),
- }
-
- // Exclude the from node from any confirmations.
- s.confirmations[from] = struct{}{}
-
- // Pass the number of confirmations into the timeout function for
- // easy telemetry.
- s.timeoutFn = func() {
- fn(int(atomic.LoadInt32(&s.n)))
- }
-
- // If there aren't any confirmations to be made then take the min
- // time from the start.
- timeout := max
- if k < 1 {
- timeout = min
- }
- s.timer = time.AfterFunc(timeout, s.timeoutFn)
-
- // Capture the start time right after starting the timer above so
- // we should always err on the side of a little longer timeout if
- // there's any preemption that separates this and the step above.
- s.start = time.Now()
- return s
-}
-
-// remainingSuspicionTime takes the state variables of the suspicion timer and
-// calculates the remaining time to wait before considering a node dead. The
-// return value can be negative, so be prepared to fire the timer immediately in
-// that case.
-func remainingSuspicionTime(n, k int32, elapsed time.Duration, min, max time.Duration) time.Duration {
- frac := math.Log(float64(n)+1.0) / math.Log(float64(k)+1.0)
- raw := max.Seconds() - frac*(max.Seconds()-min.Seconds())
- timeout := time.Duration(math.Floor(1000.0*raw)) * time.Millisecond
- if timeout < min {
- timeout = min
- }
-
- // We have to take into account the amount of time that has passed so
- // far, so we get the right overall timeout.
- return timeout - elapsed
-}
-
-// Confirm registers that a possibly new peer has also determined the given
-// node is suspect. This returns true if this was new information, and false
-// if it was a duplicate confirmation, or if we've got enough confirmations to
-// hit the minimum.
-func (s *suspicion) Confirm(from string) bool {
- // If we've got enough confirmations then stop accepting them.
- if atomic.LoadInt32(&s.n) >= s.k {
- return false
- }
-
- // Only allow one confirmation from each possible peer.
- if _, ok := s.confirmations[from]; ok {
- return false
- }
- s.confirmations[from] = struct{}{}
-
- // Compute the new timeout given the current number of confirmations and
- // adjust the timer. If the timeout becomes negative *and* we can cleanly
- // stop the timer then we will call the timeout function directly from
- // here.
- n := atomic.AddInt32(&s.n, 1)
- elapsed := time.Since(s.start)
- remaining := remainingSuspicionTime(n, s.k, elapsed, s.min, s.max)
- if s.timer.Stop() {
- if remaining > 0 {
- s.timer.Reset(remaining)
- } else {
- go s.timeoutFn()
- }
- }
- return true
-}
diff --git a/toolkit/memberlist/suspicion_test.go b/toolkit/memberlist/suspicion_test.go
deleted file mode 100644
index 1b5ca8a5..00000000
--- a/toolkit/memberlist/suspicion_test.go
+++ /dev/null
@@ -1,198 +0,0 @@
-package memberlist
-
-import (
- "testing"
- "time"
-)
-
-func TestSuspicion_remainingSuspicionTime(t *testing.T) {
- cases := []struct {
- n int32
- k int32
- elapsed time.Duration
- min time.Duration
- max time.Duration
- expected time.Duration
- }{
- {0, 3, 0, 2 * time.Second, 30 * time.Second, 30 * time.Second},
- {1, 3, 2 * time.Second, 2 * time.Second, 30 * time.Second, 14 * time.Second},
- {2, 3, 3 * time.Second, 2 * time.Second, 30 * time.Second, 4810 * time.Millisecond},
- {3, 3, 4 * time.Second, 2 * time.Second, 30 * time.Second, -2 * time.Second},
- {4, 3, 5 * time.Second, 2 * time.Second, 30 * time.Second, -3 * time.Second},
- {5, 3, 10 * time.Second, 2 * time.Second, 30 * time.Second, -8 * time.Second},
- }
- for i, c := range cases {
- remaining := remainingSuspicionTime(c.n, c.k, c.elapsed, c.min, c.max)
- if remaining != c.expected {
- t.Errorf("case %d: remaining %9.6f != expected %9.6f", i, remaining.Seconds(), c.expected.Seconds())
- }
- }
-}
-
-func TestSuspicion_Timer(t *testing.T) {
- const k = 3
- const min = 500 * time.Millisecond
- const max = 2 * time.Second
-
- type pair struct {
- from string
- newInfo bool
- }
- cases := []struct {
- numConfirmations int
- from string
- confirmations []pair
- expected time.Duration
- }{
- {
- 0,
- "me",
- []pair{},
- max,
- },
- {
- 1,
- "me",
- []pair{
- pair{"me", false},
- pair{"foo", true},
- },
- 1250 * time.Millisecond,
- },
- {
- 1,
- "me",
- []pair{
- pair{"me", false},
- pair{"foo", true},
- pair{"foo", false},
- pair{"foo", false},
- },
- 1250 * time.Millisecond,
- },
- {
- 2,
- "me",
- []pair{
- pair{"me", false},
- pair{"foo", true},
- pair{"bar", true},
- },
- 810 * time.Millisecond,
- },
- {
- 3,
- "me",
- []pair{
- pair{"me", false},
- pair{"foo", true},
- pair{"bar", true},
- pair{"baz", true},
- },
- min,
- },
- {
- 3,
- "me",
- []pair{
- pair{"me", false},
- pair{"foo", true},
- pair{"bar", true},
- pair{"baz", true},
- pair{"zoo", false},
- },
- min,
- },
- }
- for i, c := range cases {
- ch := make(chan time.Duration, 1)
- start := time.Now()
- f := func(numConfirmations int) {
- if numConfirmations != c.numConfirmations {
- t.Errorf("case %d: bad %d != %d", i, numConfirmations, c.numConfirmations)
- }
-
- ch <- time.Now().Sub(start)
- }
-
- // Create the timer and add the requested confirmations. Wait
- // the fudge amount to help make sure we calculate the timeout
- // overall, and don't accumulate extra time.
- s := newSuspicion(c.from, k, min, max, f)
- fudge := 25 * time.Millisecond
- for _, p := range c.confirmations {
- time.Sleep(fudge)
- if s.Confirm(p.from) != p.newInfo {
- t.Fatalf("case %d: newInfo mismatch for %s", i, p.from)
- }
- }
-
- // Wait until right before the timeout and make sure the
- // timer hasn't fired.
- already := time.Duration(len(c.confirmations)) * fudge
- time.Sleep(c.expected - already - fudge)
- select {
- case d := <-ch:
- t.Fatalf("case %d: should not have fired (%9.6f)", i, d.Seconds())
- default:
- }
-
- // Wait through the timeout and a little after and make sure it
- // fires.
- time.Sleep(2 * fudge)
- select {
- case <-ch:
- default:
- t.Fatalf("case %d: should have fired", i)
- }
-
- // Confirm after to make sure it handles a negative remaining
- // time correctly and doesn't fire again.
- s.Confirm("late")
- time.Sleep(c.expected + 2*fudge)
- select {
- case d := <-ch:
- t.Fatalf("case %d: should not have fired (%9.6f)", i, d.Seconds())
- default:
- }
- }
-}
-
-func TestSuspicion_Timer_ZeroK(t *testing.T) {
- ch := make(chan struct{}, 1)
- f := func(int) {
- ch <- struct{}{}
- }
-
- // This should select the min time since there are no expected
- // confirmations to accelerate the timer.
- s := newSuspicion("me", 0, 25*time.Millisecond, 30*time.Second, f)
- if s.Confirm("foo") {
- t.Fatalf("should not provide new information")
- }
-
- select {
- case <-ch:
- case <-time.After(50 * time.Millisecond):
- t.Fatalf("should have fired")
- }
-}
-
-func TestSuspicion_Timer_Immediate(t *testing.T) {
- ch := make(chan struct{}, 1)
- f := func(int) {
- ch <- struct{}{}
- }
-
- // This should underflow the timeout and fire immediately.
- s := newSuspicion("me", 1, 100*time.Millisecond, 30*time.Second, f)
- time.Sleep(200 * time.Millisecond)
- s.Confirm("foo")
-
- // Wait a little while since the function gets called in a goroutine.
- select {
- case <-ch:
- case <-time.After(25 * time.Millisecond):
- t.Fatalf("should have fired")
- }
-}
diff --git a/toolkit/memberlist/todo.md b/toolkit/memberlist/todo.md
deleted file mode 100644
index 009c1d64..00000000
--- a/toolkit/memberlist/todo.md
+++ /dev/null
@@ -1,6 +0,0 @@
-# TODO
-* Dynamic RTT discovery
- * Compute 99th percentile for ping/ack
- * Better lower bound for ping/ack, faster failure detection
-* Dynamic MTU discovery
- * Prevent lost updates, increases efficiency
diff --git a/toolkit/memberlist/transport.go b/toolkit/memberlist/transport.go
deleted file mode 100644
index 026ae477..00000000
--- a/toolkit/memberlist/transport.go
+++ /dev/null
@@ -1,121 +0,0 @@
-package memberlist
-
-import (
- "fmt"
- "net"
- "time"
-)
-
-//go:generate mockgen -destination ./mock/mock_transport.go -package mock -source=./transport.go
-
-// Packet is used to provide some metadata about incoming packets from peers
-// over a packet connection, as well as the packet payload.
-type Packet struct {
- // Buf has the raw contents of the packet.
- Buf []byte
-
- // From has the address of the peer. This is an actual net.Addr so we
- // can expose some concrete details about incoming packets.
- From net.Addr
-
- // Timestamp is the time when the packet was received. This should be
- // taken as close as possible to the actual receipt time to help make an
- // accurate RTT measurement during probes.
- Timestamp time.Time
-}
-
-// Transport is used to abstract over communicating with other peers. The packet
-// interface is assumed to be best-effort and the stream interface is assumed to
-// be reliable.
-type Transport interface {
- // FinalAdvertiseAddr is given the user's configured values (which
- // might be empty) and returns the desired IP and port to advertise to
- // the rest of the cluster.
- FinalAdvertiseAddr(ip string, port int) (string, int, error)
-
- // WriteTo is a packet-oriented interface that fires off the given
- // payload to the given address in a connectionless fashion. This should
- // return a time stamp that's as close as possible to when the packet
- // was transmitted to help make accurate RTT measurements during probes.
- //
- // This is similar to net.PacketConn, though we didn't want to expose
- // that full set of required methods to keep assumptions about the
- // underlying plumbing to a minimum. We also treat the address here as a
- // string, similar to Dial, so it's network neutral, so this usually is
- // in the form of "host:port".
- WriteTo(b []byte, addr string) (time.Time, error)
-
- // PacketCh returns a channel that can be read to receive incoming
- // packets from other peers. How this is set up for listening is left as
- // an exercise for the concrete transport implementations.
- PacketCh() <-chan *Packet
-
- // DialTimeout is used to create a connection that allows us to perform
- // two-way communication with a peer. This is generally more expensive
- // than packet connections so is used for more infrequent operations
- // such as anti-entropy or fallback probes if the packet-oriented probe
- // failed.
- DialTimeout(addr string, timeout time.Duration) (net.Conn, error)
-
- // StreamCh returns a channel that can be read to handle incoming stream
- // connections from other peers. How this is set up for listening is
- // left as an exercise for the concrete transport implementations.
- StreamCh() <-chan net.Conn
-
- // Shutdown is called when memberlist is shutting down; this gives the
- // transport a chance to clean up any listeners.
- Shutdown() error
-}
-
-type Address struct {
- // Addr is a network address as a string, similar to Dial. This usually is
- // in the form of "host:port". This is required.
- Addr string
-
- // Name is the name of the node being addressed. This is optional but
- // transports may require it.
- Name string
-}
-
-func (a *Address) String() string {
- if a.Name != "" {
- return fmt.Sprintf("%s (%s)", a.Name, a.Addr)
- }
- return a.Addr
-}
-
-// IngestionAwareTransport is not used.
-//
-// Deprecated: IngestionAwareTransport is not used and may be removed in a future
-// version. Define the interface locally instead of referencing this exported
-// interface.
-type IngestionAwareTransport interface {
- IngestPacket(conn net.Conn, addr net.Addr, now time.Time, shouldClose bool) error
- IngestStream(conn net.Conn) error
-}
-
-type NodeAwareTransport interface {
- Transport
- WriteToAddress(b []byte, addr Address) (time.Time, error)
- DialAddressTimeout(addr Address, timeout time.Duration) (net.Conn, error)
-}
-
-type shimNodeAwareTransport struct {
- Transport
-}
-
-func NewshimNodeAwareTransport(t Transport) *shimNodeAwareTransport {
- return &shimNodeAwareTransport{
- t,
- }
-}
-
-var _ NodeAwareTransport = (*shimNodeAwareTransport)(nil)
-
-func (t *shimNodeAwareTransport) WriteToAddress(b []byte, addr Address) (time.Time, error) {
- return t.WriteTo(b, addr.Addr)
-}
-
-func (t *shimNodeAwareTransport) DialAddressTimeout(addr Address, timeout time.Duration) (net.Conn, error) {
- return t.DialTimeout(addr.Addr, timeout)
-}
diff --git a/toolkit/memberlist/transport_test.go b/toolkit/memberlist/transport_test.go
deleted file mode 100644
index 1fcb50f0..00000000
--- a/toolkit/memberlist/transport_test.go
+++ /dev/null
@@ -1,101 +0,0 @@
-package memberlist_test
-
-import (
- "io"
- "net"
- "strings"
- "sync/atomic"
- "testing"
- "time"
-
- "github.com/golang/mock/gomock"
- . "github.com/smartystreets/goconvey/convey"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
- memmock "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist/mock"
-
- "github.com/stretchr/testify/require"
-)
-
-type testCountingWriter struct {
- t *testing.T
- numCalls *int32
-}
-
-func (tw testCountingWriter) Write(p []byte) (n int, err error) {
- atomic.AddInt32(tw.numCalls, 1)
- if !strings.Contains(string(p), "memberlist: Error accepting TCP connection") {
- tw.t.Error("did not receive expected log message")
- }
- tw.t.Log("countingWriter:", string(p))
- return len(p), nil
-}
-
-func TestAddress_String(t *testing.T) {
- addr := &memberlist.Address{
- Addr: "localhost:7946",
- Name: "testNode",
- }
- require.Equal(t, "testNode (localhost:7946)", addr.String())
-
- addr = &memberlist.Address{
- Addr: "localhost:7946",
- }
- require.Equal(t, "localhost:7946", addr.String())
-}
-
-func Test_shimNodeAwareTransport_WriteToAddress(t *testing.T) {
- Convey("", t, func() {
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- nat := memmock.NewMockTransport(ctrl)
- addr := memberlist.Address{
- Addr: "localhost:7946",
- Name: "testNode",
- }
- now := time.Now()
- msg := []byte("test message")
- nat.
- EXPECT().
- WriteTo(msg, addr.Addr).
- AnyTimes().
- Return(now, nil)
-
- s := memberlist.NewshimNodeAwareTransport(nat)
- sendAt, err := s.WriteToAddress(msg, addr)
- So(err, ShouldBeNil)
- So(sendAt, ShouldResemble, now)
- })
-}
-
-type emptyReadNetConn struct {
- net.Conn
-}
-
-func (c *emptyReadNetConn) Read(b []byte) (n int, err error) {
- return 0, io.EOF
-}
-
-func (c *emptyReadNetConn) Close() error {
- return nil
-}
-
-func Test_shimNodeAwareTransport_DialAddressTimeout(t1 *testing.T) {
- Convey("", t1, func() {
- ctrl := gomock.NewController(t1)
- defer ctrl.Finish()
- nat := memmock.NewMockTransport(ctrl)
- addr := memberlist.Address{
- Addr: "localhost:7946",
- Name: "testNode",
- }
- nat.
- EXPECT().
- DialTimeout(addr.Addr, 10*time.Second).
- AnyTimes().
- Return(&emptyReadNetConn{}, nil)
-
- s := memberlist.NewshimNodeAwareTransport(nat)
- _, err := s.DialAddressTimeout(addr, 10*time.Second)
- So(err, ShouldBeNil)
- })
-}
diff --git a/toolkit/memberlist/util.go b/toolkit/memberlist/util.go
deleted file mode 100644
index cfae4c9e..00000000
--- a/toolkit/memberlist/util.go
+++ /dev/null
@@ -1,309 +0,0 @@
-package memberlist
-
-import (
- "bytes"
- "compress/lzw"
- "encoding/binary"
- "fmt"
- "io"
- "math"
- "math/rand"
- "net"
- "strconv"
- "strings"
- "time"
-
- "github.com/hashicorp/go-msgpack/codec"
- "github.com/sean-/seed"
-)
-
-// pushPullScale is the minimum number of nodes
-// before we start scaling the push/pull timing. The scale
-// effect is the log2(Nodes) - log2(pushPullScale). This means
-// that the 33rd node will cause us to double the interval,
-// while the 65th will triple it.
-const pushPullScaleThreshold = 32
-
-const (
- // Constant litWidth 2-8
- lzwLitWidth = 8
-)
-
-func init() {
- seed.Init()
-}
-
-// Decode reverses the encode operation on a byte slice input
-func decode(buf []byte, out interface{}) error {
- r := bytes.NewReader(buf)
- hd := codec.MsgpackHandle{}
- dec := codec.NewDecoder(r, &hd)
- return dec.Decode(out)
-}
-
-// Encode writes an encoded object to a new bytes buffer
-func encode(msgType messageType, in interface{}) (*bytes.Buffer, error) {
- buf := bytes.NewBuffer(nil)
- buf.WriteByte(uint8(msgType))
- hd := codec.MsgpackHandle{}
- enc := codec.NewEncoder(buf, &hd)
- err := enc.Encode(in)
- return buf, err
-}
-
-// Returns a random offset between 0 and n
-func randomOffset(n int) int {
- if n == 0 {
- return 0
- }
- return int(rand.Uint32() % uint32(n))
-}
-
-// suspicionTimeout computes the timeout that should be used when
-// a node is suspected
-func suspicionTimeout(suspicionMult, n int, interval time.Duration) time.Duration {
- nodeScale := math.Max(1.0, math.Log10(math.Max(1.0, float64(n))))
- // multiply by 1000 to keep some precision because time.Duration is an int64 type
- timeout := time.Duration(suspicionMult) * time.Duration(nodeScale*1000) * interval / 1000
- return timeout
-}
-
-// retransmitLimit computes the limit of retransmissions
-func retransmitLimit(retransmitMult, n int) int {
- nodeScale := math.Ceil(math.Log10(float64(n + 1)))
- limit := retransmitMult * int(nodeScale)
- return limit
-}
-
-// shuffleNodes randomly shuffles the input nodes using the Fisher-Yates shuffle
-func shuffleNodes(nodes []*nodeState) {
- n := len(nodes)
- rand.Shuffle(n, func(i, j int) {
- nodes[i], nodes[j] = nodes[j], nodes[i]
- })
-}
-
-// pushPushScale is used to scale the time interval at which push/pull
-// syncs take place. It is used to prevent network saturation as the
-// cluster size grows
-func pushPullScale(interval time.Duration, n int) time.Duration {
- // Don't scale until we cross the threshold
- if n <= pushPullScaleThreshold {
- return interval
- }
-
- multiplier := math.Ceil(math.Log2(float64(n))-math.Log2(pushPullScaleThreshold)) + 1.0
- return time.Duration(multiplier) * interval
-}
-
-// moveDeadNodes moves dead and left nodes that that have not changed during the gossipToTheDeadTime interval
-// to the end of the slice and returns the index of the first moved node.
-func moveDeadNodes(nodes []*nodeState, gossipToTheDeadTime time.Duration) int {
- numDead := 0
- n := len(nodes)
- for i := 0; i < n-numDead; i++ {
- if !nodes[i].DeadOrLeft() {
- continue
- }
-
- // Respect the gossip to the dead interval
- if time.Since(nodes[i].StateChange) <= gossipToTheDeadTime {
- continue
- }
-
- // Move this node to the end
- nodes[i], nodes[n-numDead-1] = nodes[n-numDead-1], nodes[i]
- numDead++
- i--
- }
- return n - numDead
-}
-
-// kRandomNodes is used to select up to k random Nodes, excluding any nodes where
-// the exclude function returns true. It is possible that less than k nodes are
-// returned.
-func kRandomNodes(k int, nodes []*nodeState, exclude func(*nodeState) bool) []Node {
- n := len(nodes)
- kNodes := make([]Node, 0, k)
-OUTER:
- // Probe up to 3*n times, with large n this is not necessary
- // since k << n, but with small n we want search to be
- // exhaustive
- for i := 0; i < 3*n && len(kNodes) < k; i++ {
- // Get random nodeState
- idx := randomOffset(n)
- state := nodes[idx]
-
- // Give the filter a shot at it.
- if exclude != nil && exclude(state) {
- continue OUTER
- }
-
- // Check if we have this node already
- for j := 0; j < len(kNodes); j++ {
- if state.Node.Name == kNodes[j].Name {
- continue OUTER
- }
- }
-
- // Append the node
- kNodes = append(kNodes, state.Node)
- }
- return kNodes
-}
-
-// makeCompoundMessage takes a list of messages and generates
-// a single compound message containing all of them
-func makeCompoundMessage(msgs [][]byte) *bytes.Buffer {
- // Create a local buffer
- buf := bytes.NewBuffer(nil)
-
- // Write out the type
- buf.WriteByte(uint8(compoundMsg))
-
- // Write out the number of message
- buf.WriteByte(uint8(len(msgs)))
-
- // Add the message lengths
- for _, m := range msgs {
- binary.Write(buf, binary.BigEndian, uint16(len(m)))
- }
-
- // Append the messages
- for _, m := range msgs {
- buf.Write(m)
- }
-
- return buf
-}
-
-// decodeCompoundMessage splits a compound message and returns
-// the slices of individual messages. Also returns the number
-// of truncated messages and any potential error
-func decodeCompoundMessage(buf []byte) (trunc int, parts [][]byte, err error) {
- if len(buf) < 1 {
- err = fmt.Errorf("missing compound length byte")
- return
- }
- numParts := int(buf[0])
- buf = buf[1:]
-
- // Check we have enough bytes
- if len(buf) < numParts*2 {
- err = fmt.Errorf("truncated len slice")
- return
- }
-
- // Decode the lengths
- lengths := make([]uint16, numParts)
- for i := 0; i < numParts; i++ {
- lengths[i] = binary.BigEndian.Uint16(buf[i*2 : i*2+2])
- }
- buf = buf[numParts*2:]
-
- // Split each message
- for idx, msgLen := range lengths {
- if len(buf) < int(msgLen) {
- trunc = numParts - idx
- return
- }
-
- // Extract the slice, seek past on the buffer
- slice := buf[:msgLen]
- buf = buf[msgLen:]
- parts = append(parts, slice)
- }
- return
-}
-
-// compressPayload takes an opaque input buffer, compresses it
-// and wraps it in a compress{} message that is encoded.
-func compressPayload(inp []byte) (*bytes.Buffer, error) {
- var buf bytes.Buffer
- compressor := lzw.NewWriter(&buf, lzw.LSB, lzwLitWidth)
-
- _, err := compressor.Write(inp)
- if err != nil {
- return nil, err
- }
-
- // Ensure we flush everything out
- if err := compressor.Close(); err != nil {
- return nil, err
- }
-
- // Create a compressed message
- c := compress{
- Algo: lzwAlgo,
- Buf: buf.Bytes(),
- }
- return encode(compressMsg, &c)
-}
-
-// decompressPayload is used to unpack an encoded compress{}
-// message and return its payload uncompressed
-func decompressPayload(msg []byte) ([]byte, error) {
- // Decode the message
- var c compress
- if err := decode(msg, &c); err != nil {
- return nil, err
- }
- return decompressBuffer(&c)
-}
-
-// decompressBuffer is used to decompress the buffer of
-// a single compress message, handling multiple algorithms
-func decompressBuffer(c *compress) ([]byte, error) {
- // Verify the algorithm
- if c.Algo != lzwAlgo {
- return nil, fmt.Errorf("Cannot decompress unknown algorithm %d", c.Algo)
- }
-
- // Create a uncompressor
- uncomp := lzw.NewReader(bytes.NewReader(c.Buf), lzw.LSB, lzwLitWidth)
- defer uncomp.Close()
-
- // Read all the data
- var b bytes.Buffer
- _, err := io.Copy(&b, uncomp)
- if err != nil {
- return nil, err
- }
-
- // Return the uncompressed bytes
- return b.Bytes(), nil
-}
-
-// joinHostPort returns the host:port form of an address, for use with a
-// transport.
-func joinHostPort(host string, port uint16) string {
- return net.JoinHostPort(host, strconv.Itoa(int(port)))
-}
-
-// hasPort is given a string of the form "host", "host:port", "ipv6::address",
-// or "[ipv6::address]:port", and returns true if the string includes a port.
-func hasPort(s string) bool {
- // IPv6 address in brackets.
- if strings.LastIndex(s, "[") == 0 {
- return strings.LastIndex(s, ":") > strings.LastIndex(s, "]")
- }
-
- // Otherwise the presence of a single colon determines if there's a port
- // since IPv6 addresses outside of brackets (count > 1) can't have a
- // port.
- return strings.Count(s, ":") == 1
-}
-
-// ensurePort makes sure the given string has a port number on it, otherwise it
-// appends the given port as a default.
-func ensurePort(s string, port int) string {
- if hasPort(s) {
- return s
- }
-
- // If this is an IPv6 address, the join call will add another set of
- // brackets, so we have to trim before we add the default port.
- s = strings.Trim(s, "[]")
- s = net.JoinHostPort(s, strconv.Itoa(port))
- return s
-}
diff --git a/toolkit/memberlist/util_test.go b/toolkit/memberlist/util_test.go
deleted file mode 100644
index 4bd6dadb..00000000
--- a/toolkit/memberlist/util_test.go
+++ /dev/null
@@ -1,372 +0,0 @@
-package memberlist
-
-import (
- "fmt"
- "reflect"
- "testing"
- "time"
-
- "github.com/stretchr/testify/require"
-)
-
-func TestUtil_PortFunctions(t *testing.T) {
- tests := []struct {
- addr string
- hasPort bool
- ensurePort string
- }{
- {"1.2.3.4", false, "1.2.3.4:8301"},
- {"1.2.3.4:1234", true, "1.2.3.4:1234"},
- {"2600:1f14:e22:1501:f9a:2e0c:a167:67e8", false, "[2600:1f14:e22:1501:f9a:2e0c:a167:67e8]:8301"},
- {"[2600:1f14:e22:1501:f9a:2e0c:a167:67e8]", false, "[2600:1f14:e22:1501:f9a:2e0c:a167:67e8]:8301"},
- {"[2600:1f14:e22:1501:f9a:2e0c:a167:67e8]:1234", true, "[2600:1f14:e22:1501:f9a:2e0c:a167:67e8]:1234"},
- {"localhost", false, "localhost:8301"},
- {"localhost:1234", true, "localhost:1234"},
- {"hashicorp.com", false, "hashicorp.com:8301"},
- {"hashicorp.com:1234", true, "hashicorp.com:1234"},
- }
- for _, tt := range tests {
- t.Run(tt.addr, func(t *testing.T) {
- if got, want := hasPort(tt.addr), tt.hasPort; got != want {
- t.Fatalf("got %v want %v", got, want)
- }
- if got, want := ensurePort(tt.addr, 8301), tt.ensurePort; got != want {
- t.Fatalf("got %v want %v", got, want)
- }
- })
- }
-}
-
-func TestEncodeDecode(t *testing.T) {
- msg := &ping{SeqNo: 100}
- buf, err := encode(pingMsg, msg)
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
- var out ping
- if err := decode(buf.Bytes()[1:], &out); err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
- if msg.SeqNo != out.SeqNo {
- t.Fatalf("bad sequence no")
- }
-}
-
-func TestRandomOffset(t *testing.T) {
- vals := make(map[int]struct{})
- for i := 0; i < 100; i++ {
- offset := randomOffset(2 << 30)
- if _, ok := vals[offset]; ok {
- t.Fatalf("got collision")
- }
- vals[offset] = struct{}{}
- }
-}
-
-func TestRandomOffset_Zero(t *testing.T) {
- offset := randomOffset(0)
- if offset != 0 {
- t.Fatalf("bad offset")
- }
-}
-
-func TestSuspicionTimeout(t *testing.T) {
- timeouts := map[int]time.Duration{
- 5: 1000 * time.Millisecond,
- 10: 1000 * time.Millisecond,
- 50: 1698 * time.Millisecond,
- 100: 2000 * time.Millisecond,
- 500: 2698 * time.Millisecond,
- 1000: 3000 * time.Millisecond,
- }
- for n, expected := range timeouts {
- timeout := suspicionTimeout(3, n, time.Second) / 3
- if timeout != expected {
- t.Fatalf("bad: %v, %v", expected, timeout)
- }
- }
-}
-
-func TestSuspicionTimeout2(t *testing.T) {
- timeout := suspicionTimeout(6, 6, 5*time.Second)
- fmt.Println(timeout)
-}
-
-func TestRetransmitLimit(t *testing.T) {
- lim := retransmitLimit(3, 0)
- if lim != 0 {
- t.Fatalf("bad val %v", lim)
- }
- lim = retransmitLimit(3, 1)
- if lim != 3 {
- t.Fatalf("bad val %v", lim)
- }
- lim = retransmitLimit(3, 99)
- if lim != 6 {
- t.Fatalf("bad val %v", lim)
- }
- lim = retransmitLimit(4, 15)
- if lim != 8 {
- t.Fatalf("bad val %v", lim)
- }
-}
-
-func TestShuffleNodes(t *testing.T) {
- orig := []*nodeState{
- &nodeState{
- State: StateDead,
- },
- &nodeState{
- State: StateAlive,
- },
- &nodeState{
- State: StateAlive,
- },
- &nodeState{
- State: StateDead,
- },
- &nodeState{
- State: StateAlive,
- },
- &nodeState{
- State: StateAlive,
- },
- &nodeState{
- State: StateDead,
- },
- &nodeState{
- State: StateAlive,
- },
- }
- nodes := make([]*nodeState, len(orig))
- copy(nodes[:], orig[:])
-
- if !reflect.DeepEqual(nodes, orig) {
- t.Fatalf("should match")
- }
-
- shuffleNodes(nodes)
-}
-
-func TestPushPullScale(t *testing.T) {
- sec := time.Second
- for i := 0; i <= 32; i++ {
- if s := pushPullScale(sec, i); s != sec {
- t.Fatalf("Bad time scale: %v", s)
- }
- }
- for i := 33; i <= 64; i++ {
- if s := pushPullScale(sec, i); s != 2*sec {
- t.Fatalf("Bad time scale: %v", s)
- }
- }
- for i := 65; i <= 128; i++ {
- if s := pushPullScale(sec, i); s != 3*sec {
- t.Fatalf("Bad time scale: %v", s)
- }
- }
-}
-
-func TestMoveDeadNodes(t *testing.T) {
- nodes := []*nodeState{
- &nodeState{
- State: StateDead,
- StateChange: time.Now().Add(-20 * time.Second),
- },
- &nodeState{
- State: StateAlive,
- StateChange: time.Now().Add(-20 * time.Second),
- },
- // This dead node should not be moved, as its state changed
- // less than the specified GossipToTheDead time ago
- &nodeState{
- State: StateDead,
- StateChange: time.Now().Add(-10 * time.Second),
- },
- &nodeState{
- State: StateAlive,
- StateChange: time.Now().Add(-20 * time.Second),
- },
- &nodeState{
- State: StateDead,
- StateChange: time.Now().Add(-20 * time.Second),
- },
- &nodeState{
- State: StateAlive,
- StateChange: time.Now().Add(-20 * time.Second),
- },
- }
-
- idx := moveDeadNodes(nodes, (15 * time.Second))
- if idx != 4 {
- t.Fatalf("bad index")
- }
- for i := 0; i < idx; i++ {
- switch i {
- case 2:
- // Recently dead node remains at index 2,
- // since nodes are swapped out to move to end.
- if nodes[i].State != StateDead {
- t.Fatalf("Bad state %d", i)
- }
- default:
- if nodes[i].State != StateAlive {
- t.Fatalf("Bad state %d", i)
- }
- }
- }
- for i := idx; i < len(nodes); i++ {
- if nodes[i].State != StateDead {
- t.Fatalf("Bad state %d", i)
- }
- }
-}
-
-func TestKRandomNodes(t *testing.T) {
- nodes := []*nodeState{}
- for i := 0; i < 90; i++ {
- // Half the nodes are in a bad state
- state := StateAlive
- switch i % 3 {
- case 0:
- state = StateAlive
- case 1:
- state = StateSuspect
- case 2:
- state = StateDead
- }
- nodes = append(nodes, &nodeState{
- Node: Node{
- Name: fmt.Sprintf("test%d", i),
- },
- State: state,
- })
- }
-
- filterFunc := func(n *nodeState) bool {
- if n.Name == "test0" || n.State != StateAlive {
- return true
- }
- return false
- }
-
- s1 := kRandomNodes(3, nodes, filterFunc)
- s2 := kRandomNodes(3, nodes, filterFunc)
- s3 := kRandomNodes(3, nodes, filterFunc)
-
- if reflect.DeepEqual(s1, s2) {
- t.Fatalf("unexpected equal")
- }
- if reflect.DeepEqual(s1, s3) {
- t.Fatalf("unexpected equal")
- }
- if reflect.DeepEqual(s2, s3) {
- t.Fatalf("unexpected equal")
- }
-
- for _, s := range [][]Node{s1, s2, s3} {
- if len(s) != 3 {
- t.Fatalf("bad len")
- }
- for _, n := range s {
- if n.Name == "test0" {
- t.Fatalf("Bad name")
- }
- if n.State != StateAlive {
- t.Fatalf("Bad state")
- }
- }
- }
-}
-
-func TestMakeCompoundMessage(t *testing.T) {
- msg := &ping{SeqNo: 100}
- buf, err := encode(pingMsg, msg)
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
-
- msgs := [][]byte{buf.Bytes(), buf.Bytes(), buf.Bytes()}
- compound := makeCompoundMessage(msgs)
-
- if compound.Len() != 3*buf.Len()+3*compoundOverhead+compoundHeaderOverhead {
- t.Fatalf("bad len")
- }
-}
-
-func TestDecodeCompoundMessage(t *testing.T) {
- msg := &ping{SeqNo: 100}
- buf, err := encode(pingMsg, msg)
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
-
- msgs := [][]byte{buf.Bytes(), buf.Bytes(), buf.Bytes()}
- compound := makeCompoundMessage(msgs)
-
- trunc, parts, err := decodeCompoundMessage(compound.Bytes()[1:])
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
- if trunc != 0 {
- t.Fatalf("should not truncate")
- }
- if len(parts) != 3 {
- t.Fatalf("bad parts")
- }
- for _, p := range parts {
- if len(p) != buf.Len() {
- t.Fatalf("bad part len")
- }
- }
-}
-
-func TestDecodeCompoundMessage_NumberOfPartsOverflow(t *testing.T) {
- buf := []byte{0x80}
- _, _, err := decodeCompoundMessage(buf)
- require.Error(t, err)
- require.Equal(t, err.Error(), "truncated len slice")
-}
-
-func TestDecodeCompoundMessage_Trunc(t *testing.T) {
- msg := &ping{SeqNo: 100}
- buf, err := encode(pingMsg, msg)
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
-
- msgs := [][]byte{buf.Bytes(), buf.Bytes(), buf.Bytes()}
- compound := makeCompoundMessage(msgs)
-
- trunc, parts, err := decodeCompoundMessage(compound.Bytes()[1:38])
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
- if trunc != 1 {
- t.Fatalf("truncate: %d", trunc)
- }
- if len(parts) != 2 {
- t.Fatalf("bad parts")
- }
- for _, p := range parts {
- if len(p) != buf.Len() {
- t.Fatalf("bad part len")
- }
- }
-}
-
-func TestCompressDecompressPayload(t *testing.T) {
- buf, err := compressPayload([]byte("testing"))
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
-
- decomp, err := decompressPayload(buf.Bytes()[1:])
- if err != nil {
- t.Fatalf("unexpected err: %s", err)
- }
-
- if !reflect.DeepEqual(decomp, []byte("testing")) {
- t.Fatalf("bad payload: %v", decomp)
- }
-}
diff --git a/toolkit/memberlist/weightbroadcast.go b/toolkit/memberlist/weightbroadcast.go
deleted file mode 100644
index 49a36933..00000000
--- a/toolkit/memberlist/weightbroadcast.go
+++ /dev/null
@@ -1,43 +0,0 @@
-package memberlist
-
-/*
-The broadcast mechanism works by maintaining a sorted list of messages to be
-sent out. When a message is to be broadcast, the retransmit count
-is set to zero and appended to the queue. The retransmit count serves
-as the "priority", ensuring that newer messages get sent first. Once
-a message hits the retransmit limit, it is removed from the queue.
-
-Additionally, older entries can be invalidated by new messages that
-are contradictory. For example, if we send "{suspect M1 inc: 1},
-then a following {alive M1 inc: 2} will invalidate that message
-*/
-
-type weightBroadcast struct {
- node string
- msg []byte
-}
-
-func NewWeightBroadcast(node string, msg []byte) *weightBroadcast {
- return &weightBroadcast{
- node,
- msg,
- }
-}
-
-func (b *weightBroadcast) Invalidates(other Broadcast) bool {
- // Check if that broadcast is a weight type
- mb, ok := other.(*weightBroadcast)
- if !ok {
- return false
- }
-
- // Invalidates any message about the same node
- return b.node == mb.node
-}
-
-func (b *weightBroadcast) Message() []byte {
- return b.msg
-}
-
-func (b *weightBroadcast) Finished() {
-}
diff --git a/toolkit/memberlist/weightbroadcast_test.go b/toolkit/memberlist/weightbroadcast_test.go
deleted file mode 100644
index a67b4121..00000000
--- a/toolkit/memberlist/weightbroadcast_test.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package memberlist_test
-
-import (
- "testing"
-
- "github.com/golang/mock/gomock"
- "github.com/stretchr/testify/require"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist"
- memmock "github.com/unionj-cloud/go-doudou/v2/toolkit/memberlist/mock"
-)
-
-func Test_weightBroadcast_Invalidates(t *testing.T) {
- msg1 := memberlist.NewWeightBroadcast("testNode", []byte("test weight message1"))
- ok := msg1.Invalidates(memberlist.NewWeightBroadcast("testNode", []byte("test weight message2")))
- require.True(t, ok)
-
- ctrl := gomock.NewController(t)
- defer ctrl.Finish()
- b := memmock.NewMockBroadcast(ctrl)
- ok = msg1.Invalidates(b)
- require.False(t, ok)
-
- require.Equal(t, "test weight message1", string(msg1.Message()))
- msg1.Finished()
-}
diff --git a/toolkit/numberutils/numberutils.go b/toolkit/numberutils/numberutils.go
deleted file mode 100644
index 00cb70fb..00000000
--- a/toolkit/numberutils/numberutils.go
+++ /dev/null
@@ -1,63 +0,0 @@
-package numberutils
-
-import (
- "fmt"
- "sort"
-
- "github.com/shopspring/decimal"
-)
-
-type Percentage struct {
- Value int
- Percent float64
- PercentFormatted string
- Data interface{}
-}
-
-type decimalPercentage struct {
- Percentage *Percentage
- Percent decimal.Decimal
- Remainder decimal.Decimal
-}
-
-// LargestRemainder https://en.wikipedia.org/wiki/Largest_remainder_method
-func LargestRemainder(percentages []Percentage, places int32) {
- if len(percentages) == 0 {
- return
- }
- decimalPercentages := make([]decimalPercentage, len(percentages))
- for i := range percentages {
- decimalPercentages[i] = decimalPercentage{
- Percentage: &percentages[i],
- }
- }
- var sum int
- for _, item := range decimalPercentages {
- sum += item.Percentage.Value
- }
- if sum > 0 {
- for i, item := range decimalPercentages {
- raw := decimal.NewFromFloat(float64(item.Percentage.Value*100) / float64(sum))
- decimalPercentages[i].Percent = raw.RoundFloor(places)
- decimalPercentages[i].Remainder = raw.Sub(decimal.NewFromFloat(percentages[i].Percent))
- }
- var curSum decimal.Decimal
- for _, item := range decimalPercentages {
- curSum = curSum.Add(item.Percent)
- }
- offset := decimal.New(1, -places)
- limit := decimal.NewFromInt(100)
- for curSum.LessThan(limit) {
- sort.Slice(decimalPercentages, func(i, j int) bool {
- return decimalPercentages[j].Remainder.LessThan(decimalPercentages[i].Remainder)
- })
- decimalPercentages[0].Percent = decimalPercentages[0].Percent.Add(offset)
- decimalPercentages[0].Remainder = decimal.Decimal{}
- curSum = curSum.Add(offset)
- }
- }
- for _, item := range decimalPercentages {
- item.Percentage.Percent, _ = item.Percent.Float64()
- item.Percentage.PercentFormatted = fmt.Sprintf("%."+fmt.Sprint(places)+"f%%", item.Percentage.Percent)
- }
-}
diff --git a/toolkit/numberutils/numberutils_test.go b/toolkit/numberutils/numberutils_test.go
deleted file mode 100644
index 4ffba8ad..00000000
--- a/toolkit/numberutils/numberutils_test.go
+++ /dev/null
@@ -1,50 +0,0 @@
-package numberutils_test
-
-import (
- "fmt"
- "testing"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/numberutils"
-)
-
-func TestLargestRemainder(t *testing.T) {
- input := []numberutils.Percentage{
- {
- Value: 10,
- Data: "a",
- },
- {
- Value: 12,
- Data: "b",
- },
- {
- Value: 7,
- Data: "c",
- },
- }
- numberutils.LargestRemainder(input, 3)
- for _, item := range input {
- fmt.Printf("%v\t%v\t%v\t%s\n", item.Data, item.Value, item.Percent, item.PercentFormatted)
- }
-}
-
-func TestLargestRemainderZeroSum(t *testing.T) {
- input := []numberutils.Percentage{
- {
- Value: 1,
- Data: "a",
- },
- {
- Value: 0,
- Data: "b",
- },
- {
- Value: 0,
- Data: "c",
- },
- }
- numberutils.LargestRemainder(input, 3)
- for _, item := range input {
- fmt.Printf("%v\t%v\t%v\t%s\n", item.Data, item.Value, item.Percent, item.PercentFormatted)
- }
-}
diff --git a/toolkit/openapi/v3/helper.go b/toolkit/openapi/v3/helper.go
deleted file mode 100644
index ada97bad..00000000
--- a/toolkit/openapi/v3/helper.go
+++ /dev/null
@@ -1,356 +0,0 @@
-package v3
-
-import (
- "encoding/json"
- "io/ioutil"
- "os"
- "path/filepath"
- "regexp"
- "strings"
- "unicode"
-
- "github.com/getkin/kin-openapi/openapi2"
- "github.com/getkin/kin-openapi/openapi2conv"
- "github.com/getkin/kin-openapi/openapi3"
- "github.com/go-resty/resty/v2"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/copier"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-)
-
-// Schemas from components of OpenAPI3.0 json document
-var Schemas = make(map[string]Schema)
-
-var Enums = make(map[string]astutils.EnumMeta)
-
-// SchemaNames schema names from components of OpenAPI3.0 json document
-// also struct names from vo package
-var SchemaNames []string
-
-// SchemaOf reference https://golang.org/pkg/builtin/
-// type bool
-// type byte
-// type complex128
-// type complex64
-// type error
-// type float32
-// type float64
-// type int
-// type int16
-// type int32
-// type int64
-// type int8
-// type rune
-// type string
-// type uint
-// type uint16
-// type uint32
-// type uint64
-// type uint8
-// type uintptr
-func SchemaOf(field astutils.FieldMeta) *Schema {
- ft := field.Type
- if IsVarargs(ft) {
- ft = ToSlice(ft)
- }
- ft = strings.TrimLeft(ft, "*")
- switch ft {
- case "int", "int8", "int16", "int32", "uint", "uint8", "uint16", "uint32", "byte", "rune", "complex64", "complex128":
- return Int
- case "int64", "uint64", "uintptr":
- return Int64
- case "bool":
- return Bool
- case "string", "error", "[]rune", "[]byte":
- return String
- case "float32":
- return Float32
- case "float64":
- return Float64
- case "multipart.FileHeader", "v3.FileModel":
- return File
- case "time.Time", "customtypes.Time":
- return Time
- case "decimal.Decimal": // simple treat decimal.Decimal as string
- return Decimal
- default:
- return handleDefaultCase(ft)
- }
-}
-
-func handleDefaultCase(ft string) *Schema {
- if strings.HasPrefix(ft, "map[") {
- elem := ft[strings.Index(ft, "]")+1:]
- return &Schema{
- Type: ObjectT,
- AdditionalProperties: SchemaOf(astutils.FieldMeta{
- Type: elem,
- }),
- XMapType: ft,
- }
- }
- if strings.HasPrefix(ft, "[") {
- elem := ft[strings.Index(ft, "]")+1:]
- return &Schema{
- Type: ArrayT,
- Items: SchemaOf(astutils.FieldMeta{
- Type: elem,
- }),
- }
- }
- re := regexp.MustCompile(`anonystruct«(.*)»`)
- if re.MatchString(ft) {
- result := re.FindStringSubmatch(ft)
- var structmeta astutils.StructMeta
- json.Unmarshal([]byte(result[1]), &structmeta)
- schema := NewSchema(structmeta)
- return &schema
- }
- var title string
- if !strings.Contains(ft, ".") {
- title = ft
- }
- if stringutils.IsEmpty(title) {
- title = ft[strings.LastIndex(ft, ".")+1:]
- }
- if stringutils.IsNotEmpty(title) {
- if unicode.IsUpper(rune(title[0])) {
- if sliceutils.StringContains(SchemaNames, title) {
- return &Schema{
- Ref: "#/components/schemas/" + title,
- }
- }
- }
- if enumMeta, ok := Enums[title]; ok {
- enumSchema := &Schema{
- Type: StringT,
- Enum: sliceutils.StringSlice2InterfaceSlice(enumMeta.Values),
- }
- if len(enumMeta.Values) > 0 {
- enumSchema.Default = enumMeta.Values[0]
- }
- return enumSchema
- }
- }
- return Any
-}
-
-var castFuncMap = map[string]string{
- "bool": "ToBool",
- "float64": "ToFloat64",
- "float32": "ToFloat32",
- "int64": "ToInt64",
- "int32": "ToInt32",
- "int16": "ToInt16",
- "int8": "ToInt8",
- "int": "ToInt",
- "uint": "ToUint",
- "uint8": "ToUint8",
- "uint16": "ToUint16",
- "uint32": "ToUint32",
- "uint64": "ToUint64",
- "error": "ToError",
- "decimal.Decimal": "ToDecimal",
- "[]byte": "ToByteSlice",
- "[]rune": "ToRuneSlice",
- "[]interface{}": "ToInterfaceSlice",
- "[]bool": "ToBoolSlice",
- "[]int": "ToIntSlice",
- "[]float64": "ToFloat64Slice",
- "[]float32": "ToFloat32Slice",
- "[]int64": "ToInt64Slice",
- "[]int32": "ToInt32Slice",
- "[]int16": "ToInt16Slice",
- "[]int8": "ToInt8Slice",
- "[]uint": "ToUintSlice",
- "[]uint8": "ToUint8Slice",
- "[]uint16": "ToUint16Slice",
- "[]uint32": "ToUint32Slice",
- "[]uint64": "ToUint64Slice",
- "[]error": "ToErrorSlice",
- "[]decimal.Decimal": "ToDecimalSlice",
- "[][]byte": "ToByteSliceSlice",
- "[][]rune": "ToRuneSliceSlice",
-}
-
-func IsSupport(t string) bool {
- if IsVarargs(t) {
- t = ToSlice(t)
- }
- _, exists := castFuncMap[strings.TrimLeft(t, "*")]
- return exists
-}
-
-func IsOptional(t string) bool {
- return strings.HasPrefix(t, "*") || strings.HasPrefix(t, "...")
-}
-
-func IsSlice(t string) bool {
- return strings.Contains(t, "[") || strings.HasPrefix(t, "...")
-}
-
-func IsVarargs(t string) bool {
- return strings.HasPrefix(t, "...")
-}
-
-func ToSlice(t string) string {
- return "[]" + strings.TrimPrefix(t, "...")
-}
-
-func CastFunc(t string) string {
- if IsVarargs(t) {
- t = ToSlice(t)
- }
- return castFuncMap[strings.TrimLeft(t, "*")]
-}
-
-// CopySchema as SchemaOf returns pointer, so deepcopy the schema the pointer points
-func CopySchema(field astutils.FieldMeta) Schema {
- var schema Schema
- err := copier.DeepCopy(SchemaOf(field), &schema)
- if err != nil {
- panic(err)
- }
- return schema
-}
-
-func RefAddDoc(schema *Schema, doc string) {
- if stringutils.IsNotEmpty(schema.Ref) {
- title := strings.TrimPrefix(schema.Ref, "#/components/schemas/")
- temp := Schemas[title]
- temp.Description = strings.Join([]string{doc, temp.Description}, "\n")
- Schemas[title] = temp
- } else {
- schema.Description = doc
- }
-}
-
-// NewSchema new schema from astutils.StructMeta
-func NewSchema(structmeta astutils.StructMeta) Schema {
- properties := make(map[string]*Schema)
- var required []string
- for _, field := range structmeta.Fields {
- fschema := CopySchema(field)
- RefAddDoc(&fschema, strings.Join(field.Comments, "\n"))
- properties[field.DocName] = &fschema
- if !strings.HasPrefix(field.Type, "*") {
- required = append(required, field.DocName)
- }
- }
- return Schema{
- Title: structmeta.Name,
- Type: ObjectT,
- Properties: properties,
- Description: strings.Join(structmeta.Comments, "\n"),
- Required: required,
- }
-}
-
-// IsBuiltin check whether field is built-in type https://pkg.go.dev/builtin or not
-func IsBuiltin(field astutils.FieldMeta) bool {
- simples := []interface{}{Int, Int64, Bool, String, Float32, Float64}
- types := []interface{}{IntegerT, StringT, BooleanT, NumberT}
- pschema := SchemaOf(field)
- if sliceutils.Contains(simples, pschema) || (sliceutils.Contains(types, pschema.Type) && pschema.Format != BinaryF) {
- return true
- }
- if pschema.Type == ArrayT && (sliceutils.Contains(simples, pschema.Items) || (sliceutils.Contains(types, pschema.Items.Type) && pschema.Items.Format != BinaryF)) {
- return true
- }
- return false
-}
-
-// IsEnum check whether field is enum
-func IsEnum(field astutils.FieldMeta) bool {
- pschema := SchemaOf(field)
- return len(pschema.Enum) > 0 || (pschema.Type == ArrayT && len(pschema.Items.Enum) > 0)
-}
-
-// IsStruct check whether field is struct type
-func IsStruct(field astutils.FieldMeta) bool {
- return stringutils.IsNotEmpty(SchemaOf(field).Ref)
-}
-
-// ElementType get element type string from slice
-func ElementType(t string) string {
- if IsVarargs(t) {
- return strings.TrimPrefix(t, "...")
- }
- return t[strings.Index(t, "]")+1:]
-}
-
-func LoadAPI(file string) API {
- var (
- docfile *os.File
- err error
- docraw []byte
- api API
- )
- if strings.HasPrefix(file, "http") {
- link := file
- client := resty.New()
- client.SetRedirectPolicy(resty.FlexibleRedirectPolicy(15))
- root, _ := os.Getwd()
- client.SetOutputDirectory(root)
- filename := ".openapi3"
- _, err := client.R().
- SetOutput(filename).
- Get(link)
- if err != nil {
- panic(err)
- }
- file = filepath.Join(root, filename)
- defer os.Remove(file)
- }
- if docfile, err = os.Open(file); err != nil {
- panic(err)
- }
- defer docfile.Close()
- if docraw, err = ioutil.ReadAll(docfile); err != nil {
- panic(err)
- }
- if err = json.Unmarshal(docraw, &api); err != nil {
- panic(err)
- }
- if stringutils.IsEmpty(api.Openapi) {
- var doc openapi2.T
- if err = json.Unmarshal(docraw, &doc); err != nil {
- panic(err)
- }
- if stringutils.IsEmpty(doc.Swagger) {
- panic("not support")
- }
- var doc1 *openapi3.T
- doc1, err = openapi2conv.ToV3(&doc)
- if err != nil {
- panic(err)
- }
- doc1Json, _ := json.Marshal(doc1)
- root, _ := os.Getwd()
- spec := filepath.Join(root, "swaggerV2ToV3"+filepath.Ext(file))
- os.WriteFile(spec, doc1Json, os.ModePerm)
- copier.DeepCopy(doc1, &api)
- }
- return api
-}
-
-func ToOptional(t string) string {
- if !strings.HasPrefix(t, "*") {
- return "*" + t
- }
- return t
-}
-
-func IsEnumType(methods []astutils.MethodMeta) bool {
- methodMap := make(map[string]struct{})
- for _, item := range methods {
- methodMap[item.String()] = struct{}{}
- }
- for _, item := range IEnumMethods {
- if _, ok := methodMap[item]; !ok {
- return false
- }
- }
- return true
-}
diff --git a/toolkit/openapi/v3/helper_test.go b/toolkit/openapi/v3/helper_test.go
deleted file mode 100644
index 8b7621e6..00000000
--- a/toolkit/openapi/v3/helper_test.go
+++ /dev/null
@@ -1,280 +0,0 @@
-package v3
-
-import (
- "go/ast"
- "go/parser"
- "go/token"
- "strings"
- "testing"
-
- . "github.com/smartystreets/goconvey/convey"
- "github.com/stretchr/testify/require"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/pathutils"
-)
-
-func Test_isSupport(t *testing.T) {
- type args struct {
- t string
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {
- name: "1",
- args: args{
- t: "float32",
- },
- want: true,
- },
- {
- name: "2",
- args: args{
- t: "[]int64",
- },
- want: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := IsSupport(tt.args.t); got != tt.want {
- t.Errorf("isSupport() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func Test_castFunc(t *testing.T) {
- type args struct {
- t string
- }
- tests := []struct {
- name string
- args args
- want string
- }{
- {
- name: "1",
- args: args{
- t: "uint64",
- },
- want: "ToUint64",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := CastFunc(tt.args.t); got != tt.want {
- t.Errorf("castFunc() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestSchemaOf(t *testing.T) {
- Convey("SchemaOf", t, func() {
- So(SchemaOf(astutils.FieldMeta{
- Name: "avatar",
- Type: "v3.FileModel",
- IsExport: true,
- }), ShouldEqual, File)
-
- So(SchemaOf(astutils.FieldMeta{
- Name: "name",
- Type: "string",
- IsExport: true,
- }), ShouldEqual, String)
-
- So(SchemaOf(astutils.FieldMeta{
- Name: "age",
- Type: "int",
- IsExport: true,
- }), ShouldEqual, Int)
-
- So(SchemaOf(astutils.FieldMeta{
- Name: "id",
- Type: "int64",
- IsExport: true,
- }), ShouldEqual, Int64)
-
- So(SchemaOf(astutils.FieldMeta{
- Name: "married",
- Type: "bool",
- IsExport: true,
- }), ShouldEqual, Bool)
-
- So(SchemaOf(astutils.FieldMeta{
- Name: "score",
- Type: "float32",
- IsExport: true,
- }), ShouldEqual, Float32)
-
- So(SchemaOf(astutils.FieldMeta{
- Name: "average",
- Type: "float64",
- IsExport: true,
- }), ShouldEqual, Float64)
-
- So(SchemaOf(astutils.FieldMeta{
- Name: "params",
- Type: "...int",
- IsExport: true,
- }).Type, ShouldEqual, ArrayT)
-
- So(SchemaOf(astutils.FieldMeta{
- Name: "data",
- Type: "map[string]string",
- IsExport: true,
- }).Type, ShouldEqual, ObjectT)
-
- So(SchemaOf(astutils.FieldMeta{
- Name: "anony",
- Type: "anonystruct«{\"Name\":\"\",\"Fields\":[{\"Name\":\"Name\",\"Type\":\"string\",\"Tag\":\"\",\"Comments\":null,\"IsExport\":true,\"DocName\":\"Name\"},{\"Name\":\"Addr\",\"Type\":\"anonystruct«{\\\"Name\\\":\\\"\\\",\\\"Fields\\\":[{\\\"Name\\\":\\\"Zip\\\",\\\"Type\\\":\\\"string\\\",\\\"Tag\\\":\\\"\\\",\\\"Comments\\\":null,\\\"IsExport\\\":true,\\\"DocName\\\":\\\"Zip\\\"},{\\\"Name\\\":\\\"Block\\\",\\\"Type\\\":\\\"string\\\",\\\"Tag\\\":\\\"\\\",\\\"Comments\\\":null,\\\"IsExport\\\":true,\\\"DocName\\\":\\\"Block\\\"},{\\\"Name\\\":\\\"Full\\\",\\\"Type\\\":\\\"string\\\",\\\"Tag\\\":\\\"\\\",\\\"Comments\\\":null,\\\"IsExport\\\":true,\\\"DocName\\\":\\\"Full\\\"}],\\\"Comments\\\":null,\\\"Methods\\\":null,\\\"IsExport\\\":false}»\",\"Tag\":\"\",\"Comments\":null,\"IsExport\":true,\"DocName\":\"Addr\"}],\"Comments\":null,\"Methods\":null,\"IsExport\":false}»",
- IsExport: true,
- }).Type, ShouldEqual, ObjectT)
-
- SchemaNames = []string{"User"}
- So(SchemaOf(astutils.FieldMeta{
- Name: "user",
- Type: "User",
- IsExport: true,
- }).Ref, ShouldEqual, "#/components/schemas/User")
-
- So(SchemaOf(astutils.FieldMeta{
- Name: "user",
- Type: "vo.User",
- IsExport: true,
- }).Ref, ShouldEqual, "#/components/schemas/User")
-
- Enums = map[string]astutils.EnumMeta{
- "KeyboardLayout": {
- Name: "KeyboardLayout",
- Values: []string{
- "UNKNOWN",
- "QWERTZ",
- },
- },
- }
- So(SchemaOf(astutils.FieldMeta{
- Name: "layout",
- Type: "KeyboardLayout",
- IsExport: true,
- }).Enum, ShouldResemble, []interface{}{
- "UNKNOWN",
- "QWERTZ",
- })
-
- So(SchemaOf(astutils.FieldMeta{
- Name: "any",
- Type: "Any",
- IsExport: true,
- }), ShouldEqual, Any)
- })
-}
-
-func TestRefAddDoc(t *testing.T) {
- Convey("Description should be equal to doc", t, func() {
- SchemaNames = []string{"User"}
- schema := SchemaOf(astutils.FieldMeta{
- Name: "user",
- Type: "User",
- IsExport: true,
- })
- doc := "This is a struct for user field"
- RefAddDoc(schema, doc)
- So(strings.TrimSpace(Schemas["User"].Description), ShouldEqual, doc)
- })
-}
-
-func TestIsBuiltin(t *testing.T) {
- Convey("Test IsBuiltIn", t, func() {
- So(IsBuiltin(astutils.FieldMeta{
- Name: "age",
- Type: "int",
- IsExport: true,
- }), ShouldBeTrue)
-
- So(IsBuiltin(astutils.FieldMeta{
- Name: "books",
- Type: "[]string",
- IsExport: true,
- }), ShouldBeTrue)
-
- So(IsBuiltin(astutils.FieldMeta{
- Name: "data",
- Type: "map[string]string",
- IsExport: true,
- }), ShouldBeFalse)
- })
-}
-
-func TestIsEnum(t *testing.T) {
- Convey("Test IsEnum", t, func() {
- So(IsEnum(astutils.FieldMeta{
- Name: "age",
- Type: "int",
- IsExport: true,
- }), ShouldBeFalse)
-
- Enums = map[string]astutils.EnumMeta{
- "KeyboardLayout": {
- Name: "KeyboardLayout",
- Values: []string{
- "UNKNOWN",
- "QWERTZ",
- },
- },
- }
- So(IsEnum(astutils.FieldMeta{
- Name: "layout",
- Type: "KeyboardLayout",
- IsExport: true,
- }), ShouldBeTrue)
- })
-}
-
-func TestElementType(t *testing.T) {
- Convey("Test ElementType", t, func() {
- So(ElementType("[]int"), ShouldEqual, "int")
- So(ElementType("...int"), ShouldEqual, "int")
- })
-}
-
-func TestIsOptional(t *testing.T) {
- Convey("Test IsOptional", t, func() {
- So(IsOptional("*[]int"), ShouldBeTrue)
- So(IsOptional("...int"), ShouldBeTrue)
- So(IsOptional("int"), ShouldBeFalse)
- })
-}
-
-func TestIsSlice(t *testing.T) {
- Convey("Test IsOptional", t, func() {
- So(IsSlice("*[]int"), ShouldBeTrue)
- So(IsSlice("...int"), ShouldBeTrue)
- So(IsSlice("int"), ShouldBeFalse)
- })
-}
-
-func TestEnum(t *testing.T) {
- file := pathutils.Abs("testdata/enum.go")
- fset := token.NewFileSet()
- root, err := parser.ParseFile(fset, file, nil, parser.ParseComments)
- if err != nil {
- panic(err)
- }
- sc := astutils.NewEnumCollector(astutils.ExprString)
- ast.Walk(sc, root)
- enumMap := make(map[string]astutils.EnumMeta)
- for k, v := range sc.Methods {
- if IsEnumType(v) {
- em := astutils.EnumMeta{
- Name: k,
- Values: sc.Consts[k],
- }
- enumMap[k] = em
- }
- }
- require.Equal(t, 1, len(enumMap))
-}
diff --git a/toolkit/openapi/v3/model.go b/toolkit/openapi/v3/model.go
deleted file mode 100644
index d6c9045b..00000000
--- a/toolkit/openapi/v3/model.go
+++ /dev/null
@@ -1,379 +0,0 @@
-package v3
-
-import (
- "io"
-)
-
-// Contact https://spec.openapis.org/oas/v3.0.3#contact-object
-type Contact struct {
- Email string `json:"email,omitempty"`
-}
-
-// License https://spec.openapis.org/oas/v3.0.3#license-object
-type License struct {
- Name string `json:"name,omitempty"`
- URL string `json:"url,omitempty"`
-}
-
-// Info https://spec.openapis.org/oas/v3.0.3#info-object
-type Info struct {
- Title string `json:"title,omitempty"`
- Description string `json:"description,omitempty"`
- TermsOfService string `json:"termsOfService,omitempty"`
- Contact *Contact `json:"contact,omitempty"`
- License *License `json:"license,omitempty"`
- Version string `json:"version,omitempty"`
-}
-
-// Server https://spec.openapis.org/oas/v3.0.3#server-object
-type Server struct {
- URL string `json:"url,omitempty"`
-}
-
-// ExternalDocs https://spec.openapis.org/oas/v3.0.3#external-documentation-object
-type ExternalDocs struct {
- Description string `json:"description,omitempty"`
- URL string `json:"url,omitempty"`
-}
-
-// Tag https://spec.openapis.org/oas/v3.0.3#tag-object
-type Tag struct {
- Name string `json:"name,omitempty"`
- Description string `json:"description,omitempty"`
- ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"`
-}
-
-// In represents parameter position
-type In string
-
-const (
- // InQuery query string parameter
- InQuery In = "query"
- // InPath TODO not implemented yet
- InPath In = "path"
- // InHeader TODO not implemented yet
- InHeader In = "header"
- // InCookie TODO not implemented yet
- InCookie In = "cookie"
-)
-
-// Example https://spec.openapis.org/oas/v3.0.3#example-object
-type Example struct {
- // TODO
-}
-
-// Encoding https://spec.openapis.org/oas/v3.0.3#encoding-object
-type Encoding struct {
- // TODO
-}
-
-// MediaType https://spec.openapis.org/oas/v3.0.3#media-type-object
-type MediaType struct {
- Schema *Schema `json:"schema,omitempty"`
- Example interface{} `json:"example,omitempty"`
- Examples map[string]Example `json:"examples,omitempty"`
- Encoding map[string]Encoding `json:"encoding,omitempty"`
-}
-
-// Content REQUIRED. The content of the request body. The key is a media type or [media type range]appendix-D)
-// and the value describes it. For requests that match multiple keys, only the most specific key is applicable.
-// e.g. text/plain overrides text/*
-type Content struct {
- TextPlain *MediaType `json:"text/plain,omitempty"`
- JSON *MediaType `json:"application/json,omitempty"`
- FormURL *MediaType `json:"application/x-www-form-urlencoded,omitempty"`
- Stream *MediaType `json:"application/octet-stream,omitempty"`
- FormData *MediaType `json:"multipart/form-data,omitempty"`
- Default *MediaType `json:"*/*,omitempty"`
-}
-
-// Parameter https://spec.openapis.org/oas/v3.0.3#parameter-object
-type Parameter struct {
- Name string `json:"name,omitempty"`
- In In `json:"in,omitempty"`
- Description string `json:"description,omitempty"`
- Required bool `json:"required,omitempty"`
- Deprecated bool `json:"deprecated,omitempty"`
- Example interface{} `json:"example,omitempty"`
- Schema *Schema `json:"schema,omitempty"`
- Style string `json:"style,omitempty"`
- Explode bool `json:"explode,omitempty"`
- AllowReserved bool `json:"allowReserved,omitempty"`
- Content *Content `json:"content,omitempty"`
- AllowEmptyValue bool `json:"allowEmptyValue,omitempty"`
-}
-
-// RequestBody https://spec.openapis.org/oas/v3.0.3#request-body-object
-type RequestBody struct {
- Description string `json:"description,omitempty"`
- Content *Content `json:"content,omitempty"`
- Required bool `json:"required,omitempty"`
- Ref string `json:"$ref,omitempty"`
-}
-
-// Header https://spec.openapis.org/oas/v3.0.3#header-object
-type Header struct {
- Ref string `json:"$ref,omitempty"`
- Description string `json:"description,omitempty"`
- Required bool `json:"required,omitempty"`
- Deprecated bool `json:"deprecated,omitempty"`
- Example interface{} `json:"example,omitempty"`
- Schema *Schema `json:"schema,omitempty"`
-}
-
-// Link https://spec.openapis.org/oas/v3.0.3#link-object
-type Link struct {
- // TODO
-}
-
-// Response https://spec.openapis.org/oas/v3.0.3#response-object
-type Response struct {
- Description string `json:"description"`
- Content *Content `json:"content,omitempty"`
- // TODO
- Headers map[string]Header `json:"headers,omitempty"`
- Links map[string]Link `json:"links,omitempty"`
- Ref string `json:"$ref,omitempty"`
-}
-
-// Responses https://spec.openapis.org/oas/v3.0.3#responses-object
-type Responses struct {
- Resp200 *Response `json:"200,omitempty"`
- Resp400 *Response `json:"400,omitempty"`
- Resp401 *Response `json:"401,omitempty"`
- Resp403 *Response `json:"403,omitempty"`
- Resp404 *Response `json:"404,omitempty"`
- Resp405 *Response `json:"405,omitempty"`
- Default *Response `json:"default,omitempty"`
-}
-
-// Callback https://spec.openapis.org/oas/v3.0.3#callback-object
-type Callback struct {
- // TODO
-}
-
-// Security not implemented yet
-type Security struct {
- // TODO
-}
-
-// Operation https://spec.openapis.org/oas/v3.0.3#operation-object
-type Operation struct {
- Tags []string `json:"tags,omitempty"`
- Summary string `json:"summary,omitempty"`
- Description string `json:"description,omitempty"`
- OperationID string `json:"operationId,omitempty"`
- Parameters []Parameter `json:"parameters,omitempty"`
- RequestBody *RequestBody `json:"requestBody,omitempty"`
- Responses *Responses `json:"responses,omitempty"`
- Deprecated bool `json:"deprecated,omitempty"`
- ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"`
- Callbacks map[string]Callback `json:"callbacks,omitempty"`
- Security []Security `json:"security,omitempty"`
- Servers []Server `json:"servers,omitempty"`
-}
-
-// Path https://spec.openapis.org/oas/v3.0.3#path-item-object
-type Path struct {
- Get *Operation `json:"get,omitempty"`
- Post *Operation `json:"post,omitempty"`
- Put *Operation `json:"put,omitempty"`
- Delete *Operation `json:"delete,omitempty"`
- // TODO
- Parameters []Parameter `json:"parameters,omitempty"`
-}
-
-// SecurityScheme https://spec.openapis.org/oas/v3.0.3#security-scheme-object
-type SecurityScheme struct {
- // TODO
-}
-
-// Discriminator https://spec.openapis.org/oas/v3.0.3#discriminator-object
-type Discriminator struct {
- PropertyName string `json:"propertyName,omitempty"`
- Mapping map[string]string `json:"mapping,omitempty"`
-}
-
-// Schema https://spec.openapis.org/oas/v3.0.3#schema-object
-type Schema struct {
- Ref string `json:"$ref,omitempty"`
- Title string `json:"title,omitempty"`
- Type Type `json:"type,omitempty"`
- Properties map[string]*Schema `json:"properties,omitempty"`
- Format Format `json:"format,omitempty"`
- Items *Schema `json:"items,omitempty"`
- Description string `json:"description,omitempty"`
- Default interface{} `json:"default,omitempty"`
- Example interface{} `json:"example,omitempty"`
- Deprecated bool `json:"deprecated,omitempty"`
- Discriminator *Discriminator `json:"discriminator,omitempty"`
- Nullable bool `json:"nullable,omitempty"`
- Maximum interface{} `json:"maximum,omitempty"`
- Minimum interface{} `json:"minimum,omitempty"`
- ExclusiveMaximum interface{} `json:"exclusiveMaximum,omitempty"`
- ExclusiveMinimum interface{} `json:"exclusiveMinimum,omitempty"`
- MaxLength int `json:"maxLength,omitempty"`
- MinLength int `json:"minLength,omitempty"`
- Required []string `json:"required,omitempty"`
- Enum []interface{} `json:"enum,omitempty"`
- AllOf []*Schema `json:"allOf,omitempty"`
- OneOf []*Schema `json:"oneOf,omitempty"`
- AnyOf []*Schema `json:"anyOf,omitempty"`
- Not []*Schema `json:"not,omitempty"`
- // AdditionalProperties *Schema or bool
- AdditionalProperties interface{} `json:"additionalProperties,omitempty"`
- Pattern interface{} `json:"pattern,omitempty"`
- XMapType string `json:"x-map-type,omitempty"`
-}
-
-// Components https://spec.openapis.org/oas/v3.0.3#components-object
-type Components struct {
- Schemas map[string]Schema `json:"schemas,omitempty"`
- RequestBodies map[string]RequestBody `json:"requestBodies,omitempty"`
- Responses map[string]Response `json:"responses,omitempty"`
- // TODO
- Parameters map[string]Parameter `json:"parameters,omitempty"`
- // TODO
- Examples map[string]Example `json:"examples,omitempty"`
- // TODO
- Headers map[string]Header `json:"headers,omitempty"`
- // TODO
- SecuritySchemes map[string]SecurityScheme `json:"securitySchemes,omitempty"`
- // TODO
- Links map[string]Link `json:"links,omitempty"`
- // TODO
- Callbacks map[string]Callback `json:"callbacks,omitempty"`
-}
-
-// API https://spec.openapis.org/oas/v3.0.3#openapi-object
-type API struct {
- Openapi string `json:"openapi,omitempty"`
- Info *Info `json:"info,omitempty"`
- Servers []Server `json:"servers,omitempty"`
- Tags []Tag `json:"tags,omitempty"`
- Paths map[string]Path `json:"paths,omitempty"`
- Components *Components `json:"components,omitempty"`
- ExternalDocs *ExternalDocs `json:"externalDocs,omitempty"`
-}
-
-// Type represents types in OpenAPI3.0 spec
-type Type string
-
-const (
- // IntegerT integer
- IntegerT Type = "integer"
- // StringT string
- StringT Type = "string"
- // BooleanT boolean
- BooleanT Type = "boolean"
- // NumberT number
- NumberT Type = "number"
- // ObjectT object
- ObjectT Type = "object"
- // ArrayT array
- ArrayT Type = "array"
-)
-
-// Format represents format in OpenAPI3.0 spec
-type Format string
-
-const (
- // Int32F int32
- Int32F Format = "int32"
- // Int64F int64
- Int64F Format = "int64"
- // FloatF float
- FloatF Format = "float"
- // DoubleF double
- DoubleF Format = "double"
- // DateTimeF date-time
- DateTimeF Format = "date-time"
- // BinaryF binary
- BinaryF Format = "binary"
- DecimalF Format = "decimal"
-)
-
-var (
- // Any constant schema for object
- Any = &Schema{
- Type: ObjectT,
- }
- // Int constant schema for int
- Int = &Schema{
- Type: IntegerT,
- Format: Int32F,
- }
- // Int64 constant schema for int64
- Int64 = &Schema{
- Type: IntegerT,
- Format: Int64F,
- }
- // String constant schema for string
- String = &Schema{
- Type: StringT,
- }
- // Time constant schema for time
- Time = &Schema{
- Type: StringT,
- Format: DateTimeF,
- }
- // Bool constant schema for bool
- Bool = &Schema{
- Type: BooleanT,
- }
- // Float32 constant schema for float32
- Float32 = &Schema{
- Type: NumberT,
- Format: FloatF,
- }
- // Float64 constant schema for float64
- Float64 = &Schema{
- Type: NumberT,
- Format: DoubleF,
- }
- // File constant schema for file
- File = &Schema{
- Type: StringT,
- Format: BinaryF,
- }
- // FileArray constant schema for file slice
- FileArray = &Schema{
- Type: ArrayT,
- Items: File,
- }
- Decimal = &Schema{
- Type: StringT,
- Format: DecimalF,
- }
-)
-
-type FileModel struct {
- Filename string
- Reader io.ReadCloser
-}
-
-func (f *FileModel) Close() error {
- return f.Reader.Close()
-}
-
-type IEnum interface {
- StringSetter(value string)
- StringGetter() string
- UnmarshalJSON(bytes []byte) error
- MarshalJSON() ([]byte, error)
-}
-
-var IEnumMethods = []string{
- "func StringSetter(value string)",
- "func StringGetter() string",
- "func UnmarshalJSON(bytes []byte) error",
- "func MarshalJSON() ([]byte, error)",
-}
-
-type ExampleType int
-
-const (
- UNKNOWN_EXAMPLE ExampleType = iota
- JSON_EXAMPLE
- TEXT_EXAMPLE
-)
diff --git a/toolkit/openapi/v3/testdata/enum.go b/toolkit/openapi/v3/testdata/enum.go
deleted file mode 100644
index c3d61206..00000000
--- a/toolkit/openapi/v3/testdata/enum.go
+++ /dev/null
@@ -1,65 +0,0 @@
-package main
-
-import "encoding/json"
-
-//go:generate go-doudou name --file $GOFILE -o
-
-type KeyboardLayout int
-
-const (
- UNKNOWN KeyboardLayout = iota
- QWERTZ = 300
-
- AZERTY int64 = 400
- QWERTY
- ddd = "abc"
-)
-
-func (k *KeyboardLayout) StringSetter(value string) {
- switch value {
- case "UNKNOWN":
- *k = UNKNOWN
- case "QWERTY":
- *k = QWERTY
- case "QWERTZ":
- *k = QWERTZ
- case "AZERTY":
- *k = AZERTY
- default:
- *k = UNKNOWN
- }
-}
-
-func (k *KeyboardLayout) StringGetter() string {
- switch *k {
- case UNKNOWN:
- return "UNKNOWN"
- case QWERTY:
- return "QWERTY"
- case QWERTZ:
- return "QWERTZ"
- case AZERTY:
- return "AZERTY"
- default:
- return "UNKNOWN"
- }
-}
-
-func (k *KeyboardLayout) UnmarshalJSON(bytes []byte) error {
- var _k string
- err := json.Unmarshal(bytes, &_k)
- if err != nil {
- return err
- }
- k.StringSetter(_k)
- return nil
-}
-
-func (k KeyboardLayout) MarshalJSON() ([]byte, error) {
- return json.Marshal(k.StringGetter())
-}
-
-type Keyboard struct {
- Layout *KeyboardLayout `json:"layout,omitempty"`
- Backlit bool `json:"backlit,omitempty"`
-}
diff --git a/toolkit/pagination/gorm/LICENSE b/toolkit/pagination/gorm/LICENSE
deleted file mode 100644
index 746ea061..00000000
--- a/toolkit/pagination/gorm/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2020 morkid
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/toolkit/pagination/gorm/README.md b/toolkit/pagination/gorm/README.md
deleted file mode 100644
index b93105e7..00000000
--- a/toolkit/pagination/gorm/README.md
+++ /dev/null
@@ -1,877 +0,0 @@
-# paginate - Gorm Pagination
-
-[![Go Reference](https://pkg.go.dev/badge/github.com/morkid/paginate.svg)](https://pkg.go.dev/github.com/morkid/paginate)
-[![CircleCI](https://circleci.com/gh/morkid/paginate.svg?style=svg)](https://circleci.com/gh/morkid/paginate)
-[![Github Actions](https://github.com/morkid/paginate/workflows/Go/badge.svg)](https://github.com/morkid/paginate/actions)
-[![Build Status](https://travis-ci.com/morkid/paginate.svg?branch=master)](https://travis-ci.com/morkid/paginate)
-[![Go Report Card](https://goreportcard.com/badge/github.com/morkid/paginate)](https://goreportcard.com/report/github.com/morkid/paginate)
-[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/morkid/paginate)](https://github.com/morkid/paginate/releases)
-
-Simple way to paginate [Gorm](https://github.com/go-gorm/gorm) result. **paginate** is compatible with [net/http](https://golang.org/pkg/net/http/) and [fasthttp](https://github.com/valyala/fasthttp). This library also supports many frameworks are based on net/http or fasthttp.
-
-## Table Of Contents
-- [Installation](#installation)
-- [Configuration](#configuration)
-- [Paginate using http request](#paginate-using-http-request)
-- [Example usage](#example-usage)
- - [net/http](#nethttp-example)
- - [Fasthttp](#fasthttp-example)
- - [Mux Router](#mux-router-example)
- - [Fiber](#fiber-example)
- - [Echo](#echo-example)
- - [Gin](#gin-example)
- - [Martini](#martini-example)
- - [Beego](#beego-example)
- - [jQuery DataTable Integration](#jquery-datatable-integration)
- - [jQuery Select2 Integration](#jquery-select2-integration)
-- [Filter format](#filter-format)
-- [Customize default configuration](#customize-default-configuration)
-- [Override results](#override-results)
-- [Field Selector](#field-selector)
-- [Dynamic Field Selector](#dynamic-field-selector)
-- [Speed up response with cache](#speed-up-response-with-cache)
- - [In Memory Cache](#in-memory-cache)
- - [Disk Cache](#disk-cache)
- - [Redis Cache](#redis-cache)
- - [Elasticsearch Cache](#elasticsearch-cache)
- - [Custom cache](#custom-cache)
- - [Clean up cache](#clean-up-cache)
-- [Limitations](#limitations)
-- [License](#license)
-
-## Installation
-
-```bash
-go get -u github.com/morkid/paginate
-```
-
-## Configuration
-
-```go
-var db *gorm.DB = ...
-var req *http.Request = ...
-// or
-// var req *fasthttp.Request
-
-model := db.Where("id > ?", 1).Model(&Article{})
-pg := paginate.New()
-page := pg.Response(model, req, &[]Article{})
-// or
-page := pg.With(model).Request(req).Response(&[]Article{})
-
-log.Println(page.Total)
-log.Println(page.Items)
-log.Println(page.First)
-log.Println(page.Last)
-
-```
-you can customize config with `paginate.Config` struct.
-```go
-pg := paginate.New(&paginate.Config{
- DefaultSize: 50,
-})
-```
-see more about [customize default configuration](#customize-default-configuration).
-
-> Note that `Response` was marked as a deprecated function. Please use `With` instead.
-> Old: `pg.Response(model, req, &[]Article{})`,
-> New: `pg.With(model).Request(req).Response(&[]Article{})`
-
-## Paginate using http request
-example paging, sorting and filtering:
-1. `http://localhost:3000/?size=10&page=0&sort=-name`
- produces:
- ```sql
- SELECT * FROM user ORDER BY name DESC LIMIT 10 OFFSET 0
- ```
- `JSON` response:
- ```js
- {
- // result items
- "items": [
- {
- "id": 1,
- "name": "john",
- "age": 20
- }
- ],
- "page": 0, // current selected page
- "size": 10, // current limit or size per page
- "max_page": 0, // maximum page
- "total_pages": 1, // total pages
- "total": 1, // total matches including next page
- "visible": 1, // total visible on current page
- "last": true, // if response is first page
- "first": true // if response is last page
- }
- ```
-2. `http://localhost:3000/?size=10&page=1&sort=-name,id`
- produces:
- ```sql
- SELECT * FROM user ORDER BY name DESC, id ASC LIMIT 10 OFFSET 10
- ```
-3. `http://localhost:3000/?filters=["name","john"]`
- produces:
- ```sql
- SELECT * FROM user WHERE name = 'john' LIMIT 10 OFFSET 0
- ```
-4. `http://localhost:3000/?filters=["name","like","john"]`
- produces:
- ```sql
- SELECT * FROM user WHERE name LIKE '%john%' LIMIT 10 OFFSET 0
- ```
-5. `http://localhost:3000/?filters=["age","between",[20, 25]]`
- produces:
- ```sql
- SELECT * FROM user WHERE ( age BETWEEN 20 AND 25 ) LIMIT 10 OFFSET 0
- ```
-6. `http://localhost:3000/?filters=[["name","like","john%25"],["OR"],["age","between",[20, 25]]]`
- produces:
- ```sql
- SELECT * FROM user WHERE (
- (name LIKE '%john\%%' ESCAPE '\') OR (age BETWEEN (20 AND 25))
- ) LIMIT 10 OFFSET 0
- ```
-7. `http://localhost:3000/?filters=[[["name","like","john"],["AND"],["name","not like","doe"]],["OR"],["age","between",[20, 25]]]`
- produces:
- ```sql
- SELECT * FROM user WHERE (
- (
- (name LIKE '%john%')
- AND
- (name NOT LIKE '%doe%')
- )
- OR
- (age BETWEEN (20 AND 25))
- ) LIMIT 10 OFFSET 0
- ```
-8. `http://localhost:3000/?filters=["name","IS NOT",null]`
- produces:
- ```sql
- SELECT * FROM user WHERE name IS NOT NULL LIMIT 10 OFFSET 0
- ```
-9. Using `POST` method:
- ```bash
- curl -X POST \
- -H 'Content-type: application/json' \
- -d '{"page":"1","size":"20","sort":"-name","filters":["name","john"]}' \
- http://localhost:3000/
- ```
-
-## Example usage
-
-### NetHTTP Example
-
-```go
-package main
-
-import (
- "github.com/morkid/paginate"
- ...
-)
-
-func main() {
- // var db *gorm.DB
- pg := paginate.New()
-
- http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
- model := db.Joins("User").Model(&Article{})
- paginated := pg.Response(model, r, &[]Article{})
- j, _ := json.Marshal(paginated)
- w.Header().Set("Content-type", "application/json")
- w.Write(j)
- })
-
- log.Fatal(http.ListenAndServe(":3000", nil))
-}
-```
-
-### Fasthttp Example
-
-```go
-package main
-
-import (
- "github.com/morkid/paginate"
- ...
-)
-
-func main() {
- // var db *gorm.DB
- pg := paginate.New()
-
- fasthttp.ListenAndServe(":3000", func(ctx *fasthttp.RequestCtx) {
- model := db.Joins("User").Model(&Article{})
- paginated := pg.Response(model, &ctx.Request, &[]Article{})
- j, _ := json.Marshal(paginated)
- ctx.SetContentType("application/json")
- ctx.SetBody(j)
- })
-}
-```
-
-### Mux Router Example
-```go
-package main
-
-import (
- "github.com/morkid/paginate"
- ...
-)
-
-func main() {
- // var db *gorm.DB
- pg := paginate.New()
- app := mux.NewRouter()
- app.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
- model := db.Joins("User").Model(&Article{})
- paginated := pg.Response(model, req, &[]Article{})
- j, _ := json.Marshal(paginated)
- w.Header().Set("Content-type", "application/json")
- w.Write(j)
- }).Methods("GET")
- http.Handle("/", app)
- http.ListenAndServe(":3000", nil)
-}
-```
-
-### Fiber example
-
-```go
-package main
-
-import (
- "github.com/morkid/paginate"
- ...
-)
-
-func main() {
- // var db *gorm.DB
- pg := paginate.New()
- app := fiber.New()
- app.Get("/", func(c *fiber.Ctx) error {
- model := db.Joins("User").Model(&Article{})
- return c.JSON(pg.Response(model, c.Request(), &[]Article{}))
- })
-
- app.Listen(":3000")
-}
-```
-
-### Echo example
-
-```go
-package main
-
-import (
- "github.com/morkid/paginate"
- ...
-)
-
-func main() {
- // var db *gorm.DB
- pg := paginate.New()
- app := echo.New()
- app.GET("/", func(c echo.Context) error {
- model := db.Joins("User").Model(&Article{})
- return c.JSON(200, pg.Response(model, c.Request(), &[]Article{}))
- })
-
- app.Logger.Fatal(app.Start(":3000"))
-}
-```
-
-### Gin Example
-
-```go
-package main
-
-import (
- "github.com/morkid/paginate"
- ...
-)
-
-func main() {
- // var db *gorm.DB
- pg := paginate.New()
- app := gin.Default()
- app.GET("/", func(c *gin.Context) {
- model := db.Joins("User").Model(&Article{})
- c.JSON(200, pg.Response(model, c.Request, &[]Article{}))
- })
- app.Run(":3000")
-}
-
-```
-
-### Martini Example
-
-```go
-package main
-
-import (
- "github.com/morkid/paginate"
- ...
-)
-
-func main() {
- // var db *gorm.DB
- pg := paginate.New()
- app := martini.Classic()
- app.Use(render.Renderer())
- app.Get("/", func(req *http.Request, r render.Render) {
- model := db.Joins("User").Model(&Article{})
- r.JSON(200, pg.Response(model, req, &[]Article{}))
- })
- app.Run()
-}
-```
-### Beego Example
-
-```go
-package main
-
-import (
- "github.com/morkid/paginate"
- ...
-)
-
-func main() {
- // var db *gorm.DB
- pg := paginate.New()
- web.Get("/", func(c *context.Context) {
- model := db.Joins("User").Model(&Article{})
- c.Output.JSON(
- pg.Response(model, c.Request, &[]Article{}), false, false)
- })
- web.Run(":3000")
-}
-```
-
-### jQuery DataTable Integration
-
-```js
-var logicalOperator = "OR"
-
-$('#myTable').DataTable({
-
- columns: [
- {
- title: "Author",
- data: "user.name"
- }, {
- title: "Title",
- data: "title"
- }
- ],
-
- processing: true,
-
- serverSide: true,
-
- ajax: {
- cache: true,
- url: "http://localhost:3000/articles",
- dataSrc: function(json) {
- json.recordsTotal = json.visible
- json.recordsFiltered = json.total
- return json.items
- },
- data: function(params) {
- var custom = {
- page: !params.start ? 0 : Math.round(params.start / params.length),
- size: params.length
- }
-
- if (params.order.length > 0) {
- var sorts = []
- for (var o in params.order) {
- var order = params.order[o]
- if (params.columns[order.column].orderable != false) {
- var sort = order.dir != 'desc' ? '' : '-'
- sort += params.columns[order.column].data
- sorts.push(sort)
- }
- }
- custom.sort = sorts.join()
- }
-
- if (params.search.value) {
- var columns = []
- for (var c in params.columns) {
- var col = params.columns[c]
- if (col.searchable == false) {
- continue
- }
- columns.push(JSON.stringify([col.data, "like", encodeURIComponent(params.search.value.toLowerCase())]))
- }
- custom.filters = '[' + columns.join(',["' + logicalOperator + '"],') + ']'
- }
-
- return custom
- }
- },
-})
-```
-
-### jQuery Select2 Integration
-
-```js
-$('#mySelect').select2({
- ajax: {
- url: "http://localhost:3000/users",
- processResults: function(json) {
- json.items.forEach(function(item) {
- item.text = item.name
- })
- // optional
- if (json.first) json.items.unshift({id: 0, text: 'All'})
-
- return {
- results: json.items,
- pagination: {
- more: json.last == false
- }
- }
- },
- data: function(params) {
- var filters = [
- ["name", "like", params.term]
- ]
-
- return {
- filters: params.term ? JSON.stringify(filters) : "",
- sort: "name",
- page: params.page && params.page - 1 ? params.page - 1 : 0
- }
- },
- }
-})
-```
-
-
-## Filter format
-
-The format of filter param is a json encoded of multidimensional array.
-Maximum array members is three, first index is `column_name`, second index is `operator` and third index is `values`, you can also pass array to values.
-
-```js
-// Format:
-["column_name", "operator", "values"]
-
-// Example:
-["age", "=", 20]
-// Shortcut:
-["age", 20]
-
-// Produces:
-// WHERE age = 20
-```
-
-Single array member is known as **Logical Operator**.
-```js
-// Example
-[["age", "=", 20],["or"],["age", "=", 25]]
-
-// Produces:
-// WHERE age = 20 OR age = 25
-```
-
-You are allowed to send array inside a value.
-```js
-["age", "between", [20, 30] ]
-// Produces:
-// WHERE age BETWEEN 20 AND 30
-
-["age", "not in", [20, 21, 22, 23, 24, 25, 26, 26] ]
-// Produces:
-// WHERE age NOT IN(20, 21, 22, 23, 24, 25, 26, 26)
-```
-
-You can filter nested condition with deep array.
-```js
-[
- [
- ["age", ">", 20],
- ["and"]
- ["age", "<", 30]
- ],
- ["and"],
- ["name", "like", "john"],
- ["and"],
- ["name", "like", "doe"]
-]
-// Produces:
-// WHERE ( (age > 20 AND age < 20) and name like '%john%' and name like '%doe%' )
-```
-
-For `null` value, you can send string `"null"` or `null` value, *(lower)*
-```js
-// Wrong request
-[ "age", "is", NULL ]
-[ "age", "is", Null ]
-[ "age", "is not", NULL ]
-[ "age", "is not", Null ]
-
-// Right request
-[ "age", "is", "NULL" ]
-[ "age", "is", "Null" ]
-[ "age", "is", "null" ]
-[ "age", "is", null ]
-[ "age", null ]
-[ "age", "is not", "NULL" ]
-[ "age", "is not", "Null" ]
-[ "age", "is not", "null" ]
-[ "age", "is not", null ]
-```
-
-## Customize default configuration
-
-You can customize the default configuration with `paginate.Config` struct.
-
-```go
-pg := paginate.New(&paginate.Config{
- DefaultSize: 50,
-})
-```
-
-Config | Type | Default | Description
------------------- | ---------- | --------------------- | -------------
-Operator | `string` | `OR` | Default conditional operator if no operator specified.
For example
`GET /user?filters=[["name","like","jo"],["age",">",20]]`,
produces
`SELECT * FROM user where name like '%jo' OR age > 20`
-FieldWrapper | `string` | `LOWER(%s)` | FieldWrapper for `LIKE` operator *(for postgres default is: `LOWER((%s)::text)`)*
-DefaultSize | `int64` | `10` | Default size or limit per page
-SmartSearch | `bool` | `false` | Enable smart search *(Experimental feature)*
-CustomParamEnabled | `bool` | `false` | Enable custom request parameter
-FieldSelectorEnabled | `bool` | `false` | Enable partial response with specific fields. Comma separated per field. eg: `?fields=title,user.name`
-SortParams | `[]string` | `[]string{"sort"}` | if `CustomParamEnabled` is `true`,
you can set the `SortParams` with custom parameter names.
For example: `[]string{"sorting", "ordering", "other_alternative_param"}`.
The following requests will capture same result
`?sorting=-name`
or `?ordering=-name`
or `?other_alternative_param=-name`
or `?sort=-name`
-PageParams | `[]string` | `[]string{"page"}` | if `CustomParamEnabled` is `true`,
you can set the `PageParams` with custom parameter names.
For example:
`[]string{"number", "num", "other_alternative_param"}`.
The following requests will capture same result `?number=0`
or `?num=0`
or `?other_alternative_param=0`
or `?page=0`
-SizeParams | `[]string` | `[]string{"size"}` | if `CustomParamEnabled` is `true`,
you can set the `SizeParams` with custom parameter names.
For example:
`[]string{"limit", "max", "other_alternative_param"}`.
The following requests will capture same result `?limit=50`
or `?limit=50`
or `?other_alternative_param=50`
or `?max=50`
-OrderParams | `[]string` | `[]string{"order"}` | if `CustomParamEnabled` is `true`,
you can set the `OrderParams` with custom parameter names.
For example:
`[]string{"order", "direction", "other_alternative_param"}`.
The following requests will capture same result `?order=desc`
or `?direction=desc`
or `?other_alternative_param=desc`
-FilterParams | `[]string` | `[]string{"filters"}` | if `CustomParamEnabled` is `true`,
you can set the `FilterParams` with custom parameter names.
For example:
`[]string{"search", "find", "other_alternative_param"}`.
The following requests will capture same result
`?search=["name","john"]`
or `?find=["name","john"]`
or `?other_alternative_param=["name","john"]`
or `?filters=["name","john"]`
-FieldsParams | `[]string` | `[]string{"fields"}` | if `FieldSelectorEnabled` and `CustomParamEnabled` is `true`,
you can set the `FieldsParams` with custom parameter names.
For example:
`[]string{"fields", "columns", "other_alternative_param"}`.
The following requests will capture same result `?fields=title,user.name`
or `?columns=title,user.name`
or `?other_alternative_param=title,user.name`
-CacheAdapter | `*gocache.AdapterInterface` | `nil` | the cache adapter, see more about [cache config](#speed-up-response-with-cache).
-
-## Override results
-
-You can override result with custom function.
-
-```go
-// var db = *gorm.DB
-// var httpRequest ... net/http or fasthttp instance
-// Example override function
-override := func(article *Article) {
- if article.UserID > 0 {
- article.Title = fmt.Sprintf(
- "%s written by %s", article.Title, article.User.Name)
- }
-}
-
-var articles []Article
-model := db.Joins("User").Model(&Article{})
-
-pg := paginate.New()
-result := pg.Response(model, httpRequest, &articles)
-for index := range articles {
- override(&articles[index])
-}
-
-log.Println(result.Items)
-
-```
-
-## Field selector
-To implement a custom field selector, struct properties must have a json tag with omitempty.
-
-```go
-// real gorm model
-type User {
- gorm.Model
- Name string `json:"name"`
- Age int64 `json:"age"`
-}
-
-// fake gorm model
-type UserNullable {
- ID *string `json:"id,omitempty"`
- CreatedAt *time.Time `json:"created_at,omitempty"`
- UpdatedAt *time.Time `json:"updated_at,omitempty"`
- Name *string `json:"name,omitempty"`
- Age *int64 `json:"age,omitempty"`
-}
-```
-
-```go
-// usage
-nameAndIDOnly := []string{"name","id"}
-model := db.Model(&User{})
-
-page := pg.With(model).
- Request(req).
- Fields(nameAndIDOnly).
- Response([]&UserNullable{})
-```
-
-```javascript
-// response
-{
- "items": [
- {
- "id": 1,
- "name": "John"
- }
- ],
- ...
-}
-```
-## Dynamic field selector
-If the request contains query parameter `fields` (eg: `?fieilds=name,id`), then the response will show only `name` and `id`. To activate this feature, please set `FieldSelectorEnabled` to `true`.
-```go
-config := paginate.Config{
- FieldSelectorEnabled: true,
-}
-
-pg := paginate.New(config)
-```
-
-## Speed up response with cache
-You can speed up results without looking database directly with cache adapter. See more about [cache adapter](https://github.com/morkid/gocache).
-
-### In memory cache
-in memory cache is not recommended for production environment:
-```go
-import (
- "github.com/morkid/gocache"
- ...
-)
-
-func main() {
- ...
- adapterConfig := gocache.InMemoryCacheConfig{
- ExpiresIn: 1 * time.Hour,
- }
- pg := paginate.New(&paginate.Config{
- CacheAdapter: gocache.NewInMemoryCache(adapterConfig),
- })
-
- page := pg.With(model).
- Request(req).
- Cache("article"). // set cache name
- Response(&[]Article{})
- ...
-}
-```
-
-### Disk cache
-Disk cache will create a file for every single request. You can use disk cache if you don't care about inode.
-```go
-import (
- "github.com/morkid/gocache"
- ...
-)
-
-func main() {
- adapterConfig := gocache.DiskCacheConfig{
- Directory: "/writable/path/to/my-cache-dir",
- ExpiresIn: 1 * time.Hour,
- }
- pg := paginate.New(&paginate.Config{
- CacheAdapter: gocache.NewDiskCache(adapterConfig),
- })
-
- page := pg.With(model).
- Request(req).
- Cache("article"). // set cache name
- Response(&[]Article{})
- ...
-}
-```
-
-### Redis cache
-Redis cache require [redis client](https://github.com/go-redis/redis) for golang.
-```go
-import (
- cache "github.com/morkid/gocache-redis/v8"
- "github.com/go-redis/redis/v8"
- ...
-)
-
-func main() {
- client := redis.NewClient(&redis.Options{
- Addr: "localhost:6379",
- Password: "",
- DB: 0,
- })
-
- adapterConfig := cache.RedisCacheConfig{
- Client: client,
- ExpiresIn: 1 * time.Hour,
- }
- pg := paginate.New(&paginate.Config{
- CacheAdapter: cache.NewRedisCache(adapterConfig),
- })
-
- page := pg.With(model).
- Request(req).
- Cache("article").
- Response(&[]Article{})
- ...
-}
-```
-> if your code already adopts another redis client, you can implement the [redis adapter](https://github.com/morkid/gocache-redis) according to its version. See more about [redis adapter](https://github.com/morkid/gocache-redis).
-
-### Elasticsearch cache
-Elasticsearch cache require official [elasticsearch client](https://github.com/elastic/go-elasticsearch) for golang.
-```go
-import (
- cache "github.com/morkid/gocache-elasticsearch/v7"
- "github.com/elastic/go-elasticsearch/v7"
- ...
-)
-
-func main() {
- config := elasticsearch.Config{
- Addresses: []string{
- "http://localhost:9200",
- },
- }
- es, err := elasticsearch.NewClient(config)
- if nil != err {
- panic(err)
- }
-
- adapterConfig := cache.ElasticCacheConfig{
- Client: es,
- Index: "exampleproject",
- ExpiresIn: 1 * time.Hour,
- }
- pg := paginate.New(&paginate.Config{
- CacheAdapter: cache.NewElasticCache(adapterConfig),
- })
-
- page := pg.With(model).
- Request(req).
- Cache("article").
- Response(&[]Article{})
- ...
-}
-```
-> if your code already adopts another elasticsearch client, you can implement the [elasticsearch adapter](https://github.com/morkid/gocache-elasticsearch) according to its version. See more about [elasticsearch adapter](https://github.com/morkid/gocache-elasticsearch).
-
-### Custom cache
-Create your own cache adapter by implementing [gocache AdapterInterface](https://github.com/morkid/gocache/blob/master/gocache.go). See more about [cache adapter](https://github.com/morkid/gocache).
-```go
-// AdapterInterface interface
-type AdapterInterface interface {
- // Set cache with key
- Set(key string, value string) error
- // Get cache by key
- Get(key string) (string, error)
- // IsValid check if cache is valid
- IsValid(key string) bool
- // Clear clear cache by key
- Clear(key string) error
- // ClearPrefix clear cache by key prefix
- ClearPrefix(keyPrefix string) error
- // Clear all cache
- ClearAll() error
-}
-```
-
-### Clean up cache
-Clear cache by cache name
-```go
-pg.ClearCache("article")
-```
-Clear multiple cache
-```go
-pg.ClearCache("cache1", "cache2", "cache3")
-```
-
-Clear all cache
-```go
-pg.ClearAllCache()
-```
-
-
-## Limitations
-
-Paginate doesn't support has many relationship. You can make API with separated endpoints for parent and child:
-```javascript
-GET /users
-
-{
- "items": [
- {
- "id": 1,
- "name": "john",
- "age": 20,
- "addresses": [...] // doesn't support
- }
- ],
- ...
-}
-```
-
-Best practice:
-
-```javascript
-GET /users
-{
- "items": [
- {
- "id": 1,
- "name": "john",
- "age": 20
- }
- ],
- ...
-}
-
-GET /users/1/addresses
-{
- "items": [
- {
- "id": 1,
- "name": "home",
- "street": "home street"
- "user": {
- "id": 1,
- "name": "john",
- "age": 20
- }
- }
- ],
- ...
-}
-```
-
-Paginate doesn't support for customized json or table field name.
-Make sure your struct properties have same name with gorm column and json property before you expose them.
-
-Example bad configuration:
-
-```go
-
-type User struct {
- gorm.Model
- UserName string `gorm:"column:nickname" json:"name"`
- UserAddress string `gorm:"column:user_address" json:"address"`
-}
-
-// request: GET /path/to/endpoint?sort=-name,address
-// response: "items": [] with sql error (column name not found)
-```
-
-Best practice:
-```go
-type User struct {
- gorm.Model
- Name string `gorm:"column:name" json:"name"`
- Address string `gorm:"column:address" json:"address"`
-}
-
-```
-
-## License
-
-Published under the [MIT License](https://github.com/morkid/paginate/blob/master/LICENSE).
\ No newline at end of file
diff --git a/toolkit/pagination/gorm/paginate.go b/toolkit/pagination/gorm/paginate.go
deleted file mode 100644
index 7501cbb7..00000000
--- a/toolkit/pagination/gorm/paginate.go
+++ /dev/null
@@ -1,801 +0,0 @@
-package gorm
-
-import (
- "crypto/md5"
- "fmt"
- "log"
- "math"
- "github.com/goccy/go-reflect"
- "regexp"
- "strconv"
- "strings"
-
- "github.com/bytedance/sonic"
- "github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
-
- "github.com/morkid/gocache"
- "github.com/wubin1989/gorm"
-)
-
-var json = sonic.ConfigDefault
-
-// ResponseContext interface
-type ResponseContext interface {
- Cache(string) ResponseContext
- Fields([]string) ResponseContext
- Distinct([]string) ResponseContext
- Response(interface{}) Page
- BuildWhereClause() (string, []interface{})
- Error() error
-}
-
-// RequestContext interface
-type RequestContext interface {
- Request(IParameter) ResponseContext
-}
-
-// Pagination gorm paginate struct
-type Pagination struct {
- Config *Config
-}
-
-// With func
-func (p *Pagination) With(stmt *gorm.DB) RequestContext {
- return reqContext{
- Statement: stmt,
- Pagination: p,
- }
-}
-
-// ClearCache clear cache contains prefix
-func (p Pagination) ClearCache(keyPrefixes ...string) {
- if len(keyPrefixes) > 0 && nil != p.Config && nil != p.Config.CacheAdapter {
- adapter := *p.Config.CacheAdapter
- for i := range keyPrefixes {
- if err := adapter.ClearPrefix(keyPrefixes[i]); nil != err {
- log.Println(err)
- }
- }
- }
-}
-
-// ClearAllCache clear all existing cache
-func (p Pagination) ClearAllCache() {
- if nil != p.Config && nil != p.Config.CacheAdapter {
- adapter := *p.Config.CacheAdapter
- if err := adapter.ClearAll(); nil != err {
- log.Println(err)
- }
- }
-}
-
-type reqContext struct {
- Statement *gorm.DB
- Pagination *Pagination
-}
-
-func (r reqContext) Request(parameter IParameter) ResponseContext {
- var response ResponseContext = &resContext{
- Statement: r.Statement,
- Parameter: parameter,
- Pagination: r.Pagination,
- }
-
- return response
-}
-
-type resContext struct {
- Pagination *Pagination
- Statement *gorm.DB
- Parameter IParameter
- cachePrefix string
- fieldList []string
- customSelect string
- distinct bool
- error error
-}
-
-func (r *resContext) Error() error {
- return r.error
-}
-
-func (r *resContext) Cache(prefix string) ResponseContext {
- r.cachePrefix = prefix
- return r
-}
-
-func (r *resContext) Fields(fields []string) ResponseContext {
- r.fieldList = fields
- return r
-}
-
-// CustomSelect currently used for distinct on clause
-func (r *resContext) Distinct(fields []string) ResponseContext {
- r.fieldList = fields
- r.distinct = true
- return r
-}
-
-func (r *resContext) Response(res interface{}) Page {
- p := r.Pagination
- query := r.Statement
- p.Config = defaultConfig(p.Config)
- p.Config.Statement = query.Statement
- if p.Config.DefaultSize == 0 {
- p.Config.DefaultSize = 10
- }
-
- if p.Config.FieldWrapper == "" && p.Config.ValueWrapper == "" {
- defaultWrapper := "LOWER(%s)"
- wrappers := map[string]string{
- "sqlite": defaultWrapper,
- "mysql": defaultWrapper,
- "postgres": "LOWER((%s)::text)",
- }
- p.Config.FieldWrapper = defaultWrapper
- if wrapper, ok := wrappers[query.Dialector.Name()]; ok {
- p.Config.FieldWrapper = wrapper
- }
- }
-
- page := Page{}
- pr, err := parseRequest(r.Parameter, *p.Config)
- if err != nil {
- r.error = err
- return page
- }
- causes := createCauses(pr)
- cKey := ""
- var adapter gocache.AdapterInterface
- var hasAdapter bool = false
-
- if nil != p.Config.CacheAdapter {
- cKey = createCacheKey(r.cachePrefix, pr)
- adapter = *p.Config.CacheAdapter
- hasAdapter = true
- if cKey != "" && adapter.IsValid(cKey) {
- if cache, err := adapter.Get(cKey); nil == err {
- if err := p.Config.JSONUnmarshal([]byte(cache), &page); nil == err {
- return page
- }
- }
- }
- }
-
- dbs := query.Statement.DB.Session(&gorm.Session{NewDB: true})
- var selects []string
- if len(r.fieldList) > 0 {
- if len(pr.Fields) > 0 && p.Config.FieldSelectorEnabled {
- for i := range pr.Fields {
- for j := range r.fieldList {
- if r.fieldList[j] == pr.Fields[i] {
- fname := query.Statement.Quote("s." + fieldName(pr.Fields[i]))
- if !contains(selects, fname) {
- selects = append(selects, fname)
- }
- break
- }
- }
- }
- } else {
- for i := range r.fieldList {
- fname := query.Statement.Quote("s." + fieldName(r.fieldList[i]))
- if !contains(selects, fname) {
- selects = append(selects, fname)
- }
- }
- }
- } else if len(pr.Fields) > 0 && p.Config.FieldSelectorEnabled {
- for i := range pr.Fields {
- fname := query.Statement.Quote("s." + fieldName(pr.Fields[i]))
- if !contains(selects, fname) {
- selects = append(selects, fname)
- }
- }
- }
-
- result := dbs.
- Unscoped().
- Table("(?) AS s", query)
-
- if len(selects) > 0 {
- if r.distinct {
- result = result.Distinct(selects)
- } else {
- result = result.Select(selects)
- }
- }
-
- if len(causes.Params) > 0 || len(causes.WhereString) > 0 {
- result = result.Where(causes.WhereString, causes.Params...)
- }
-
- dbs = query.Statement.DB.Session(&gorm.Session{NewDB: true})
- result = dbs.
- Unscoped().
- Table("(?) AS s1", result)
-
- if pr.Page >= 0 {
- result = result.Count(&page.Total).
- Limit(int(causes.Limit)).
- Offset(int(causes.Offset))
- }
- if result.Error != nil {
- r.error = result.Error
- return page
- }
-
- if nil != query.Statement.Preloads {
- for table, args := range query.Statement.Preloads {
- result = result.Preload(table, args...)
- }
- }
- if len(causes.Sorts) > 0 {
- for _, sort := range causes.Sorts {
- result = result.Order(sort.Column + " " + sort.Direction)
- }
- }
-
- rs := result.Find(res)
- if result.Error != nil {
- r.error = result.Error
- return page
- }
-
- page.Items, _ = sliceutils.ConvertAny2Interface(res)
- f := float64(page.Total) / float64(causes.Limit)
- if math.Mod(f, 1.0) > 0 {
- f = f + 1
- }
- page.TotalPages = int64(f)
- page.Page = int64(pr.Page)
- page.Size = int64(pr.Size)
- page.MaxPage = 0
- page.Visible = rs.RowsAffected
- if page.TotalPages > 0 {
- page.MaxPage = page.TotalPages - 1
- }
- if page.TotalPages < 1 {
- page.TotalPages = 1
- }
- if page.Total < 1 {
- page.MaxPage = 0
- page.TotalPages = 0
- }
- page.First = causes.Offset < 1
- page.Last = page.MaxPage == page.Page
-
- if hasAdapter && cKey != "" {
- if cache, err := p.Config.JSONMarshal(page); nil == err {
- if err := adapter.Set(cKey, string(cache)); err != nil {
- log.Println(err)
- }
- }
- }
-
- return page
-}
-
-func (r *resContext) BuildWhereClause() (statement string, args []interface{}) {
- p := r.Pagination
- query := r.Statement
- p.Config = defaultConfig(p.Config)
- p.Config.Statement = query.Statement
- if p.Config.DefaultSize == 0 {
- p.Config.DefaultSize = 10
- }
-
- if p.Config.FieldWrapper == "" && p.Config.ValueWrapper == "" {
- defaultWrapper := "LOWER(%s)"
- wrappers := map[string]string{
- "sqlite": defaultWrapper,
- "mysql": defaultWrapper,
- "postgres": "LOWER((%s)::text)",
- }
- p.Config.FieldWrapper = defaultWrapper
- if wrapper, ok := wrappers[query.Dialector.Name()]; ok {
- p.Config.FieldWrapper = wrapper
- }
- }
-
- pr, err := parseRequest(r.Parameter, *p.Config)
- if err != nil {
- r.error = err
- return "", nil
- }
- causes := createCauses(pr)
- return causes.WhereString, causes.Params
-}
-
-// New Pagination instance
-func New(params ...interface{}) *Pagination {
- if len(params) >= 1 {
- var config *Config
- for _, param := range params {
- c, isConfig := param.(*Config)
- if isConfig {
- config = c
- continue
- }
- }
-
- return &Pagination{Config: defaultConfig(config)}
- }
-
- return &Pagination{Config: defaultConfig(nil)}
-}
-
-// parseRequest func
-func parseRequest(param IParameter, config Config) (pageRequest, error) {
- pr := pageRequest{
- Config: *defaultConfig(&config),
- }
- err := parsingQueryString(param, &pr)
- if err != nil {
- return pageRequest{}, err
- }
- return pr, nil
-}
-
-// createFilters func
-func createFilters(filterParams interface{}, p *pageRequest) error {
- s, ok2 := filterParams.(string)
- if reflect.ValueOf(filterParams).Kind() == reflect.Slice {
- f, err := sliceutils.ConvertAny2Interface(filterParams)
- if err != nil {
- return errors.WithStack(err)
- }
- p.Filters = arrayToFilter(f, p.Config)
- p.Filters.Fields = p.Fields
- } else if ok2 {
- iface := []interface{}{}
- if e := p.Config.JSONUnmarshal([]byte(s), &iface); nil == e && len(iface) > 0 {
- p.Filters = arrayToFilter(iface, p.Config)
- }
- p.Filters.Fields = p.Fields
- }
- return nil
-}
-
-// createCauses func
-func createCauses(p pageRequest) requestQuery {
- query := requestQuery{}
- wheres, params := generateWhereCauses(p.Filters, p.Config)
- sorts := []sortOrder{}
-
- for _, so := range p.Sorts {
- so.Column = fieldName(so.Column)
- if nil != p.Config.Statement {
- so.Column = p.Config.Statement.Quote(so.Column)
- }
- sorts = append(sorts, so)
- }
-
- query.Limit = p.Size
- query.Offset = p.Page * p.Size
- query.Wheres = wheres
- query.WhereString = strings.Join(wheres, " ")
- query.Sorts = sorts
- query.Params = params
-
- return query
-}
-
-func parsingQueryString(param IParameter, p *pageRequest) error {
- p.Size = param.GetSize()
-
- if p.Size == 0 {
- if p.Config.DefaultSize > 0 {
- p.Size = p.Config.DefaultSize
- } else {
- p.Size = 10
- }
- }
-
- p.Page = param.GetPage()
-
- if param.GetSort() != "" {
- sorts := strings.Split(param.GetSort(), ",")
- for _, col := range sorts {
- if col == "" {
- continue
- }
-
- so := sortOrder{
- Column: col,
- Direction: "ASC",
- }
- if strings.ToUpper(param.GetOrder()) == "DESC" {
- so.Direction = "DESC"
- }
-
- if string(col[0]) == "-" {
- so.Column = string(col[1:])
- so.Direction = "DESC"
- }
-
- p.Sorts = append(p.Sorts, so)
- }
- }
-
- if param.GetFields() != "" {
- re := regexp.MustCompile(`[^A-z0-9_\.,]+`)
- if fields := strings.Split(param.GetFields(), ","); len(fields) > 0 {
- for i := range fields {
- fieldByte := re.ReplaceAll([]byte(fields[i]), []byte(""))
- if field := string(fieldByte); field != "" {
- p.Fields = append(p.Fields, field)
- }
- }
- }
- }
-
- return createFilters(param.GetFilters(), p)
-}
-
-//gocyclo:ignore
-func arrayToFilter(arr []interface{}, config Config) pageFilters {
- filters := pageFilters{
- Single: false,
- }
-
- operatorEscape := regexp.MustCompile(`[^A-z=\<\>\-\+\^/\*%&! ]+`)
- arrayLen := len(arr)
-
- if len(arr) > 0 {
- subFilters := []pageFilters{}
- for k, i := range arr {
- iface, ok := i.([]interface{})
- if ok && !filters.Single {
- subFilters = append(subFilters, arrayToFilter(iface, config))
- } else if arrayLen == 1 {
- operator, ok := i.(string)
- if ok {
- operator = operatorEscape.ReplaceAllString(operator, "")
- filters.Operator = strings.ToUpper(operator)
- filters.IsOperator = true
- filters.Single = true
- }
- } else if arrayLen == 2 {
- if k == 0 {
- if column, ok := i.(string); ok {
- filters.Column = column
- filters.Operator = "="
- filters.Single = true
- }
- } else if k == 1 {
- filters.Value = i
- if nil == i {
- filters.Operator = "IS"
- }
- }
- } else if arrayLen == 3 {
- if k == 0 {
- if column, ok := i.(string); ok {
- filters.Column = column
- filters.Single = true
- }
- } else if k == 1 {
- if operator, ok := i.(string); ok {
- operator = operatorEscape.ReplaceAllString(operator, "")
- filters.Operator = strings.ToUpper(operator)
- filters.Single = true
- }
- } else if k == 2 {
- switch filters.Operator {
- case "LIKE", "ILIKE", "NOT LIKE", "NOT ILIKE":
- escapeString := ""
- escapePattern := `(%|\\)`
- if nil != config.Statement {
- driverName := config.Statement.Dialector.Name()
- switch driverName {
- case "sqlite", "sqlserver", "postgres":
- escapeString = `\`
- filters.ValueSuffix = "ESCAPE '\\'"
- case "mysql":
- escapeString = `\`
- filters.ValueSuffix = `ESCAPE '\\'`
- }
- }
- value := fmt.Sprintf("%v", i)
- re := regexp.MustCompile(escapePattern)
- value = string(re.ReplaceAll([]byte(value), []byte(escapeString+`$1`)))
- if config.SmartSearch {
- re := regexp.MustCompile(`[\s]+`)
- byt := re.ReplaceAll([]byte(value), []byte("%"))
- value = string(byt)
- }
- filters.Value = fmt.Sprintf("%s%s%s", "%", value, "%")
- default:
- filters.Value = i
- }
- }
- }
- }
- if len(subFilters) > 0 {
- separatedSubFilters := []pageFilters{}
- hasOperator := false
- defaultOperator := config.Operator
- if "" == defaultOperator {
- defaultOperator = "OR"
- }
- for k, s := range subFilters {
- if s.IsOperator && len(subFilters) == (k+1) {
- break
- }
- if !hasOperator && !s.IsOperator && k > 0 {
- separatedSubFilters = append(separatedSubFilters, pageFilters{
- Operator: defaultOperator,
- IsOperator: true,
- Single: true,
- })
- }
- hasOperator = s.IsOperator
- separatedSubFilters = append(separatedSubFilters, s)
- }
- filters.Value = separatedSubFilters
- filters.Single = false
- }
- }
-
- return filters
-}
-
-//gocyclo:ignore
-func generateWhereCauses(f pageFilters, config Config) ([]string, []interface{}) {
- wheres := []string{}
- params := []interface{}{}
-
- if !f.Single && !f.IsOperator {
- ifaces, ok := f.Value.([]pageFilters)
- if ok && len(ifaces) > 0 {
- wheres = append(wheres, "(")
- hasOpen := false
- for _, i := range ifaces {
- subs, isSub := i.Value.([]pageFilters)
- regular, isNotSub := i.Value.(pageFilters)
- if isSub && len(subs) > 0 {
- wheres = append(wheres, "(")
- for _, s := range subs {
- subWheres, subParams := generateWhereCauses(s, config)
- wheres = append(wheres, subWheres...)
- params = append(params, subParams...)
- }
- wheres = append(wheres, ")")
- } else if isNotSub {
- subWheres, subParams := generateWhereCauses(regular, config)
- wheres = append(wheres, subWheres...)
- params = append(params, subParams...)
- } else {
- if !hasOpen && !i.IsOperator {
- wheres = append(wheres, "(")
- hasOpen = true
- }
- subWheres, subParams := generateWhereCauses(i, config)
- wheres = append(wheres, subWheres...)
- params = append(params, subParams...)
- }
- }
- if hasOpen {
- wheres = append(wheres, ")")
- }
- wheres = append(wheres, ")")
- }
- } else if f.Single {
- if f.IsOperator {
- wheres = append(wheres, f.Operator)
- } else {
- fname := fieldName(f.Column)
- if nil != config.Statement {
- fname = config.Statement.Quote(fname)
- }
- switch f.Operator {
- case "IS", "IS NOT":
- if nil == f.Value {
- wheres = append(wheres, fname, f.Operator, "NULL")
- } else {
- if strValue, isStr := f.Value.(string); isStr && strings.ToLower(strValue) == "null" {
- wheres = append(wheres, fname, f.Operator, "NULL")
- } else {
- wheres = append(wheres, fname, f.Operator, "?")
- params = append(params, f.Value)
- }
- }
- case "BETWEEN":
- if values, ok := f.Value.([]interface{}); ok && len(values) >= 2 {
- wheres = append(wheres, "(", fname, f.Operator, "? AND ?", ")")
- params = append(params, valueFixer(values[0]), valueFixer(values[1]))
- }
- case "IN", "NOT IN":
- if values, ok := f.Value.([]interface{}); ok {
- wheres = append(wheres, fname, f.Operator, "?")
- params = append(params, valueFixer(values))
- }
- case "LIKE", "NOT LIKE", "ILIKE", "NOT ILIKE":
- if config.FieldWrapper != "" {
- fname = fmt.Sprintf(config.FieldWrapper, fname)
- }
- wheres = append(wheres, fname, f.Operator, "?")
- if f.ValueSuffix != "" {
- wheres = append(wheres, f.ValueSuffix)
- }
- value, isStrValue := f.Value.(string)
- if isStrValue {
- if config.ValueWrapper != "" {
- value = fmt.Sprintf(config.ValueWrapper, value)
- } else {
- value = strings.ToLower(value)
- }
- params = append(params, value)
- } else {
- params = append(params, f.Value)
- }
- default:
- wheres = append(wheres, fname, f.Operator, "?")
- params = append(params, valueFixer(f.Value))
- }
- }
- }
-
- return wheres, params
-}
-
-func valueFixer(n interface{}) interface{} {
- var values []interface{}
- if rawValues, ok := n.([]interface{}); ok {
- for i := range rawValues {
- values = append(values, valueFixer(rawValues[i]))
- }
-
- return values
- }
- if nil != n && reflect.TypeOf(n).Name() == "float64" {
- strValue := fmt.Sprintf("%v", n)
- if match, e := regexp.Match(`^[0-9]+$`, []byte(strValue)); nil == e && match {
- v, err := strconv.ParseInt(strValue, 10, 64)
- if nil == err {
- return v
- }
- }
- }
-
- return n
-}
-
-func contains(source []string, value string) bool {
- found := false
- for i := range source {
- if source[i] == value {
- found = true
- break
- }
- }
-
- return found
-}
-
-func FieldAs(tableName, colName string) string {
- return fmt.Sprintf("%s_%s", strings.ToLower(tableName), strings.ToLower(colName))
-}
-
-func GetLowerColNameFromAlias(alias, tableName string) string {
- return strings.TrimPrefix(alias, strings.ToLower(tableName)+"_")
-}
-
-func fieldName(field string) string {
- if strings.HasPrefix(field, `"`) && strings.HasSuffix(field, `"`) {
- return field
- }
- return `"` + strings.ToLower(field) + `"`
-}
-
-// Config for customize pagination result
-type Config struct {
- Operator string
- FieldWrapper string
- ValueWrapper string
- DefaultSize int64
- SmartSearch bool
- Statement *gorm.Statement `json:"-"`
- CustomParamEnabled bool
- SortParams []string
- PageParams []string
- OrderParams []string
- SizeParams []string
- FilterParams []string
- FieldsParams []string
- FieldSelectorEnabled bool
- CacheAdapter *gocache.AdapterInterface `json:"-"`
- JSONMarshal func(v interface{}) ([]byte, error) `json:"-"`
- JSONUnmarshal func(data []byte, v interface{}) error `json:"-"`
-}
-
-// pageFilters struct
-type pageFilters struct {
- Column string
- Operator string
- Value interface{}
- ValuePrefix string
- ValueSuffix string
- Single bool
- IsOperator bool
- Fields []string
-}
-
-// Page result wrapper
-type Page struct {
- Items []interface{} `json:"items"`
- Page int64 `json:"page"`
- Size int64 `json:"size"`
- MaxPage int64 `json:"max_page"`
- TotalPages int64 `json:"total_pages"`
- Total int64 `json:"total"`
- Last bool `json:"last"`
- First bool `json:"first"`
- Visible int64 `json:"visible"`
-}
-
-type IParameter interface {
- GetPage() int64
- GetSize() int64
- GetSort() string
- GetOrder() string
- GetFields() string
- GetFilters() interface{}
- IParameterInstance()
-}
-
-// query struct
-type requestQuery struct {
- WhereString string
- Wheres []string
- Params []interface{}
- Sorts []sortOrder
- Limit int64
- Offset int64
-}
-
-// pageRequest struct
-type pageRequest struct {
- Size int64
- Page int64
- Sorts []sortOrder
- Filters pageFilters
- Config Config `json:"-"`
- Fields []string
-}
-
-// sortOrder struct
-type sortOrder struct {
- Column string
- Direction string
-}
-
-func createCacheKey(cachePrefix string, pr pageRequest) string {
- key := ""
- if bte, err := pr.Config.JSONMarshal(pr); nil == err && cachePrefix != "" {
- key = fmt.Sprintf("%s%x", cachePrefix, md5.Sum(bte))
- }
-
- return key
-}
-
-func defaultConfig(c *Config) *Config {
- if nil == c {
- return &Config{
- JSONMarshal: json.Marshal,
- JSONUnmarshal: json.Unmarshal,
- }
- }
-
- if nil == c.JSONMarshal {
- c.JSONMarshal = json.Marshal
- }
-
- if nil == c.JSONUnmarshal {
- c.JSONUnmarshal = json.Unmarshal
- }
-
- return c
-}
diff --git a/toolkit/pagination/gorm/paginate_test.go b/toolkit/pagination/gorm/paginate_test.go
deleted file mode 100644
index 801f887b..00000000
--- a/toolkit/pagination/gorm/paginate_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package gorm
-
-import (
- "fmt"
- "testing"
-
- "github.com/DATA-DOG/go-sqlmock"
- "github.com/wubin1989/gorm"
- "github.com/wubin1989/postgres"
-)
-
-type Parameter struct {
- Page int64 `json:"page" form:"page"`
- Size int64 `json:"size" form:"size"`
- Sort string `json:"sort" form:"sort"`
- Order string `json:"order" form:"order"`
- Fields string `json:"fields" form:"fields"`
- Filters string `json:"filters" form:"filters"`
-}
-
-func (receiver Parameter) GetPage() int64 {
- return receiver.Page
-}
-
-func (receiver Parameter) GetSize() int64 {
- return receiver.Size
-}
-
-func (receiver Parameter) GetSort() string {
- return receiver.Sort
-}
-
-func (receiver Parameter) GetOrder() string {
- return receiver.Order
-}
-
-func (receiver Parameter) GetFields() string {
- return receiver.Fields
-}
-
-func (receiver Parameter) GetFilters() interface{} {
- return receiver.Filters
-}
-
-func (receiver Parameter) IParameterInstance() {
-
-}
-
-func Test_resContext_BuildWhereClause(t *testing.T) {
- pg := New(&Config{
- FieldSelectorEnabled: true,
- })
- filters := make([][]interface{}, 0)
- filters = append(filters, []interface{}{
- "sys_role_id",
- "=",
- 123,
- })
- filters = append(filters, []interface{}{
- "and",
- })
- filters = append(filters, []interface{}{
- "deleted_at",
- "is",
- "null",
- })
- filterContent, _ := json.Marshal(filters)
- mockDB, _, _ := sqlmock.New()
- dialector := postgres.New(postgres.Config{
- Conn: mockDB,
- DriverName: "postgres",
- })
- db, _ := gorm.Open(dialector, &gorm.Config{})
- resCxt := pg.With(db).Request(Parameter{
- Page: 0,
- Size: 0,
- Sort: "",
- Order: "",
- Fields: "",
- Filters: string(filterContent),
- })
- statement, args := resCxt.BuildWhereClause()
- if resCxt.Error() != nil {
- panic(resCxt.Error())
- }
- fmt.Println(statement)
- fmt.Println(args)
-}
diff --git a/toolkit/pathutils/pathutils.go b/toolkit/pathutils/pathutils.go
deleted file mode 100644
index ab851e75..00000000
--- a/toolkit/pathutils/pathutils.go
+++ /dev/null
@@ -1,34 +0,0 @@
-package pathutils
-
-import (
- "os"
- "path/filepath"
- "runtime"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-)
-
-// Abs converts relative path to absolute path
-func Abs(rel string) string {
- _, fileName, _, _ := runtime.Caller(1)
- return filepath.Join(filepath.Dir(fileName), rel)
-}
-
-// FixPath fixes path
-func FixPath(dir string, fallback string) (string, error) {
- var (
- wd string
- ret string
- )
- if stringutils.IsEmpty(dir) {
- wd, _ = os.Getwd()
- ret = filepath.Join(wd, fallback)
- return ret, nil
- }
- if !filepath.IsAbs(dir) {
- wd, _ = os.Getwd()
- ret = filepath.Join(wd, dir)
- return ret, nil
- }
- return dir, nil
-}
diff --git a/toolkit/pathutils/pathutils_test.go b/toolkit/pathutils/pathutils_test.go
deleted file mode 100644
index f3aaf2f3..00000000
--- a/toolkit/pathutils/pathutils_test.go
+++ /dev/null
@@ -1,82 +0,0 @@
-package pathutils
-
-import (
- "strings"
- "testing"
-)
-
-func TestAbs(t *testing.T) {
- type args struct {
- rel string
- }
- tests := []struct {
- name string
- args args
- want string
- }{
- {
- name: "",
- args: args{
- rel: "testfiles",
- },
- want: "",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := Abs(tt.args.rel); len(got) == 0 {
- t.Errorf("Abs() got nothing")
- }
- })
- }
-}
-
-func TestFixPath(t *testing.T) {
- type args struct {
- dir string
- fallback string
- }
- tests := []struct {
- name string
- args args
- want string
- wantErr bool
- }{
- {
- args: args{
- dir: "testfiles",
- fallback: "fallback",
- },
- want: "",
- wantErr: false,
- },
- {
- args: args{
- dir: "",
- fallback: "fallback",
- },
- want: "fallback",
- wantErr: false,
- },
- {
- args: args{
- dir: "/absolute/path",
- fallback: "",
- },
- want: "/absolute/path",
- wantErr: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := FixPath(tt.args.dir, tt.args.fallback)
- if (err != nil) != tt.wantErr {
- t.Errorf("FixPath() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !strings.HasSuffix(got, tt.want) {
- t.Errorf("FixPath() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
diff --git a/toolkit/pipeconn/pipe_listener.go b/toolkit/pipeconn/pipe_listener.go
deleted file mode 100644
index a9af0374..00000000
--- a/toolkit/pipeconn/pipe_listener.go
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright (c) 2020 StackRox Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-package pipeconn
-
-import (
- "context"
- "errors"
- "net"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/concurrency"
-)
-
-const (
- // Network is the network reported by a pipe's address.
- Network = "pipe"
-)
-
-var (
- // ErrClosed indicates that a call to Accept() failed because the listener was closed
- ErrClosed = errors.New("listener was closed")
-
- // ErrAlreadyClosed indicates that a call to Close() failed because the listener had already been closed.
- ErrAlreadyClosed = errors.New("already closed")
-
- pipeAddr = func() net.Addr {
- c1, c2 := net.Pipe()
- addr := c1.RemoteAddr()
- _ = c1.Close()
- _ = c2.Close()
- return addr
- }()
-)
-
-// DialContextFunc is a function for dialing a pipe listener.
-type DialContextFunc func(context.Context) (net.Conn, error)
-
-type pipeListener struct {
- closed concurrency.Signal
- serverConnsC chan net.Conn
-}
-
-// NewPipeListener returns a net.Listener that accepts connections which are local pipe connections (i.e., via
-// net.Pipe()). It also returns a function that implements a context-aware dial.
-func NewPipeListener() (net.Listener, DialContextFunc) {
- lis := &pipeListener{
- closed: concurrency.NewSignal(),
- serverConnsC: make(chan net.Conn),
- }
-
- return lis, lis.DialContext
-}
-
-func (l *pipeListener) Accept() (net.Conn, error) {
- if l.closed.IsDone() {
- return nil, ErrClosed
- }
- select {
- case conn := <-l.serverConnsC:
- return conn, nil
- case <-l.closed.Done():
- return nil, ErrClosed
- }
-}
-
-func (l *pipeListener) DialContext(ctx context.Context) (net.Conn, error) {
- if l.closed.IsDone() {
- return nil, ErrClosed
- }
-
- serverConn, clientConn := net.Pipe()
-
- select {
- case l.serverConnsC <- serverConn:
- return clientConn, nil
- case <-l.closed.Done():
- return nil, ErrClosed
- case <-ctx.Done():
- return nil, ctx.Err()
- }
-}
-
-func (l *pipeListener) Addr() net.Addr {
- return pipeAddr
-}
-
-func (l *pipeListener) Close() error {
- if !l.closed.Signal() {
- return ErrAlreadyClosed
- }
- return nil
-}
diff --git a/toolkit/pipeconn/pipe_listener_test.go b/toolkit/pipeconn/pipe_listener_test.go
deleted file mode 100644
index ad94a952..00000000
--- a/toolkit/pipeconn/pipe_listener_test.go
+++ /dev/null
@@ -1,126 +0,0 @@
-// Copyright (c) 2020 StackRox Inc.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License
-
-package pipeconn
-
-import (
- "context"
- "encoding/binary"
- "io"
- "net"
- "sync"
- "sync/atomic"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/stretchr/testify/require"
-)
-
-func u32BE(v uint32) []byte {
- var enc [4]byte
- binary.BigEndian.PutUint32(enc[:], v)
- return enc[:]
-}
-
-func TestNetwork(t *testing.T) {
- t.Parallel()
-
- assert.Equal(t, Network, pipeAddr.Network())
-}
-
-func TestPipeListener_Connections(t *testing.T) {
- t.Parallel()
-
- lis, dialCtx := NewPipeListener()
-
- var clientSum uint32
- var wg sync.WaitGroup
- for i := 0; i < 10; i++ {
- wg.Add(1)
- go func(idx int) {
- defer wg.Done()
-
- conn, err := dialCtx(context.Background())
- require.NoError(t, err)
-
- sum := uint32(idx)
- _, err = conn.Write(u32BE(sum))
- assert.NoError(t, err)
-
- var buf [4]byte
- _, err = io.ReadFull(conn, buf[:])
- assert.NoError(t, err)
- sum += binary.BigEndian.Uint32(buf[:])
-
- _, err = conn.Write(u32BE(sum))
- assert.NoError(t, err)
-
- assert.NoError(t, conn.Close())
-
- atomic.AddUint32(&clientSum, sum)
- }(i)
- }
-
- var serverSum uint32
- for i := 0; i < 10; i++ {
- conn, err := lis.Accept()
- require.NoError(t, err)
-
- wg.Add(1)
- go func(idx int, conn net.Conn) {
- defer wg.Done()
-
- sum := uint32(idx)
-
- var buf [4]byte
- _, err := io.ReadFull(conn, buf[:])
- assert.NoError(t, err)
- sum += binary.BigEndian.Uint32(buf[:])
-
- _, err = conn.Write(u32BE(uint32(idx)))
- assert.NoError(t, err)
-
- _, err = io.ReadFull(conn, buf[:])
- assert.NoError(t, err)
- assert.Equal(t, sum, binary.BigEndian.Uint32(buf[:]))
-
- atomic.AddUint32(&serverSum, sum)
-
- n, err := io.ReadFull(conn, buf[:])
- assert.Zero(t, n)
- assert.Equal(t, io.EOF, err)
- }(i, conn)
- }
-
- wg.Wait()
- assert.Equal(t, serverSum, clientSum)
-}
-
-func TestPipeListener_Close(t *testing.T) {
- t.Parallel()
-
- lis, dialCtx := NewPipeListener()
-
- assert.NoError(t, lis.Close())
-
- conn, err := lis.Accept()
- assert.Nil(t, conn)
- assert.Equal(t, ErrClosed, err)
-
- conn, err = dialCtx(context.Background())
- assert.Nil(t, conn)
- assert.Equal(t, ErrClosed, err)
-
- assert.Equal(t, ErrAlreadyClosed, lis.Close())
-}
diff --git a/toolkit/protobuf/v3/message.go b/toolkit/protobuf/v3/message.go
deleted file mode 100644
index b0ab7520..00000000
--- a/toolkit/protobuf/v3/message.go
+++ /dev/null
@@ -1,364 +0,0 @@
-package v3
-
-import (
- "encoding/json"
- "fmt"
- "github.com/goccy/go-reflect"
- "github.com/lithammer/shortuuid/v4"
- log "github.com/sirupsen/logrus"
- "regexp"
- "strings"
- "unicode"
-
- "github.com/iancoleman/strcase"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sliceutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-)
-
-var re = regexp.MustCompile(`json:"(.*?)"`)
-
-var _ ProtobufType = (*Enum)(nil)
-var _ ProtobufType = (*Message)(nil)
-
-type ProtobufType interface {
- GetName() string
- String() string
- Inner() bool
-}
-
-var MessageStore = make(map[string]Message)
-
-var EnumStore = make(map[string]Enum)
-
-var ImportStore = make(map[string]struct{})
-
-var MessageNames []string
-
-type EnumField struct {
- Name string
- Number int
-}
-
-func (receiver ProtoGenerator) newEnumField(field string, index int) EnumField {
- return EnumField{
- Name: strings.ToUpper(strcase.ToSnake(field)),
- Number: index,
- }
-}
-
-type Enum struct {
- Name string
- Fields []EnumField
-}
-
-func (e Enum) Inner() bool {
- return false
-}
-
-func (e Enum) String() string {
- return e.Name
-}
-
-func (e Enum) GetName() string {
- return e.Name
-}
-
-func (receiver ProtoGenerator) NewEnum(enumMeta astutils.EnumMeta) Enum {
- var fields []EnumField
- for i, field := range enumMeta.Values {
- fields = append(fields, receiver.newEnumField(field, i))
- }
- return Enum{
- Name: strcase.ToCamel(enumMeta.Name),
- Fields: fields,
- }
-}
-
-// Message represents protobuf message definition
-type Message struct {
- Name string
- Fields []Field
- Comments []string
- IsInner bool
- IsScalar bool
- IsMap bool
- IsRepeated bool
- IsTopLevel bool
- // IsImported denotes the message will be imported from third-party, such as from google/protobuf
- IsImported bool
-}
-
-func (m Message) Inner() bool {
- return m.IsInner
-}
-
-func (m Message) GetName() string {
- return m.Name
-}
-
-func (m Message) String() string {
- switch {
- case reflect.DeepEqual(m, Any):
- return "anypb.Any"
- case reflect.DeepEqual(m, Struct):
- return "structpb.Struct"
- case reflect.DeepEqual(m, Value):
- return "structpb.Value"
- case reflect.DeepEqual(m, ListValue):
- return "structpb.ListValue"
- case reflect.DeepEqual(m, Empty):
- return "emptypb.Empty"
- default:
- return m.Name
- }
-}
-
-// NewMessage returns message instance from astutils.StructMeta
-func (receiver ProtoGenerator) NewMessage(structmeta astutils.StructMeta) Message {
- var fields []Field
- for i, field := range structmeta.Fields {
- fields = append(fields, receiver.newField(field, i+1))
- }
- return Message{
- Name: strcase.ToCamel(structmeta.Name),
- Fields: fields,
- Comments: structmeta.Comments,
- IsTopLevel: true,
- }
-}
-
-// Field represents protobuf message field definition
-type Field struct {
- Name string
- Type ProtobufType
- Number int
- Comments []string
- JsonName string
-}
-
-func (receiver ProtoGenerator) newField(field astutils.FieldMeta, index int) Field {
- t := receiver.MessageOf(field.Type)
- if t.Inner() {
- message := t.(Message)
- message.Name = strcase.ToCamel(field.Name)
- t = message
- }
- var fieldName string
- if stringutils.IsNotEmpty(field.Tag) && re.MatchString(field.Tag) {
- jsonName := re.FindStringSubmatch(field.Tag)[1]
- fieldName = strings.Split(jsonName, ",")[0]
- if fieldName == "-" {
- fieldName = receiver.fieldNamingFunc(field.Name)
- }
- } else {
- fieldName = receiver.fieldNamingFunc(field.Name)
- }
- return Field{
- Name: fieldName,
- Type: t,
- Number: index,
- Comments: field.Comments,
- JsonName: fieldName,
- }
-}
-
-var (
- Double = Message{
- Name: "double",
- IsScalar: true,
- }
- Float = Message{
- Name: "float",
- IsScalar: true,
- }
- Int32 = Message{
- Name: "int32",
- IsScalar: true,
- }
- Int64 = Message{
- Name: "int64",
- IsScalar: true,
- }
- Uint32 = Message{
- Name: "uint32",
- IsScalar: true,
- }
- Uint64 = Message{
- Name: "uint64",
- IsScalar: true,
- }
- Bool = Message{
- Name: "bool",
- IsScalar: true,
- }
- String = Message{
- Name: "string",
- IsScalar: true,
- }
- Bytes = Message{
- Name: "bytes",
- IsScalar: true,
- }
- Any = Message{
- Name: "google.protobuf.Any",
- IsTopLevel: true,
- IsImported: true,
- }
- Struct = Message{
- Name: "google.protobuf.Struct",
- IsTopLevel: true,
- IsImported: true,
- }
- Value = Message{
- Name: "google.protobuf.Value",
- IsTopLevel: true,
- IsImported: true,
- }
- ListValue = Message{
- Name: "google.protobuf.ListValue",
- IsTopLevel: true,
- IsImported: true,
- }
- Empty = Message{
- Name: "google.protobuf.Empty",
- IsTopLevel: true,
- IsImported: true,
- }
- Time = Message{
- Name: "google.protobuf.Timestamp",
- IsScalar: true,
- }
-)
-
-func (receiver ProtoGenerator) MessageOf(ft string) ProtobufType {
- if astutils.IsVarargs(ft) {
- ft = astutils.ToSlice(ft)
- }
- ft = strings.TrimLeft(ft, "*")
- switch ft {
- case "int", "int8", "int16", "int32", "byte", "rune", "complex64", "complex128":
- return Int32
- case "uint", "uint8", "uint16", "uint32":
- return Uint32
- case "int64":
- return Int64
- case "uint64", "uintptr":
- return Uint64
- case "bool":
- return Bool
- case "string", "error", "[]rune", "decimal.Decimal":
- return String
- case "[]byte", "v3.FileModel", "os.File":
- return Bytes
- case "float32":
- return Float
- case "float64":
- return Double
- case "time.Time", "gorm.DeletedAt", "customtypes.Time":
- //ImportStore["google/protobuf/timestamp.proto"] = struct{}{}
- //return Time
- return String
- default:
- return receiver.handleDefaultCase(ft)
- }
-}
-
-var anonystructre *regexp.Regexp
-
-func init() {
- anonystructre = regexp.MustCompile(`anonystruct«(.*)»`)
-}
-
-func (receiver ProtoGenerator) handleDefaultCase(ft string) ProtobufType {
- var title string
- if ft == "map[string]interface{}" {
- ImportStore["google/protobuf/struct.proto"] = struct{}{}
- return Struct
- }
- if ft == "[]interface{}" {
- ImportStore["google/protobuf/struct.proto"] = struct{}{}
- return ListValue
- }
- if strings.HasPrefix(ft, "map[") {
- elem := ft[strings.Index(ft, "]")+1:]
- key := ft[4:strings.Index(ft, "]")]
- keyMessage := receiver.MessageOf(key)
- if reflect.DeepEqual(keyMessage, Float) || reflect.DeepEqual(keyMessage, Double) || reflect.DeepEqual(keyMessage, Bytes) {
- log.Error("floating point types and bytes cannot be key_type of maps, please refer to https://developers.google.com/protocol-buffers/docs/proto3#maps")
- goto ANY
- }
- elemMessage := receiver.MessageOf(elem)
- if strings.HasPrefix(elemMessage.GetName(), "map<") {
- log.Error("the value_type cannot be another map, please refer to https://developers.google.com/protocol-buffers/docs/proto3#maps")
- goto ANY
- }
- return Message{
- Name: fmt.Sprintf("map<%s, %s>", keyMessage.GetName(), elemMessage.GetName()),
- IsMap: true,
- }
- }
- if strings.HasPrefix(ft, "[") {
- elem := ft[strings.Index(ft, "]")+1:]
- elemMessage := receiver.MessageOf(elem)
- if strings.HasPrefix(elemMessage.GetName(), "map<") {
- log.Error("map fields cannot be repeated, please refer to https://developers.google.com/protocol-buffers/docs/proto3#maps")
- goto ANY
- }
- messageName := elemMessage.GetName()
- if strings.Contains(elemMessage.GetName(), "repeated ") {
- messageName = messageName[strings.LastIndex(messageName, ".")+1:]
- messageName = "Nested" + strcase.ToCamel(messageName)
- fieldName := receiver.fieldNamingFunc(messageName)
- MessageStore[messageName] = Message{
- Name: messageName,
- Fields: []Field{
- {
- Name: fieldName,
- Type: elemMessage,
- Number: 1,
- JsonName: fieldName,
- },
- },
- IsInner: true,
- }
- }
- return Message{
- Name: fmt.Sprintf("repeated %s", messageName),
- IsRepeated: true,
- }
- }
- if anonystructre.MatchString(ft) {
- result := anonystructre.FindStringSubmatch(ft)
- var structmeta astutils.StructMeta
- json.Unmarshal([]byte(result[1]), &structmeta)
- message := receiver.NewMessage(structmeta)
- message.IsInner = true
- message.IsTopLevel = false
- message.Name = "Anonystruct" + shortuuid.NewWithNamespace(result[1])
- MessageStore[message.Name] = message
- return message
- }
- if !strings.Contains(ft, ".") {
- title = ft
- }
- if stringutils.IsEmpty(title) {
- title = ft[strings.LastIndex(ft, ".")+1:]
- }
- if stringutils.IsNotEmpty(title) {
- if unicode.IsUpper(rune(title[0])) {
- if sliceutils.StringContains(MessageNames, title) {
- return Message{
- Name: strcase.ToCamel(title),
- IsTopLevel: true,
- }
- }
- }
- if e, ok := EnumStore[title]; ok {
- return e
- }
- }
-ANY:
- ImportStore["google/protobuf/struct.proto"] = struct{}{}
- return Value
-}
diff --git a/toolkit/protobuf/v3/service.go b/toolkit/protobuf/v3/service.go
deleted file mode 100644
index bb52a9f1..00000000
--- a/toolkit/protobuf/v3/service.go
+++ /dev/null
@@ -1,210 +0,0 @@
-package v3
-
-import (
- "fmt"
- "github.com/goccy/go-reflect"
- "github.com/pkg/errors"
- "github.com/samber/lo"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/executils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- "strings"
- "time"
-
- "github.com/iancoleman/strcase"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/astutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
- "github.com/unionj-cloud/go-doudou/v2/version"
-)
-
-type ProtoGenerator struct {
- fieldNamingFunc func(string) string
- Structs []astutils.StructMeta
- annotatedOnly bool
- protocCmd string
-}
-
-type ProtoGeneratorOption func(*ProtoGenerator)
-
-func WithProtocCmd(cmd string) ProtoGeneratorOption {
- return func(p *ProtoGenerator) {
- p.protocCmd = cmd
- }
-}
-
-func WithFieldNamingFunc(fn func(string) string) ProtoGeneratorOption {
- return func(p *ProtoGenerator) {
- p.fieldNamingFunc = fn
- }
-}
-
-func WithAnnotatedOnly(annotatedOnly bool) ProtoGeneratorOption {
- return func(p *ProtoGenerator) {
- p.annotatedOnly = annotatedOnly
- }
-}
-
-func NewProtoGenerator(options ...ProtoGeneratorOption) ProtoGenerator {
- var p ProtoGenerator
- p.Structs = make([]astutils.StructMeta, 0)
- for _, opt := range options {
- opt(&p)
- }
- return p
-}
-
-const Syntax = "proto3"
-
-type Service struct {
- Name string
- Package string
- GoPackage string
- Syntax string
- // go-doudou version
- Version string
- ProtoVer string
- Rpcs []Rpc
- Messages []Message
- Enums []Enum
- Comments []string
- Imports []string
-}
-
-func (receiver ProtoGenerator) NewService(name, goPackage string) Service {
- return Service{
- Name: strcase.ToCamel(name) + "Service",
- Package: strcase.ToSnake(name),
- GoPackage: goPackage,
- Syntax: Syntax,
- Version: version.Release,
- ProtoVer: fmt.Sprintf("v%s", time.Now().Local().Format(constants.FORMAT10)),
- }
-}
-
-type StreamType int
-
-const (
- biStream = iota + 1
- clientStream
- serverStream
-)
-
-type Rpc struct {
- Name string
- Request Message
- Response Message
- Comments []string
- StreamType StreamType
-}
-
-func (receiver ProtoGenerator) NewRpc(method astutils.MethodMeta) *Rpc {
- if receiver.annotatedOnly {
- if lo.CountBy(method.Comments, func(item string) bool {
- return strings.Contains(item, "@grpc")
- }) == 0 {
- return nil
- }
- }
- rpcName := strcase.ToCamel(method.Name) + "Rpc"
- rpcRequest := receiver.newRequest(rpcName, method.Params)
- if reflect.DeepEqual(rpcRequest, Empty) {
- ImportStore["google/protobuf/empty.proto"] = struct{}{}
- }
- if !strings.HasPrefix(rpcRequest.Name, "stream ") && !rpcRequest.IsImported {
- if _, ok := MessageStore[rpcRequest.Name]; !ok {
- MessageStore[rpcRequest.Name] = rpcRequest
- }
- }
- rpcResponse := receiver.newResponse(rpcName, method.Results)
- if reflect.DeepEqual(rpcResponse, Empty) {
- ImportStore["google/protobuf/empty.proto"] = struct{}{}
- }
- if !strings.HasPrefix(rpcResponse.Name, "stream ") && !rpcResponse.IsImported {
- if _, ok := MessageStore[rpcResponse.Name]; !ok {
- MessageStore[rpcResponse.Name] = rpcResponse
- }
- }
- var st StreamType
- if strings.HasPrefix(rpcRequest.Name, "stream ") && strings.HasPrefix(rpcResponse.Name, "stream ") {
- st = biStream
- } else if strings.HasPrefix(rpcRequest.Name, "stream ") {
- st = clientStream
- } else if strings.HasPrefix(rpcResponse.Name, "stream ") {
- st = serverStream
- }
- return &Rpc{
- Name: rpcName,
- Request: rpcRequest,
- Response: rpcResponse,
- Comments: method.Comments,
- StreamType: st,
- }
-}
-
-func (receiver ProtoGenerator) newRequest(rpcName string, params []astutils.FieldMeta) Message {
- if len(params) == 0 {
- return Empty
- }
- if len(params) == 1 && params[0].Type == "context.Context" {
- return Empty
- }
- if params[0].Type == "context.Context" {
- params = params[1:]
- }
- if len(params) == 1 {
- if m, ok := receiver.MessageOf(params[0].Type).(Message); ok && m.IsTopLevel {
- if strings.HasPrefix(params[0].Name, "stream") {
- m.Name = "stream " + m.Name
- }
- return m
- }
- }
- var fields []Field
- for i, field := range params {
- fields = append(fields, receiver.newField(field, i+1))
- }
- return Message{
- Name: strcase.ToCamel(rpcName + "Request"),
- Fields: fields,
- IsTopLevel: true,
- }
-}
-
-func (receiver ProtoGenerator) newResponse(rpcName string, params []astutils.FieldMeta) Message {
- if len(params) == 0 {
- return Empty
- }
- if len(params) == 1 && params[0].Type == "error" {
- return Empty
- }
- if params[len(params)-1].Type == "error" {
- params = params[:len(params)-1]
- }
- if len(params) == 1 {
- if m, ok := receiver.MessageOf(params[0].Type).(Message); ok && m.IsTopLevel {
- if strings.HasPrefix(params[0].Name, "stream") {
- m.Name = "stream " + m.Name
- }
- return m
- }
- }
- var fields []Field
- for i, field := range params {
- fields = append(fields, receiver.newField(field, i+1))
- }
- return Message{
- Name: strcase.ToCamel(rpcName + "Response"),
- Fields: fields,
- IsTopLevel: true,
- }
-}
-
-// Generate depends on protoc-gen-go-json plugin, so run go install github.com/mfridman/protoc-gen-go-json@v1.4.0 first.
-func (receiver ProtoGenerator) Generate(protoFile string, runner executils.Runner) error {
- if stringutils.IsEmpty(receiver.protocCmd) {
- return errors.New("Command cannot be empty")
- }
- // default: protoc --proto_path=. --go_out=. --go_opt=paths=source_relative --go-grpc_out=. --go-grpc_opt=paths=source_relative --go-json_out=. --go-json_opt=paths=source_relative transport/grpc/usersvc.proto
- args := strings.Split(receiver.protocCmd, " ")
- args = append(args, protoFile)
- return runner.Run(args[0], args[1:]...)
-}
diff --git a/toolkit/random/rand.go b/toolkit/random/rand.go
deleted file mode 100644
index 12fc7113..00000000
--- a/toolkit/random/rand.go
+++ /dev/null
@@ -1,12 +0,0 @@
-package random
-
-import (
- "math/rand"
- "time"
-)
-
-// RandInt generates random int between min and max
-func RandInt(min int, max int) int {
- r := rand.New(rand.NewSource(time.Now().UnixNano()))
- return r.Intn(max-min) + min
-}
diff --git a/toolkit/reflectutils/reflectutils.go b/toolkit/reflectutils/reflectutils.go
deleted file mode 100644
index 1c439d4f..00000000
--- a/toolkit/reflectutils/reflectutils.go
+++ /dev/null
@@ -1,16 +0,0 @@
-package reflectutils
-
-import "github.com/goccy/go-reflect"
-
-// ValueOf returns underlying value of interface data
-func ValueOf(data interface{}) reflect.Value {
- return ValueOfValue(reflect.ValueOf(data))
-}
-
-// ValueOfValue returns underlying value of reflect.Value data
-func ValueOfValue(data reflect.Value) reflect.Value {
- if data.Kind() == reflect.Ptr {
- return data.Elem()
- }
- return data
-}
diff --git a/toolkit/sliceutils/sliceutils.go b/toolkit/sliceutils/sliceutils.go
deleted file mode 100644
index 53ccdacd..00000000
--- a/toolkit/sliceutils/sliceutils.go
+++ /dev/null
@@ -1,139 +0,0 @@
-package sliceutils
-
-import (
- "github.com/goccy/go-reflect"
-
- "github.com/pkg/errors"
-)
-
-// StringSlice2InterfaceSlice converts string slice to interface slice
-func StringSlice2InterfaceSlice(strSlice []string) []interface{} {
- ret := make([]interface{}, len(strSlice))
- for i, v := range strSlice {
- ret[i] = v
- }
- return ret
-}
-
-// InterfaceSlice2StringSlice converts interface slice to string slice
-func InterfaceSlice2StringSlice(strSlice []interface{}) []string {
- ret := make([]string, len(strSlice))
- for i, v := range strSlice {
- ret[i] = v.(string)
- }
- return ret
-}
-
-// Contains asserts src contains test
-func Contains(src []interface{}, test interface{}) bool {
- for _, item := range src {
- if item == test {
- return true
- }
- }
- return false
-}
-
-// ContainsDeep asserts src contains test using reflect.DeepEqual
-func ContainsDeep(src []interface{}, test interface{}) bool {
- for _, item := range src {
- if reflect.DeepEqual(item, test) {
- return true
- }
- }
- return false
-}
-
-// StringContains asserts src contains test
-func StringContains(src []string, test string) bool {
- for _, item := range src {
- if item == test {
- return true
- }
- }
- return false
-}
-
-// StringFilter filters string slice by callback function fn
-// If fn returns true, the item will be appended to result
-func StringFilter(src []string, fn func(item string) bool) []string {
- var ret []string
- for _, item := range src {
- if fn(item) {
- ret = append(ret, item)
- }
- }
- return ret
-}
-
-// IndexOf returns index of element in string slice data
-func IndexOf(element string, data []string) int {
- for k, v := range data {
- if element == v {
- return k
- }
- }
- return -1 //not found.
-}
-
-// IndexOfAny returns index of element in slice data
-func IndexOfAny(target interface{}, anySlice interface{}) (int, error) {
- if reflect.TypeOf(anySlice).Kind() != reflect.Slice {
- return -1, errors.New("not slice")
- }
- data := reflect.ValueOf(anySlice)
- for i := 0; i < data.Len(); i++ {
- elem := data.Index(i)
- if elem.Interface() == target {
- return i, nil
- }
- }
- return -1, nil //not found.
-}
-
-// IsEmpty assert src is an empty slice
-func IsEmpty(src interface{}) bool {
- if slice, ok := TakeSliceArg(src); ok {
- return slice == nil || len(slice) == 0
- }
- panic("not slice")
-}
-
-// TakeSliceArg https://ahmet.im/blog/golang-take-slices-of-any-type-as-input-parameter/
-func TakeSliceArg(arg interface{}) (out []interface{}, ok bool) {
- slice, success := takeArg(arg, reflect.Slice)
- if !success {
- ok = false
- return
- }
- c := slice.Len()
- out = make([]interface{}, c)
- for i := 0; i < c; i++ {
- out[i] = slice.Index(i).Interface()
- }
- return out, true
-}
-
-func takeArg(arg interface{}, kind reflect.Kind) (val reflect.Value, ok bool) {
- val = reflect.ValueOf(arg)
- if val.Kind() == kind {
- ok = true
- }
- return
-}
-
-// ConvertAny2Interface converts interface src to interface slice
-func ConvertAny2Interface(src interface{}) ([]interface{}, error) {
- data := reflect.ValueOf(src)
- if data.Type().Kind() == reflect.Ptr {
- data = data.Elem()
- }
- if data.Type().Kind() != reflect.Slice {
- return nil, errors.New("Src not slice")
- }
- ret := make([]interface{}, data.Len())
- for i := 0; i < data.Len(); i++ {
- ret[i] = data.Index(i).Interface()
- }
- return ret, nil
-}
diff --git a/toolkit/sliceutils/sliceutils_test.go b/toolkit/sliceutils/sliceutils_test.go
deleted file mode 100644
index 74ecde77..00000000
--- a/toolkit/sliceutils/sliceutils_test.go
+++ /dev/null
@@ -1,471 +0,0 @@
-package sliceutils
-
-import (
- "github.com/goccy/go-reflect"
- "testing"
-
- "github.com/stretchr/testify/assert"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
-)
-
-func TestContains(t *testing.T) {
- type args struct {
- src []interface{}
- test interface{}
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {
- name: "1",
- args: args{
- src: []interface{}{"a", "2", "c"},
- test: "2",
- },
- want: true,
- },
- {
- name: "1",
- args: args{
- src: []interface{}{"a", "2", "c"},
- test: "3",
- },
- want: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := Contains(tt.args.src, tt.args.test); got != tt.want {
- t.Errorf("Contains() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestInterfaceSlice2StringSlice(t *testing.T) {
- type args struct {
- strSlice []interface{}
- }
- tests := []struct {
- name string
- args args
- want []string
- }{
- {
- name: "2",
- args: args{
- strSlice: []interface{}{"a", "n"},
- },
- want: []string{"a", "n"},
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := InterfaceSlice2StringSlice(tt.args.strSlice); !reflect.DeepEqual(got, tt.want) {
- t.Errorf("InterfaceSlice2StringSlice() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestIndexOfAny(t *testing.T) {
- type args struct {
- target interface{}
- anySlice interface{}
- }
- tests := []struct {
- name string
- args args
- want int
- wantErr bool
- }{
- {
- name: "",
- args: args{
- target: "a",
- anySlice: []string{
- "b", "m", "a", "K",
- },
- },
- want: 2,
- wantErr: false,
- },
- {
- name: "",
- args: args{
- target: "a",
- anySlice: "abc",
- },
- want: -1,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := IndexOfAny(tt.args.target, tt.args.anySlice)
- if (err != nil) != tt.wantErr {
- t.Errorf("IndexOfAny() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("IndexOfAny() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestIndexOfAnyInt(t *testing.T) {
- type args struct {
- target interface{}
- anySlice interface{}
- }
- tests := []struct {
- name string
- args args
- want int
- wantErr bool
- }{
- {
- name: "4",
- args: args{
- target: 3,
- anySlice: []int{
- 2, 5, 1, 6, 3, 8, 9,
- },
- },
- want: 4,
- wantErr: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := IndexOfAny(tt.args.target, tt.args.anySlice)
- if (err != nil) != tt.wantErr {
- t.Errorf("IndexOfAny() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("IndexOfAny() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestIndexOfAnyNotContain(t *testing.T) {
- type args struct {
- target interface{}
- anySlice interface{}
- }
- tests := []struct {
- name string
- args args
- want int
- wantErr bool
- }{
- {
- name: "4",
- args: args{
- target: 3,
- anySlice: []int{
- 2, 5, 1, 6, 11, 8, 9,
- },
- },
- want: -1,
- wantErr: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := IndexOfAny(tt.args.target, tt.args.anySlice)
- if (err != nil) != tt.wantErr {
- t.Errorf("IndexOfAny() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("IndexOfAny() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestConvertAny2Interface(t *testing.T) {
- type args struct {
- src interface{}
- }
- tests := []struct {
- name string
- args args
- want []interface{}
- wantErr bool
- }{
- {
- name: "",
- args: args{
- src: []int{1, 2, 3},
- },
- want: []interface{}{1, 2, 3},
- wantErr: false,
- },
- {
- name: "",
- args: args{
- src: "abc",
- },
- want: nil,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := ConvertAny2Interface(tt.args.src)
- if (err != nil) != tt.wantErr {
- t.Errorf("ConvertAny2Interface() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if !reflect.DeepEqual(got, tt.want) {
- t.Errorf("ConvertAny2Interface() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestStringSlice2InterfaceSlice(t *testing.T) {
- type args struct {
- strSlice []string
- }
- tests := []struct {
- name string
- args args
- want []interface{}
- }{
- {
- name: "",
- args: args{
- strSlice: []string{"a", "b", "c"},
- },
- want: []interface{}{"a", "b", "c"},
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := StringSlice2InterfaceSlice(tt.args.strSlice); !reflect.DeepEqual(got, tt.want) {
- t.Errorf("StringSlice2InterfaceSlice() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-type containsDeepStruct struct {
- Name string
- Age int
-}
-
-func TestContainsDeep(t *testing.T) {
- type args struct {
- src []interface{}
- test interface{}
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {
- name: "1",
- args: args{
- src: []interface{}{
- containsDeepStruct{
- Name: "Jack",
- Age: 10,
- },
- containsDeepStruct{
- Name: "Rose",
- Age: 18,
- },
- containsDeepStruct{
- Name: "David",
- Age: 14,
- },
- },
- test: containsDeepStruct{
- Name: "David",
- Age: 14,
- },
- },
- want: true,
- },
- {
- name: "2",
- args: args{
- src: []interface{}{
- containsDeepStruct{
- Name: "Jack",
- Age: 10,
- },
- containsDeepStruct{
- Name: "Rose",
- Age: 18,
- },
- containsDeepStruct{
- Name: "David",
- Age: 14,
- },
- },
- test: containsDeepStruct{
- Name: "Lily",
- Age: 14,
- },
- },
- want: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := ContainsDeep(tt.args.src, tt.args.test); got != tt.want {
- t.Errorf("ContainsDeep() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestStringContains(t *testing.T) {
- type args struct {
- src []string
- test string
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {
- name: "1",
- args: args{
- src: []string{"a", "b", "c"},
- test: "b",
- },
- want: true,
- },
- {
- name: "2",
- args: args{
- src: []string{"a", "b", "c"},
- test: "d",
- },
- want: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := StringContains(tt.args.src, tt.args.test); got != tt.want {
- t.Errorf("StringContains() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestIndexOf(t *testing.T) {
- type args struct {
- element string
- data []string
- }
- tests := []struct {
- name string
- args args
- want int
- }{
- {
- name: "",
- args: args{
- element: "c",
- data: []string{"a", "b", "c"},
- },
- want: 2,
- },
- {
- name: "",
- args: args{
- element: "d",
- data: []string{"a", "b", "c"},
- },
- want: -1,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := IndexOf(tt.args.element, tt.args.data); got != tt.want {
- t.Errorf("IndexOf() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestIsEmpty(t *testing.T) {
- type args struct {
- src interface{}
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {
- name: "",
- args: args{
- src: []interface{}{},
- },
- want: true,
- },
- {
- name: "",
- args: args{
- src: []interface{}{1},
- },
- want: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := IsEmpty(tt.args.src); got != tt.want {
- t.Errorf("IsEmpty() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestIsEmptyP(t *testing.T) {
- assert.Panics(t, func() {
- IsEmpty(1)
- })
-}
-
-func TestStringFilter(t *testing.T) {
- type args struct {
- src []string
- fn func(item string) bool
- }
- tests := []struct {
- name string
- args args
- want []string
- }{
- {
- name: "",
- args: args{
- src: []string{"a", "2", "c"},
- fn: func(item string) bool {
- _, err := cast.ToIntE(item)
- return err == nil
- },
- },
- want: []string{"2"},
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- assert.Equalf(t, tt.want, StringFilter(tt.args.src, tt.args.fn), "StringFilter(%v, %v)", tt.args.src, tt.args.fn)
- })
- }
-}
diff --git a/toolkit/sqlext/.env b/toolkit/sqlext/.env
deleted file mode 100644
index 44d2b9d1..00000000
--- a/toolkit/sqlext/.env
+++ /dev/null
@@ -1,6 +0,0 @@
-DB_HOST=localhost
-DB_PORT=3306
-DB_USER=root
-DB_PASSWD=1234
-DB_SCHEMA=test
-DB_CHARSET=utf8mb4
\ No newline at end of file
diff --git a/toolkit/sqlext/arithsymbol/arithsymbol.go b/toolkit/sqlext/arithsymbol/arithsymbol.go
deleted file mode 100644
index f1ff5265..00000000
--- a/toolkit/sqlext/arithsymbol/arithsymbol.go
+++ /dev/null
@@ -1,27 +0,0 @@
-package arithsymbol
-
-// ArithSymbol is alias type of string for arithmetic operators
-type ArithSymbol string
-
-const (
- // Eq equal
- Eq ArithSymbol = "="
- // Ne not equal
- Ne ArithSymbol = "!="
- // Gt greater than
- Gt ArithSymbol = ">"
- // Lt less than
- Lt ArithSymbol = "<"
- // Gte greater than and equal
- Gte ArithSymbol = ">="
- // Lte less than and equal
- Lte ArithSymbol = "<="
- // Is e.g. is null
- Is ArithSymbol = "is"
- // Not e.g. is not null
- Not ArithSymbol = "is not"
- // In contained by a slice
- In ArithSymbol = "in"
- NotIn ArithSymbol = "not in"
- Like ArithSymbol = "like"
-)
diff --git a/toolkit/sqlext/logger/logger.go b/toolkit/sqlext/logger/logger.go
deleted file mode 100644
index 2b3703e9..00000000
--- a/toolkit/sqlext/logger/logger.go
+++ /dev/null
@@ -1,92 +0,0 @@
-package logger
-
-import (
- "context"
- "fmt"
- "os"
- "strings"
-
- "github.com/ascarter/requestid"
- "github.com/opentracing/opentracing-go"
- "github.com/pkg/errors"
- "github.com/rs/zerolog"
- "github.com/uber/jaeger-client-go"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cast"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/reflectutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/zlogger"
-)
-
-type SqlLogger struct {
- Logger zerolog.Logger
-}
-
-func (receiver SqlLogger) Enable() bool {
- return cast.ToBoolOrDefault(os.Getenv("GDD_SQL_LOG_ENABLE"), false)
-}
-
-func (receiver SqlLogger) Log(ctx context.Context, query string, args ...interface{}) {
- if !receiver.Enable() {
- return
- }
- receiver.LogWithErr(ctx, nil, nil, query, args...)
-}
-
-type SqlLoggerOption func(logger *SqlLogger)
-
-func WithLogger(logger zerolog.Logger) SqlLoggerOption {
- return func(sqlLogger *SqlLogger) {
- sqlLogger.Logger = logger
- }
-}
-
-func NewSqlLogger(opts ...SqlLoggerOption) SqlLogger {
- sqlLogger := SqlLogger{
- Logger: zlogger.Logger,
- }
- for _, item := range opts {
- item(&sqlLogger)
- }
- return sqlLogger
-}
-
-func PopulatedSql(query string, args ...interface{}) string {
- var sb strings.Builder
- sb.WriteString(strings.Join(strings.Fields(query), " "))
- for _, arg := range args {
- value := reflectutils.ValueOf(arg)
- if value.IsValid() {
- sb.WriteString(fmt.Sprint(value.Interface()))
- } else {
- sb.WriteString(fmt.Sprint(arg))
- }
- }
- return strings.ReplaceAll(sb.String(), "''", "null")
-}
-
-func (receiver SqlLogger) LogWithErr(ctx context.Context, err error, hit *bool, query string, args ...interface{}) {
- if !receiver.Enable() {
- return
- }
- var sb strings.Builder
- if reqId, ok := requestid.FromContext(ctx); ok && stringutils.IsNotEmpty(reqId) {
- sb.WriteString(fmt.Sprintf("RequestID: %s\t", reqId))
- }
- span := opentracing.SpanFromContext(ctx)
- if span != nil {
- if jspan, ok := span.(*jaeger.Span); ok {
- sb.WriteString(fmt.Sprintf("TraceID: %s\t", jspan.SpanContext().TraceID().String()))
- } else {
- sb.WriteString(fmt.Sprintf("TraceID: %s\t", span))
- }
- }
- sb.WriteString(fmt.Sprintf("SQL: %s", PopulatedSql(query, args...)))
- if hit != nil {
- sb.WriteString(fmt.Sprintf("\tHIT: %t", *hit))
- }
- if err != nil {
- sb.WriteString(fmt.Sprintf("\tERR: %s", errors.Wrap(err, caller.NewCaller().String())))
- }
- receiver.Logger.Info().Msgf(sb.String())
-}
diff --git a/toolkit/sqlext/logger/logger_test.go b/toolkit/sqlext/logger/logger_test.go
deleted file mode 100644
index c2885b70..00000000
--- a/toolkit/sqlext/logger/logger_test.go
+++ /dev/null
@@ -1,84 +0,0 @@
-package logger_test
-
-import (
- "context"
- "os"
- "testing"
-
- "github.com/ascarter/requestid"
- "github.com/google/uuid"
- "github.com/opentracing/opentracing-go"
- "github.com/opentracing/opentracing-go/mocktracer"
- "github.com/unionj-cloud/go-doudou/v2/framework/tracing"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sqlext/logger"
-)
-
-func TestMain(m *testing.M) {
- os.Setenv("GDD_SERVICE_NAME", "TestSqlLogger")
- os.Setenv("GDD_SQL_LOG_ENABLE", "true")
-
- tracer, closer := tracing.Init()
- defer closer.Close()
- opentracing.SetGlobalTracer(tracer)
-
- m.Run()
-}
-
-func TestSqlLogger_Log(t *testing.T) {
- type fields struct {
- ctx func() context.Context
- }
- type args struct {
- query string
- args []interface{}
- }
- tests := []struct {
- name string
- fields fields
- args args
- }{
- {
- name: "",
- fields: fields{
- ctx: func() context.Context {
- return requestid.NewContext(context.Background(), uuid.NewString())
- },
- },
- args: args{
- query: "select * from ddl_user where (`school` = 'Shanxi Univ.' and `delete_at` is null) order by `create_at` desc limit ?,?",
- args: []interface{}{0, 2},
- },
- },
- {
- name: "",
- fields: fields{
- ctx: func() context.Context {
- _, ctx := opentracing.StartSpanFromContext(requestid.NewContext(context.Background(), uuid.NewString()), "TestSqlLogger")
- return ctx
- },
- },
- args: args{
- query: "select * from ddl_user where (`school` = 'Shanxi Univ.' and `delete_at` is null) order by `create_at` desc limit ?",
- args: []interface{}{20},
- },
- },
- {
- name: "",
- fields: fields{
- ctx: func() context.Context {
- return opentracing.ContextWithSpan(context.Background(), mocktracer.New().StartSpan("TestSqlLogger"))
- },
- },
- args: args{
- query: "select * from ddl_user where (`school` = ? and `delete_at` is null) order by `create_at` desc",
- args: []interface{}{"Shanxi Univ."},
- },
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- receiver := logger.NewSqlLogger()
- receiver.Log(tt.fields.ctx(), tt.args.query, tt.args.args...)
- })
- }
-}
diff --git a/toolkit/sqlext/logicsymbol/logicsymbol.go b/toolkit/sqlext/logicsymbol/logicsymbol.go
deleted file mode 100644
index 3216aa37..00000000
--- a/toolkit/sqlext/logicsymbol/logicsymbol.go
+++ /dev/null
@@ -1,15 +0,0 @@
-package logicsymbol
-
-// LogicSymbol define logic operators
-type LogicSymbol string
-
-const (
- // And logic
- And LogicSymbol = "and"
- // Or logic
- Or LogicSymbol = "or"
- // Append logic
- Append LogicSymbol = " "
- // End is similar with Append except that End does not add parentheses
- End LogicSymbol = ""
-)
diff --git a/toolkit/sqlext/query/query.go b/toolkit/sqlext/query/query.go
deleted file mode 100644
index 08b6a7e4..00000000
--- a/toolkit/sqlext/query/query.go
+++ /dev/null
@@ -1,429 +0,0 @@
-package query
-
-import (
- "fmt"
- "github.com/goccy/go-reflect"
- "strings"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sqlext/arithsymbol"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sqlext/logicsymbol"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sqlext/sortenum"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-)
-
-// Base sql expression
-type Base interface {
- Sql() (string, []interface{})
- //NamedSql() (string, []interface{})
-}
-
-// Q used for building sql expression
-type Q interface {
- Base
- And(q Base) Where
- Or(q Base) Where
- Append(q Base) Where
- End(q Base) Where
-}
-
-// Criteria wrap a group of column, value and operator such as name = 20
-type Criteria struct {
- // table alias
- talias string
- col string
- val interface{}
- asym arithsymbol.ArithSymbol
-}
-
-// Sql implement Base interface, return sql expression
-func (c Criteria) Sql() (string, []interface{}) {
- if c.asym == arithsymbol.In || c.asym == arithsymbol.NotIn {
- var args []interface{}
- var sb strings.Builder
- sb.WriteString(fmt.Sprintf("`%s` %s (", c.col, c.asym))
-
- var vals []string
- switch reflect.TypeOf(c.val).Kind() {
- case reflect.Slice:
- data := reflect.ValueOf(c.val)
- for i := 0; i < data.Len(); i++ {
- vals = append(vals, "?")
- args = append(args, data.Index(i).Interface())
- }
- default:
- vals = append(vals, "?")
- args = append(args, c.val)
- }
-
- sb.WriteString(strings.Join(vals, ","))
- sb.WriteString(")")
-
- return sb.String(), args
- }
- if stringutils.IsNotEmpty(c.talias) {
- if c.asym == arithsymbol.Is || c.asym == arithsymbol.Not {
- return fmt.Sprintf("%s.`%s` %s null", c.talias, c.col, c.asym), nil
- }
- return fmt.Sprintf("%s.`%s` %s ?", c.talias, c.col, c.asym), []interface{}{c.val}
- }
- if c.asym == arithsymbol.Is || c.asym == arithsymbol.Not {
- return fmt.Sprintf("`%s` %s null", c.col, c.asym), nil
- }
- return fmt.Sprintf("`%s` %s ?", c.col, c.asym), []interface{}{c.val}
-}
-
-// C new a Criteria
-func C() Criteria {
- return Criteria{}
-}
-
-func (c Criteria) ToWhere() Where {
- w := Where{
- children: make([]Base, 0),
- }
- w.children = append(w.children, c, String(""))
- w.lsym = logicsymbol.End
- return w
-}
-
-// Col set column name
-func (c Criteria) Col(col string) Criteria {
- if strings.Contains(col, ".") {
- i := strings.Index(col, ".")
- c.talias = col[:i]
- c.col = col[i+1:]
- } else {
- c.col = col
- }
- return c
-}
-
-// Eq set = operator and column value
-func (c Criteria) Eq(val interface{}) Criteria {
- c.val = val
- c.asym = arithsymbol.Eq
- return c
-}
-
-// Ne set != operator and column value
-func (c Criteria) Ne(val interface{}) Criteria {
- c.val = val
- c.asym = arithsymbol.Ne
- return c
-}
-
-// Gt set > operator and column value
-func (c Criteria) Gt(val interface{}) Criteria {
- c.val = val
- c.asym = arithsymbol.Gt
- return c
-}
-
-// Lt set < operator and column value
-func (c Criteria) Lt(val interface{}) Criteria {
- c.val = val
- c.asym = arithsymbol.Lt
- return c
-}
-
-// Gte set >= operator and column value
-func (c Criteria) Gte(val interface{}) Criteria {
- c.val = val
- c.asym = arithsymbol.Gte
- return c
-}
-
-// Lte set <= operator and column value
-func (c Criteria) Lte(val interface{}) Criteria {
- c.val = val
- c.asym = arithsymbol.Lte
- return c
-}
-
-// IsNull set is null
-func (c Criteria) IsNull() Criteria {
- c.asym = arithsymbol.Is
- return c
-}
-
-// IsNotNull set is not null
-func (c Criteria) IsNotNull() Criteria {
- c.asym = arithsymbol.Not
- return c
-}
-
-// In set in operator and column value, val should be a slice type value
-func (c Criteria) In(val interface{}) Criteria {
- c.val = val
- c.asym = arithsymbol.In
- return c
-}
-
-// NotIn set not in operator and column value, val should be a slice type value
-func (c Criteria) NotIn(val interface{}) Criteria {
- c.val = val
- c.asym = arithsymbol.NotIn
- return c
-}
-
-// Like set like operator and column value, val should be a slice type value
-func (c Criteria) Like(val interface{}) Criteria {
- c.val = val
- c.asym = arithsymbol.Like
- return c
-}
-
-// And concat another sql expression builder with And
-func (c Criteria) And(cri Base) Where {
- w := Where{
- children: make([]Base, 0),
- }
- w.children = append(w.children, c, cri)
- w.lsym = logicsymbol.And
- return w
-}
-
-// Or concat another sql expression builder with Or
-func (c Criteria) Or(cri Base) Where {
- w := Where{
- children: make([]Base, 0),
- }
- w.children = append(w.children, c, cri)
- w.lsym = logicsymbol.Or
- return w
-}
-
-// Append concat another sql expression builder with Append
-func (c Criteria) Append(cri Base) Where {
- w := Where{
- children: make([]Base, 0),
- }
- w.children = append(w.children, c, cri)
- w.lsym = logicsymbol.Append
- return w
-}
-
-// End does nothing for Criteria same as Append empty
-func (c Criteria) End(q Base) Where {
- w := Where{
- children: make([]Base, 0),
- }
- w.children = append(w.children, c, q)
- w.lsym = logicsymbol.End
- return w
-}
-
-// Where concat children clauses with one of logic operators And, Or, Append
-type Where struct {
- lsym logicsymbol.LogicSymbol
- children []Base
-}
-
-func (w Where) IsEmpty() bool {
- return len(w.children) == 0
-}
-
-// Sql implement Base interface, return string sql expression
-func (w Where) Sql() (string, []interface{}) {
- if len(w.children) == 0 {
- return "", nil
- }
- var args []interface{}
- w0, args0 := w.children[0].Sql()
- args = append(args, args0...)
- w1, args1 := w.children[1].Sql()
- args = append(args, args1...)
- switch w.lsym {
- case logicsymbol.And, logicsymbol.Or:
- if stringutils.IsNotEmpty(w0) && stringutils.IsNotEmpty(w1) {
- return fmt.Sprintf("(%s %s %s)", w0, w.lsym, w1), args
- } else {
- if stringutils.IsNotEmpty(w0) {
- return w0, args
- } else if stringutils.IsNotEmpty(w1) {
- return w1, args
- } else {
- return "", nil
- }
- }
- case logicsymbol.Append:
- if stringutils.IsNotEmpty(w0) && stringutils.IsNotEmpty(w1) {
- return fmt.Sprintf("(%s%s%s)", w0, w.lsym, w1), args
- } else {
- if stringutils.IsNotEmpty(w0) {
- return w0, args
- } else if stringutils.IsNotEmpty(w1) {
- return w1, args
- } else {
- return "", nil
- }
- }
- case logicsymbol.End:
- fallthrough
- default:
- if stringutils.IsEmpty(w1) {
- return w0, args
- }
- return fmt.Sprintf("%s %s", w0, w1), args
- }
-}
-
-// And concat another sql expression builder with And
-func (w Where) And(whe Base) Where {
- parentW := Where{
- children: make([]Base, 0),
- }
- parentW.children = append(parentW.children, w, whe)
- parentW.lsym = logicsymbol.And
- return parentW
-}
-
-// Or concat another sql expression builder with Or
-func (w Where) Or(whe Base) Where {
- parentW := Where{
- children: make([]Base, 0),
- }
- parentW.children = append(parentW.children, w, whe)
- parentW.lsym = logicsymbol.Or
- return parentW
-}
-
-// Append concat another sql expression builder with Append
-func (w Where) Append(whe Base) Where {
- parentW := Where{
- children: make([]Base, 0),
- }
- parentW.children = append(parentW.children, w, whe)
- if _, ok := whe.(Page); ok {
- parentW.lsym = logicsymbol.End
- } else {
- parentW.lsym = logicsymbol.Append
- }
- return parentW
-}
-
-func (w Where) End(q Base) Where {
- parentW := Where{
- children: make([]Base, 0),
- }
- parentW.children = append(parentW.children, w, q)
- parentW.lsym = logicsymbol.End
- return parentW
-}
-
-// Order by Col Sort
-type Order struct {
- Col string
- Sort sortenum.Sort
-}
-
-// Page a sql expression builder for order by clause
-type Page struct {
- Orders []Order
- Offset int
- Size int
-}
-
-// P new a Page
-func P() Page {
- return Page{
- Orders: make([]Order, 0),
- }
-}
-
-func NewPage(pageNo, pageSize int, orders ...Order) Page {
- if pageNo <= 0 {
- pageNo = 1
- }
- offset := 0
- if pageSize > 0 {
- offset = (pageNo - 1) * pageSize
- }
- return Page{
- Offset: offset,
- Size: pageSize,
- Orders: orders,
- }
-}
-
-// Order append an Order
-func (p Page) Order(o Order) Page {
- p.Orders = append(p.Orders, o)
- return p
-}
-
-// Limit set Offset and Size
-func (p Page) Limit(offset, size int) Page {
- p.Offset = offset
- p.Size = size
- return p
-}
-
-// Sql implement Base interface, order by age desc limit 2,1
-func (p Page) Sql() (string, []interface{}) {
- var sb strings.Builder
- var args []interface{}
- if len(p.Orders) > 0 {
- sb.WriteString("order by ")
-
- for i, order := range p.Orders {
- if i > 0 {
- sb.WriteString(",")
- }
- var (
- alias string
- col string
- )
- if strings.Contains(order.Col, ".") {
- idx := strings.Index(order.Col, ".")
- alias = order.Col[:idx]
- col = order.Col[idx+1:]
- } else {
- col = order.Col
- }
- if stringutils.IsNotEmpty(alias) {
- sb.WriteString(fmt.Sprintf("%s.`%s` %s", alias, col, order.Sort))
- } else {
- sb.WriteString(fmt.Sprintf("`%s` %s", col, order.Sort))
- }
- }
- }
-
- sb.WriteString(" ")
-
- if p.Size > 0 {
- sb.WriteString("limit ?,?")
- args = append(args, p.Offset, p.Size)
- }
-
- return strings.TrimSpace(sb.String()), args
-}
-
-// PageRet wrap page query result
-type PageRet struct {
- Items interface{}
- PageNo int
- PageSize int
- Total int
- HasNext bool
-}
-
-// NewPageRet new a PageRet
-func NewPageRet(page Page) PageRet {
- pageNo := 1
- if page.Size > 0 {
- pageNo = page.Offset/page.Size + 1
- }
- return PageRet{
- PageNo: pageNo,
- PageSize: page.Size,
- }
-}
-
-// String is an alias of string
-type String string
-
-// Sql implements Base
-func (s String) Sql() (string, []interface{}) {
- return string(s), nil
-}
diff --git a/toolkit/sqlext/query/query_test.go b/toolkit/sqlext/query/query_test.go
deleted file mode 100644
index 972339e5..00000000
--- a/toolkit/sqlext/query/query_test.go
+++ /dev/null
@@ -1,150 +0,0 @@
-package query
-
-import (
- "fmt"
- "testing"
-
- "github.com/stretchr/testify/require"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sqlext/sortenum"
-)
-
-func ExampleCriteria() {
-
- query := C().Col("name").Eq("wubin").Or(C().Col("school").Eq("havard")).And(C().Col("age").Eq(18))
- fmt.Println(query.Sql())
-
- query = C().Col("name").Eq("wubin").Or(C().Col("school").Eq("havard")).And(C().Col("delete_at").IsNotNull())
- fmt.Println(query.Sql())
-
- query = C().Col("name").Eq("wubin").Or(C().Col("school").In("havard")).And(C().Col("delete_at").IsNotNull())
- fmt.Println(query.Sql())
-
- query = C().Col("name").Eq("wubin").Or(C().Col("school").In([]string{"havard", "beijing unv"})).And(C().Col("delete_at").IsNotNull())
- fmt.Println(query.Sql())
-
- query = C().Col("name").Eq("wubin").Or(C().Col("age").In([]int{5, 10})).And(C().Col("delete_at").IsNotNull())
- fmt.Println(query.Sql())
-
- query = C().Col("name").Ne("wubin").Or(C().Col("create_at").Lt("now()"))
- fmt.Println(query.Sql())
-
- page := Page{
- Orders: []Order{
- {
- Col: "create_at",
- Sort: sortenum.Desc,
- },
- },
- Offset: 20,
- Size: 10,
- }
- page = page.Order(Order{
- Col: "score",
- Sort: sortenum.Asc,
- })
- page = page.Limit(30, 5)
- fmt.Println(page.Sql())
- pageRet := NewPageRet(page)
- fmt.Println(pageRet.PageNo)
-
- fmt.Println(P().Order(Order{
- Col: "score",
- Sort: sortenum.Asc,
- }).Limit(20, 10).Sql())
-
- // Output:
- //((`name` = ? or `school` = ?) and `age` = ?) [wubin havard 18]
- //((`name` = ? or `school` = ?) and `delete_at` is not null) [wubin havard]
- //((`name` = ? or `school` in (?)) and `delete_at` is not null) [wubin havard]
- //((`name` = ? or `school` in (?,?)) and `delete_at` is not null) [wubin havard beijing unv]
- //((`name` = ? or `age` in (?,?)) and `delete_at` is not null) [wubin 5 10]
- //(`name` != ? or `create_at` < ?) [wubin now()]
- //order by `create_at` desc,`score` asc limit ?,? [30 5]
- //7
- //order by `score` asc limit ?,? [20 10]
-}
-
-func TestCriteriaAppend(t *testing.T) {
- sqlStatement := C().Col("name").Eq("wubin").Or(C().Col("school").Eq("havard")).
- And(C().Col("age").Eq(18)).
- Or(C().Col("score").Gte(90).And(C().Col("height").Gt(160).And(C().Col("height").Lte(170).
- Append(String("and favourite = 'Go'")))))
- str, _ := sqlStatement.Sql()
- require.Equal(t, "(((`name` = ? or `school` = ?) and `age` = ?) or (`score` >= ? and (`height` > ? and (`height` <= ? and favourite = 'Go'))))", str)
-}
-
-func TestWhereAppend(t *testing.T) {
- sqlStatement := C().Col("name").Eq("wubin").Or(C().Col("school").Eq("havard")).
- And(C().Col("age").Eq(18)).
- Or(C().Col("score").Gte(90).
- And(C().Col("height").Gt(160).And(C().Col("height").Lte(170))).
- Append(String("and favourite = 'Go'")))
- str, _ := sqlStatement.Sql()
- require.Equal(t, "(((`name` = ? or `school` = ?) and `age` = ?) or ((`score` >= ? and (`height` > ? and `height` <= ?)) and favourite = 'Go'))", str)
-}
-
-func ExampleEnd() {
- page := P().Order(Order{
- Col: "create_at",
- Sort: sortenum.Desc,
- }).Limit(0, 1)
- var where Q
- where = C().Col("project_id").Eq(1)
- where = where.And(C().Col("delete_at").IsNull())
- where = where.End(page)
- fmt.Println(where.Sql())
-
- where = C().Col("project_id").Eq(1)
- where = where.And(C().Col("delete_at").IsNull())
- where = where.End(String("for update"))
- fmt.Println(where.Sql())
-
- where = C().Col("cc.project_id").Eq(1)
- where = where.And(C().Col("cc.delete_at").IsNull())
- where = where.End(String("for update"))
- fmt.Println(where.Sql())
-
- where = C().Col("cc.survey_id").Eq("abc").
- And(C().Col("cc.year").Eq(2021)).
- And(C().Col("cc.month").Eq(10)).
- And(C().Col("cc.stat_type").Eq(2)).End(String("for update"))
- fmt.Println(where.Sql())
-
- where = C().Col("cc.name").Like("%ba%")
- fmt.Println(where.Sql())
-
- page = P().Order(Order{
- Col: "user.create_at",
- Sort: sortenum.Desc,
- }).Limit(0, 1)
- where = C().Col("project_id").Eq(1)
- where = where.And(C().Col("delete_at").IsNull())
- where = where.End(page)
- fmt.Println(where.Sql())
-
- where = C().Col("delete_at").IsNull().And(C().Col("op_code").NotIn([]int{1, 2, 3}))
- fmt.Println(where.Sql())
-
- // Output:
- //(`project_id` = ? and `delete_at` is null) order by `create_at` desc limit ?,? [1 0 1]
- //(`project_id` = ? and `delete_at` is null) for update [1]
- //(cc.`project_id` = ? and cc.`delete_at` is null) for update [1]
- //(((cc.`survey_id` = ? and cc.`year` = ?) and cc.`month` = ?) and cc.`stat_type` = ?) for update [abc 2021 10 2]
- //cc.`name` like ? [%ba%]
- //(`project_id` = ? and `delete_at` is null) order by user.`create_at` desc limit ?,? [1 0 1]
- //(`delete_at` is null and `op_code` not in (?,?,?)) [1 2 3]
-}
-
-func TestWhereAppend2(t *testing.T) {
- var where Q
- where = C().Col("left_number").Gt(0).Or(C().Col("left_number").Lt(0))
- where = where.And(C().Col("name").Ne("感谢参与"))
- where = where.And(C().Col("delete_at").IsNull())
- page := P().Order(Order{
- Col: "order",
- Sort: sortenum.Desc,
- }).Limit(0, 10)
- where = where.Append(page)
- str, _ := where.Sql()
- require.Equal(t, "(((`left_number` > ? or `left_number` < ?) and `name` != ?) and `delete_at` is null) order by `order` desc limit ?,?", str)
-}
diff --git a/toolkit/sqlext/sortenum/sortenum.go b/toolkit/sqlext/sortenum/sortenum.go
deleted file mode 100644
index c5dbcf6e..00000000
--- a/toolkit/sqlext/sortenum/sortenum.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package sortenum
-
-// Sort string alias for asc and desc
-type Sort string
-
-const (
- // Asc constant value for asc
- Asc Sort = "asc"
- // Desc constant value for desc
- Desc Sort = "desc"
-)
diff --git a/toolkit/sqlext/testdata/domain/base.go b/toolkit/sqlext/testdata/domain/base.go
deleted file mode 100644
index 2bdceb0e..00000000
--- a/toolkit/sqlext/testdata/domain/base.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package domain
-
-import "time"
-
-type Base struct {
- CreateAt *time.Time `dd:"default:CURRENT_TIMESTAMP"`
- UpdateAt *time.Time `dd:"default:CURRENT_TIMESTAMP;extra:ON UPDATE CURRENT_TIMESTAMP"`
- DeleteAt *time.Time
-}
diff --git a/toolkit/sqlext/testdata/domain/order.go b/toolkit/sqlext/testdata/domain/order.go
deleted file mode 100644
index 1bd0250b..00000000
--- a/toolkit/sqlext/testdata/domain/order.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package domain
-
-//dd:table
-type Order struct {
- ID int `dd:"pk;auto"`
- Amount int64
- UserId int `dd:"type:int;fk:ddl_user,id,fk_ddl_user,ON DELETE CASCADE ON UPDATE NO ACTION"`
-
- Base
-}
diff --git a/toolkit/sqlext/testdata/domain/user.go b/toolkit/sqlext/testdata/domain/user.go
deleted file mode 100644
index 30ea4595..00000000
--- a/toolkit/sqlext/testdata/domain/user.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package domain
-
-import "time"
-
-//dd:table
-type User struct {
- ID int `dd:"pk;auto"`
- Name string `dd:"index:name_phone_idx,2;default:'jack'"`
- Phone string `dd:"index:name_phone_idx,1;default:'13552053960';extra:comment '手机号'"`
- Age int `dd:"index;unsigned"`
- No int `dd:"type:int;unique"`
- UniqueCol int `dd:"type:int;unique:unique_col_idx,1"`
- UniqueCol2 int `dd:"type:int;unique:unique_col_idx,2"`
- School string `dd:"null;default:'harvard';extra:comment '学校'"`
- IsStudent bool
- Rule string `dd:"type:varchar(255);unique;extra:comment '链接匹配规则,匹配的链接采用该css规则来爬'"`
- RuleType string `dd:"type:varchar(45);extra:comment '链接匹配规则类型,支持prefix前缀匹配和regex正则匹配'"`
-
- ArriveAt *time.Time `dd:"type:datetime;extra:comment '到货时间'"`
- Status int8 `dd:"type:tinyint(4);extra:comment '0进行中
-1完结
-2取消'"`
-
- Base
-}
diff --git a/toolkit/sqlext/testdata/domain2/base.go b/toolkit/sqlext/testdata/domain2/base.go
deleted file mode 100644
index 2bdceb0e..00000000
--- a/toolkit/sqlext/testdata/domain2/base.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package domain
-
-import "time"
-
-type Base struct {
- CreateAt *time.Time `dd:"default:CURRENT_TIMESTAMP"`
- UpdateAt *time.Time `dd:"default:CURRENT_TIMESTAMP;extra:ON UPDATE CURRENT_TIMESTAMP"`
- DeleteAt *time.Time
-}
diff --git a/toolkit/sqlext/testdata/domain2/book.go b/toolkit/sqlext/testdata/domain2/book.go
deleted file mode 100644
index 5912ca42..00000000
--- a/toolkit/sqlext/testdata/domain2/book.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package domain
-
-//dd:table
-type Book struct {
- ID int `dd:"pk;auto"`
- UserId int `dd:"type:int;fk:ddl_publisher,id,fk_user,ON DELETE CASCADE ON UPDATE NO ACTION"`
- PublisherId int
-
- Base
-}
diff --git a/toolkit/sqlext/testdata/domain2/publisher.go b/toolkit/sqlext/testdata/domain2/publisher.go
deleted file mode 100644
index 8ff3faf2..00000000
--- a/toolkit/sqlext/testdata/domain2/publisher.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package domain
-
-//dd:table
-type Publisher struct {
- ID int `dd:"pk;auto"`
- Name string
-
- Base
-}
diff --git a/toolkit/sqlext/testdata/domain2/user.go b/toolkit/sqlext/testdata/domain2/user.go
deleted file mode 100644
index 30ea4595..00000000
--- a/toolkit/sqlext/testdata/domain2/user.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package domain
-
-import "time"
-
-//dd:table
-type User struct {
- ID int `dd:"pk;auto"`
- Name string `dd:"index:name_phone_idx,2;default:'jack'"`
- Phone string `dd:"index:name_phone_idx,1;default:'13552053960';extra:comment '手机号'"`
- Age int `dd:"index;unsigned"`
- No int `dd:"type:int;unique"`
- UniqueCol int `dd:"type:int;unique:unique_col_idx,1"`
- UniqueCol2 int `dd:"type:int;unique:unique_col_idx,2"`
- School string `dd:"null;default:'harvard';extra:comment '学校'"`
- IsStudent bool
- Rule string `dd:"type:varchar(255);unique;extra:comment '链接匹配规则,匹配的链接采用该css规则来爬'"`
- RuleType string `dd:"type:varchar(45);extra:comment '链接匹配规则类型,支持prefix前缀匹配和regex正则匹配'"`
-
- ArriveAt *time.Time `dd:"type:datetime;extra:comment '到货时间'"`
- Status int8 `dd:"type:tinyint(4);extra:comment '0进行中
-1完结
-2取消'"`
-
- Base
-}
diff --git a/toolkit/sqlext/testdata/domain3/base.go b/toolkit/sqlext/testdata/domain3/base.go
deleted file mode 100644
index 2bdceb0e..00000000
--- a/toolkit/sqlext/testdata/domain3/base.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package domain
-
-import "time"
-
-type Base struct {
- CreateAt *time.Time `dd:"default:CURRENT_TIMESTAMP"`
- UpdateAt *time.Time `dd:"default:CURRENT_TIMESTAMP;extra:ON UPDATE CURRENT_TIMESTAMP"`
- DeleteAt *time.Time
-}
diff --git a/toolkit/sqlext/testdata/domain3/book.go b/toolkit/sqlext/testdata/domain3/book.go
deleted file mode 100644
index ba4d29b4..00000000
--- a/toolkit/sqlext/testdata/domain3/book.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package domain
-
-//dd:table
-type Book struct {
- ID int `dd:"pk;auto"`
- UserId int `dd:"type:int"`
- PublisherId int `dd:"fk:ddl_publisher,id,fk_publisher,ON DELETE CASCADE ON UPDATE NO ACTION"`
-
- Base
-}
diff --git a/toolkit/sqlext/testdata/domain3/publisher.go b/toolkit/sqlext/testdata/domain3/publisher.go
deleted file mode 100644
index 8ff3faf2..00000000
--- a/toolkit/sqlext/testdata/domain3/publisher.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package domain
-
-//dd:table
-type Publisher struct {
- ID int `dd:"pk;auto"`
- Name string
-
- Base
-}
diff --git a/toolkit/sqlext/testdata/domain3/user.go b/toolkit/sqlext/testdata/domain3/user.go
deleted file mode 100644
index 4539776e..00000000
--- a/toolkit/sqlext/testdata/domain3/user.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package domain
-
-import "time"
-
-//dd:table
-type User struct {
- ID int `dd:"pk;auto"`
- Name string `dd:"index:name_phone_idx,2;default:'jack'"`
- Phone string `dd:"index:name_phone_idx,1;default:'13552053960';extra:comment '手机号'"`
- Age int `dd:"unsigned"`
- No int `dd:"type:int;unique"`
- UniqueCol int `dd:"type:int;unique:unique_col_idx,1"`
- UniqueCol2 int `dd:"type:int;unique:unique_col_idx,2"`
- School string `dd:"null;default:'harvard';extra:comment '学校'"`
- IsStudent bool
- Rule string `dd:"type:varchar(255);unique;extra:comment '链接匹配规则,匹配的链接采用该css规则来爬'"`
- RuleType string `dd:"type:varchar(45);extra:comment '链接匹配规则类型,支持prefix前缀匹配和regex正则匹配'"`
-
- ArriveAt *time.Time `dd:"type:datetime;extra:comment '到货时间'"`
- Status int8 `dd:"type:tinyint(4);extra:comment '0进行中
-1完结
-2取消'"`
-
- Base
-}
diff --git a/toolkit/sqlext/testdata/domain4/base.go b/toolkit/sqlext/testdata/domain4/base.go
deleted file mode 100644
index 2bdceb0e..00000000
--- a/toolkit/sqlext/testdata/domain4/base.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package domain
-
-import "time"
-
-type Base struct {
- CreateAt *time.Time `dd:"default:CURRENT_TIMESTAMP"`
- UpdateAt *time.Time `dd:"default:CURRENT_TIMESTAMP;extra:ON UPDATE CURRENT_TIMESTAMP"`
- DeleteAt *time.Time
-}
diff --git a/toolkit/sqlext/testdata/domain4/book.go b/toolkit/sqlext/testdata/domain4/book.go
deleted file mode 100644
index 01aaf6b7..00000000
--- a/toolkit/sqlext/testdata/domain4/book.go
+++ /dev/null
@@ -1,10 +0,0 @@
-package domain
-
-//dd:table
-type Book struct {
- ID int `dd:"pk;auto"`
- UserId int `dd:"type:int;fk:ddl_user,id,fk_user,ON DELETE CASCADE ON UPDATE NO ACTION"`
- PublisherId int
-
- Base
-}
diff --git a/toolkit/sqlext/testdata/domain4/publisher.go b/toolkit/sqlext/testdata/domain4/publisher.go
deleted file mode 100644
index 8ff3faf2..00000000
--- a/toolkit/sqlext/testdata/domain4/publisher.go
+++ /dev/null
@@ -1,9 +0,0 @@
-package domain
-
-//dd:table
-type Publisher struct {
- ID int `dd:"pk;auto"`
- Name string
-
- Base
-}
diff --git a/toolkit/sqlext/testdata/domain4/user.go b/toolkit/sqlext/testdata/domain4/user.go
deleted file mode 100644
index 21d5c755..00000000
--- a/toolkit/sqlext/testdata/domain4/user.go
+++ /dev/null
@@ -1,25 +0,0 @@
-package domain
-
-import "time"
-
-//dd:table
-type User struct {
- ID int `dd:"pk;auto"`
- Name string `dd:"index:name_phone_idx,2;default:'jack'"`
- Phone string `dd:"default:'13552053960';extra:comment '手机号'"`
- Age int `dd:"unsigned"`
- No int `dd:"type:int;unique"`
- UniqueCol int `dd:"type:int;unique:unique_col_idx,1"`
- UniqueCol2 int `dd:"type:int;unique:unique_col_idx,2"`
- School string `dd:"null;index:name_phone_idx,1;default:'harvard';extra:comment '学校'"`
- IsStudent bool
- Rule string `dd:"type:varchar(255);unique;extra:comment '链接匹配规则,匹配的链接采用该css规则来爬'"`
- RuleType string `dd:"type:varchar(45);extra:comment '链接匹配规则类型,支持prefix前缀匹配和regex正则匹配'"`
-
- ArriveAt *time.Time `dd:"type:datetime;extra:comment '到货时间'"`
- Status int8 `dd:"type:tinyint(4);extra:comment '0进行中
-1完结
-2取消'"`
-
- Base
-}
diff --git a/toolkit/sqlext/testdata/go.mod b/toolkit/sqlext/testdata/go.mod
deleted file mode 100644
index 7b243b9e..00000000
--- a/toolkit/sqlext/testdata/go.mod
+++ /dev/null
@@ -1,3 +0,0 @@
-module testdata
-
-go 1.15
diff --git a/toolkit/sqlext/wrapper/wrapper.go b/toolkit/sqlext/wrapper/wrapper.go
deleted file mode 100644
index a1f7205c..00000000
--- a/toolkit/sqlext/wrapper/wrapper.go
+++ /dev/null
@@ -1,250 +0,0 @@
-package wrapper
-
-import (
- "context"
- "database/sql"
- "time"
-
- "github.com/jmoiron/sqlx"
- "github.com/lithammer/shortuuid/v4"
- "github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/cache"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/sqlext/logger"
-)
-
-// DB wraps sqlx.Tx and sqlx.DB https://github.com/jmoiron/sqlx/issues/344#issuecomment-318372779
-type DB interface {
- Querier
- BeginTxx(ctx context.Context, opts *sql.TxOptions) (GddTx, error)
- Close() error
-}
-
-// Tx transaction
-type Tx interface {
- Querier
- Commit() error
- Rollback() error
-}
-
-// Querier common operations for sqlx.Tx and sqlx.DB
-type Querier interface {
- NamedExecContext(ctx context.Context, query string, arg interface{}) (sql.Result, error)
- ExecContext(ctx context.Context, query string, args ...interface{}) (sql.Result, error)
- GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
- Rebind(query string) string
- BindNamed(query string, arg interface{}) (string, []interface{}, error)
- SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) error
- //RefreshCache()
-}
-
-// GddDB wraps sqlx.DB
-type GddDB struct {
- *sqlx.DB
- logger logger.SqlLogger
- cacheStore *cache.Cache
- redisKeyTTL time.Duration
-}
-
-type GddDBOption func(*GddDB)
-
-func WithLogger(logger logger.SqlLogger) GddDBOption {
- return func(g *GddDB) {
- g.logger = logger
- }
-}
-
-func WithCache(store *cache.Cache) GddDBOption {
- return func(g *GddDB) {
- g.cacheStore = store
- }
-}
-
-func WithRedisKeyTTL(ttl time.Duration) GddDBOption {
- return func(g *GddDB) {
- g.redisKeyTTL = ttl
- }
-}
-
-func NewGddDB(db *sqlx.DB, options ...GddDBOption) GddDB {
- g := &GddDB{
- DB: db,
- logger: logger.NewSqlLogger(),
- redisKeyTTL: time.Hour,
- }
- for _, opt := range options {
- opt(g)
- }
- return *g
-}
-
-func (g GddDB) NamedExecContext(ctx context.Context, query string, arg interface{}) (ret sql.Result, err error) {
- var (
- q string
- args []interface{}
- )
- defer func() {
- g.logger.LogWithErr(ctx, err, nil, q, args...)
- }()
- q, args, err = g.DB.BindNamed(query, arg)
- err = errors.Wrap(err, caller.NewCaller().String())
- if err != nil {
- return nil, err
- }
- ret, err = g.DB.NamedExecContext(ctx, query, arg)
- err = errors.Wrap(err, caller.NewCaller().String())
- return
-}
-
-func (g GddDB) ExecContext(ctx context.Context, query string, args ...interface{}) (ret sql.Result, err error) {
- defer func() {
- g.logger.LogWithErr(ctx, err, nil, query, args...)
- }()
- ret, err = g.DB.ExecContext(ctx, query, args...)
- err = errors.Wrap(err, caller.NewCaller().String())
- return
-}
-
-func (g GddDB) GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) (err error) {
- hit := true
- defer func() {
- g.logger.LogWithErr(ctx, err, &hit, query, args...)
- }()
- if g.cacheStore != nil {
- err = g.cacheStore.Once(&cache.Item{
- Key: shortuuid.NewWithNamespace(logger.PopulatedSql(query, args...)),
- Value: dest,
- TTL: g.redisKeyTTL,
- Do: func(*cache.Item) (interface{}, error) {
- hit = false
- err = g.DB.GetContext(ctx, dest, query, args...)
- err = errors.Wrap(err, caller.NewCaller().String())
- return dest, err
- },
- })
- return
- }
- hit = false
- err = g.DB.GetContext(ctx, dest, query, args...)
- err = errors.Wrap(err, caller.NewCaller().String())
- return
-}
-
-func (g GddDB) SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) (err error) {
- hit := true
- defer func() {
- g.logger.LogWithErr(ctx, err, &hit, query, args...)
- }()
- if g.cacheStore != nil {
- err = g.cacheStore.Once(&cache.Item{
- Key: shortuuid.NewWithNamespace(logger.PopulatedSql(query, args...)),
- Value: dest,
- TTL: g.redisKeyTTL,
- Do: func(*cache.Item) (interface{}, error) {
- hit = false
- err = g.DB.SelectContext(ctx, dest, query, args...)
- err = errors.Wrap(err, caller.NewCaller().String())
- return dest, err
- },
- })
- return
- }
- hit = false
- err = g.DB.SelectContext(ctx, dest, query, args...)
- err = errors.Wrap(err, caller.NewCaller().String())
- return
-}
-
-// BeginTxx begins a transaction
-func (g GddDB) BeginTxx(ctx context.Context, opts *sql.TxOptions) (GddTx, error) {
- tx, err := g.DB.BeginTxx(ctx, opts)
- if err != nil {
- return GddTx{}, err
- }
- return GddTx{tx, g.logger, g.cacheStore, g.redisKeyTTL}, nil
-}
-
-// GddTx wraps sqlx.Tx
-type GddTx struct {
- *sqlx.Tx
- logger logger.SqlLogger
- cacheStore *cache.Cache
- redisKeyTTL time.Duration
-}
-
-func (g GddTx) NamedExecContext(ctx context.Context, query string, arg interface{}) (ret sql.Result, err error) {
- var (
- q string
- args []interface{}
- )
- defer func() {
- g.logger.LogWithErr(ctx, err, nil, q, args...)
- }()
- q, args, err = g.Tx.BindNamed(query, arg)
- err = errors.Wrap(err, caller.NewCaller().String())
- if err != nil {
- return nil, err
- }
- ret, err = g.Tx.NamedExecContext(ctx, query, arg)
- err = errors.Wrap(err, caller.NewCaller().String())
- return
-}
-
-func (g GddTx) ExecContext(ctx context.Context, query string, args ...interface{}) (ret sql.Result, err error) {
- defer func() {
- g.logger.LogWithErr(ctx, err, nil, query, args...)
- }()
- ret, err = g.Tx.ExecContext(ctx, query, args...)
- err = errors.Wrap(err, caller.NewCaller().String())
- return
-}
-
-func (g GddTx) GetContext(ctx context.Context, dest interface{}, query string, args ...interface{}) (err error) {
- hit := true
- defer func() {
- g.logger.LogWithErr(ctx, err, &hit, query, args...)
- }()
- if g.cacheStore != nil {
- err = g.cacheStore.Once(&cache.Item{
- Key: shortuuid.NewWithNamespace(logger.PopulatedSql(query, args...)),
- Value: dest,
- TTL: g.redisKeyTTL,
- Do: func(*cache.Item) (interface{}, error) {
- hit = false
- err = g.Tx.GetContext(ctx, dest, query, args...)
- err = errors.Wrap(err, caller.NewCaller().String())
- return dest, err
- },
- })
- return
- }
- hit = false
- err = g.Tx.GetContext(ctx, dest, query, args...)
- err = errors.Wrap(err, caller.NewCaller().String())
- return
-}
-
-func (g GddTx) SelectContext(ctx context.Context, dest interface{}, query string, args ...interface{}) (err error) {
- hit := true
- defer func() {
- g.logger.LogWithErr(ctx, err, &hit, query, args...)
- }()
- if g.cacheStore != nil {
- err = g.cacheStore.Once(&cache.Item{
- Key: shortuuid.NewWithNamespace(logger.PopulatedSql(query, args...)),
- Value: dest,
- TTL: g.redisKeyTTL,
- Do: func(*cache.Item) (interface{}, error) {
- hit = false
- err = g.Tx.SelectContext(ctx, dest, query, args...)
- err = errors.Wrap(err, caller.NewCaller().String())
- return dest, err
- },
- })
- return
- }
- hit = false
- err = g.Tx.SelectContext(ctx, dest, query, args...)
- err = errors.Wrap(err, caller.NewCaller().String())
- return
-}
diff --git a/toolkit/stringutils/LICENSE b/toolkit/stringutils/LICENSE
deleted file mode 100644
index ad734c93..00000000
--- a/toolkit/stringutils/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-MIT License
-
-Copyright (c) 2020 Ben Boyter
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in all
-copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-SOFTWARE.
diff --git a/toolkit/stringutils/README.md b/toolkit/stringutils/README.md
deleted file mode 100644
index 4ea562a8..00000000
--- a/toolkit/stringutils/README.md
+++ /dev/null
@@ -1,107 +0,0 @@
-# Go-string
-
-[![Go Report Card](https://goreportcard.com/badge/github.com/boyter/go-string)](https://goreportcard.com/report/github.com/boyter/go-string)
-[![Str Count Badge](https://sloc.xyz/github/boyter/go-string/)](https://github.com/boyter/go-string/)
-
-Useful string utility functions for Go projects. Either because they are faster than the common Go version or do not exist in the standard library.
-
-You can find all details here https://pkg.go.dev/github.com/boyter/go-string
-
-Probably the most useful methods are IndexAll and IndexAllIgnoreCase which for string literal searches should be drop in replacements for regexp.FindAllIndex while totally avoiding the regular expression engine and as such being much faster.
-
-Some quick benchmarks using a simple program which opens a 550MB file and searches over it in memory.
-Each search is done three times, the first using regexp.FindAllIndex and the second using IndexAllIgnoreCase.
-
-For this specific example the wall clock time to run is at least 10x less, but with the same matching results.
-
-```
-$ ./csperf ſecret 550MB
-File length 576683100
-
-FindAllIndex (regex ignore case)
-Scan took 25.403231773s 16680
-Scan took 25.39742299s 16680
-Scan took 25.227218738s 16680
-
-IndexAllIgnoreCase (custom)
-Scan took 2.04013314s 16680
-Scan took 2.019360935s 16680
-Scan took 1.996732171s 16680
-```
-
-The above example in code for you to copy
-
-```
-// Simple test comparison between various search methods
-func main() {
- arg1 := os.Args[1]
- arg2 := os.Args[2]
-
- b, err := os.ReadFile(arg2)
- if err != nil {
- fmt.Print(err)
- return
- }
-
- fmt.Println("File length", len(b))
-
- haystack := string(b)
-
- var start time.Time
- var elapsed time.Duration
-
- fmt.Println("\nFindAllIndex (regex)")
- r := regexp.MustCompile(regexp.QuoteMeta(arg1))
- for i := 0; i < 3; i++ {
- start = time.Now()
- all := r.FindAllIndex(b, -1)
- elapsed = time.Since(start)
- fmt.Println("Scan took", elapsed, len(all))
- }
-
- fmt.Println("\nIndexAll (custom)")
- for i := 0; i < 3; i++ {
- start = time.Now()
- all := str.IndexAll(haystack, arg1, -1)
- elapsed = time.Since(start)
- fmt.Println("Scan took", elapsed, len(all))
- }
-
- r = regexp.MustCompile(`(?i)` + regexp.QuoteMeta(arg1))
- fmt.Println("\nFindAllIndex (regex ignore case)")
- for i := 0; i < 3; i++ {
- start = time.Now()
- all := r.FindAllIndex(b, -1)
- elapsed = time.Since(start)
- fmt.Println("Scan took", elapsed, len(all))
- }
-
- fmt.Println("\nIndexAllIgnoreCase (custom)")
- for i := 0; i < 3; i++ {
- start = time.Now()
- all := str.IndexAllIgnoreCase(haystack, arg1, -1)
- elapsed = time.Since(start)
- fmt.Println("Scan took", elapsed, len(all))
- }
-}
-
-```
-
-Note that it performs best with real documents and wost when searching over random data. Depending on what you are searching you may have a similar speed up or a marginal one.
-
-FindAllIndex has a similar speed up,
-
-```
-// BenchmarkFindAllIndex-8 2458844 480.0 ns/op
-// BenchmarkIndexAll-8 14819680 79.6 ns/op
-```
-
-See the benchmarks for full proof where they test various edge cases.
-
-The other most useful method is HighlightString. HighlightString takes in some content and locations and then inserts in/out
-strings which can be used for highlighting around matching terms. For example you could pass in `"test"` and have it return `"test"`.
-The argument locations accepts output from regexp.FindAllIndex or the included `IndexAllIgnoreCase` or `IndexAll`.
-
-All code is dual-licenced as either MIT or Unlicence. Your choice when you use it.
-
-Note that as an Australian I cannot put this into the public domain, hence the choice most liberal licences I can find.
diff --git a/toolkit/stringutils/UNLICENSE b/toolkit/stringutils/UNLICENSE
deleted file mode 100644
index 00d2e135..00000000
--- a/toolkit/stringutils/UNLICENSE
+++ /dev/null
@@ -1,24 +0,0 @@
-This is free and unencumbered software released into the public domain.
-
-Anyone is free to copy, modify, publish, use, compile, sell, or
-distribute this software, either in source code form or as a compiled
-binary, for any purpose, commercial or non-commercial, and by any
-means.
-
-In jurisdictions that recognize copyright laws, the author or authors
-of this software dedicate any and all copyright interest in the
-software to the public domain. We make this dedication for the benefit
-of the public at large and to the detriment of our heirs and
-successors. We intend this dedication to be an overt act of
-relinquishment in perpetuity of all present and future rights to this
-software under copyright law.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
-EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
-MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
-IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
-OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
-ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
-OTHER DEALINGS IN THE SOFTWARE.
-
-For more information, please refer to
\ No newline at end of file
diff --git a/toolkit/stringutils/common.go b/toolkit/stringutils/common.go
deleted file mode 100644
index f5742017..00000000
--- a/toolkit/stringutils/common.go
+++ /dev/null
@@ -1,137 +0,0 @@
-// SPDX-License-Identifier: MIT OR Unlicense
-
-package stringutils
-
-import (
- "strings"
- "unicode"
-)
-
-// RemoveStringDuplicates is a simple helper method that removes duplicates from
-// any given str slice and then returns a nice duplicate free str slice
-func RemoveStringDuplicates(elements []string) []string {
- var encountered = map[string]bool{}
- var result []string
-
- for v := range elements {
- if !encountered[elements[v]] {
- encountered[elements[v]] = true
- result = append(result, elements[v])
- }
- }
-
- return result
-}
-
-// Contains checks the supplied slice of string for the existence
-// of a string and returns true if found, and false otherwise
-func Contains(elements []string, needle string) bool {
- for _, v := range elements {
- if needle == v {
- return true
- }
- }
-
- return false
-}
-
-// PermuteCase given a str returns a slice containing all possible case permutations
-// of that str such that input of foo will return
-// foo Foo fOo FOo foO FoO fOO FOO
-// Note that very long inputs can produce an enormous amount of
-// results in the returned slice OR result in an overflow and return nothing
-func PermuteCase(input string) []string {
- l := len(input)
- max := 1 << l
-
- var combinations []string
-
- for i := 0; i < max; i++ {
- s := ""
- for idx, ch := range input {
- if (i & (1 << idx)) == 0 {
- s += strings.ToUpper(string(ch))
- } else {
- s += strings.ToLower(string(ch))
- }
- }
-
- combinations = append(combinations, s)
- }
-
- return RemoveStringDuplicates(combinations)
-}
-
-// PermuteCaseFolding given a str returns a slice containing all possible case permutations
-// with characters being folded such that S will return S s ſ
-func PermuteCaseFolding(input string) []string {
- combinations := PermuteCase(input)
- var combos []string
-
- for _, combo := range combinations {
- for index, runeValue := range combo {
- for _, p := range AllSimpleFold(runeValue) {
- combos = append(combos, combo[:index]+string(p)+combo[index+len(string(runeValue)):])
- }
- }
- }
-
- return RemoveStringDuplicates(combos)
-}
-
-// AllSimpleFold given an input rune return a rune slice containing
-// all of the possible simple fold
-func AllSimpleFold(input rune) []rune {
- var res []rune
-
- // This works for getting all folded representations
- // but feels totally wrong due to the bailout break.
- // That said its simpler than a while with checks
- // Investigate https://github.com/golang/go/blob/master/src/regexp/syntax/prog.go#L215 as a possible way to implement
- for i := 0; i < 255; i++ {
- input = unicode.SimpleFold(input)
- if containsRune(res, input) {
- break
- }
- res = append(res, input)
- }
-
- return res
-}
-
-func containsRune(elements []rune, needle rune) bool {
- for _, v := range elements {
- if needle == v {
- return true
- }
- }
-
- return false
-}
-
-// IsSpace checks bytes MUST which be UTF-8 encoded for a space
-// List of spaces detected (same as unicode.IsSpace):
-// '\t', '\n', '\v', '\f', '\r', ' ', U+0085 (NEL), U+00A0 (NBSP).
-// N.B only two bytes are required for these cases. If we decided
-// to support spaces like ',' then we'll need more bytes.
-func IsSpace(firstByte, nextByte byte) bool {
- switch {
- case (9 <= firstByte) && (firstByte <= 13): // \t, \n, \f, \r
- return true
- case firstByte == 32: // SPACE
- return true
- case firstByte == 194:
- if nextByte == 133 { // NEL
- return true
- } else if nextByte == 160 { // NBSP
- return true
- }
- }
- return false
-}
-
-// StartOfRune a byte and returns true if its the start of a multibyte
-// character or a single byte character otherwise false
-func StartOfRune(b byte) bool {
- return (b < (0b1 << 7)) || ((0b11 << 6) < b)
-}
diff --git a/toolkit/stringutils/common_test.go b/toolkit/stringutils/common_test.go
deleted file mode 100644
index 355f3014..00000000
--- a/toolkit/stringutils/common_test.go
+++ /dev/null
@@ -1,161 +0,0 @@
-// SPDX-License-Identifier: MIT OR Unlicense
-
-package stringutils
-
-import (
- "testing"
-)
-
-func TestRemoveStringDuplicates(t *testing.T) {
- r := []string{"test", "test"}
-
- if len(RemoveStringDuplicates(r)) != 1 {
- t.Error("Expected a single return")
- }
-}
-
-func TestPermuteCase(t *testing.T) {
- permutations := PermuteCase("fo")
-
- if len(permutations) != 4 {
- t.Error("Expected 4 returns")
- }
-}
-
-func TestPermuteCaseUnicode(t *testing.T) {
- permutations := PermuteCase("ȺȾ")
-
- if len(permutations) != 4 {
- t.Error("Expected 4 returns")
- }
-}
-
-func TestPermuteCaseUnicodeNoFolding(t *testing.T) {
- permutations := PermuteCase("ſ")
-
- if len(permutations) != 2 {
- t.Error("Expected 2 returns")
- }
-}
-
-func TestAllSimpleFoldAsciiNumber(t *testing.T) {
- folded := AllSimpleFold('1')
-
- if len(folded) != 1 {
- t.Error("Should get 1 result")
- }
-}
-
-func TestAllSimpleFoldAsciiLetter(t *testing.T) {
- folded := AllSimpleFold('z')
-
- if len(folded) != 2 {
- t.Error("Should get 2 results")
- }
-}
-
-func TestAllSimpleFoldMultipleReturn(t *testing.T) {
- folded := AllSimpleFold('ſ')
-
- if len(folded) != 3 {
- t.Error("Should get 3 results")
- }
-}
-
-func TestAllSimpleFoldNotFullFold(t *testing.T) {
- // ß (assuming I copied the lowercase one)
- // can with full fold rules turn into SS
- // https://www.w3.org/TR/charmod-norm/#definitionCaseFolding
- // however in this case its a simple fold
- // so we would not expect that
- folded := AllSimpleFold('ß')
-
- if len(folded) != 2 {
- t.Error("Should get 2 results")
- }
-}
-
-func TestPermuteCaseFoldingUnicodeNoFolding(t *testing.T) {
- permutations := PermuteCaseFolding("ſ")
-
- if len(permutations) != 3 {
- t.Error("Expected 3 returns")
- }
-}
-
-func TestPermuteCaseFolding(t *testing.T) {
- folded := PermuteCaseFolding("nſ")
-
- if len(folded) != 6 {
- t.Error("Should get 6 results got", len(folded))
- }
-}
-
-func TestPermuteCaseFoldingNumbers(t *testing.T) {
- folded := PermuteCaseFolding("07123E1")
-
- if len(folded) != 2 {
- t.Error("Should get 2 results got", len(folded))
- }
-}
-
-func TestPermuteCaseFoldingComparison(t *testing.T) {
- r1 := PermuteCase("groß")
- r2 := PermuteCaseFolding("groß")
-
- if len(r1) >= len(r2) {
- t.Error("Should not be of equal length")
- }
-}
-
-func TestIsSpace(t *testing.T) {
- var cases = []struct {
- b1, b2 byte
- want bool
- }{
- // True cases
- {'\t', 'a', true},
- {'\n', 'a', true},
- {'\v', 'a', true},
- {'\f', 'a', true},
- {'\r', 'a', true},
- {' ', 'a', true},
- {'\xc2', '\x85', true}, // NEL
- {'\xc2', '\xa0', true}, // NBSP
- // False cases
- {'a', '\t', false},
- {byte(234), 'a', false},
- {byte(8), ' ', false},
- {'\xc2', byte(84), false},
- {'\xc2', byte(9), false},
- }
-
- for _, c := range cases {
- if got := IsSpace(c.b1, c.b2); got != c.want {
- t.Error("Expected", c.want, "got", got, ":", c.b1, c.b2)
- }
- }
-}
-
-func TestStartOfRune(t *testing.T) {
- var cases = []struct {
- bs []byte
- idx int
- want bool
- }{
- {[]byte("yo"), 1, true},
- {[]byte("τoρνoς"), 0, true},
- {[]byte("τoρνoς"), 1, false},
- {[]byte("τoρνoς"), 2, true},
- {[]byte("🍺"), 0, true},
- {[]byte("🍺"), 1, false},
- {[]byte("🍺"), 2, false},
- {[]byte("🍺"), 3, false},
- }
-
- for _, c := range cases {
- if got := StartOfRune(c.bs[c.idx]); got != c.want {
- t.Error("[", string(c.bs), c.idx, "]", "Expected:", c.want, "got", got)
- }
- }
-}
diff --git a/toolkit/stringutils/constants_test.go b/toolkit/stringutils/constants_test.go
deleted file mode 100644
index b83abf1f..00000000
--- a/toolkit/stringutils/constants_test.go
+++ /dev/null
@@ -1,14597 +0,0 @@
-// SPDX-License-Identifier: MIT OR Unlicense
-
-package stringutils
-
-var prideAndPrejudice = `
-The Project Gutenberg EBook of Pride and Prejudice, by Jane Austen
-
-This eBook is for the use of anyone anywhere at no cost and with
-almost no restrictions whatsoever. You may copy it, give it away or
-re-use it under the terms of the Project Gutenberg License included
-with this eBook or online at www.gutenberg.org
-
-
-Title: Pride and Prejudice
-
-Author: Jane Austen
-
-Release Date: August 26, 2008 [EBook #1342]
-Last Updated: November 12, 2019
-
-
-Language: English
-
-Character set encoding: UTF-8
-
-*** START OF THIS PROJECT GUTENBERG EBOOK PRIDE AND PREJUDICE ***
-
-
-
-
-Produced by Anonymous Volunteers, and David Widger
-
-THERE IS AN ILLUSTRATED EDITION OF THIS TITLE WHICH MAY VIEWED AT EBOOK
-[# 42671 ]
-
-cover
-
-
-
-
- Pride and Prejudice
-
- By Jane Austen
-
- CONTENTS
-
- Chapter 1
-
- Chapter 2
-
- Chapter 3
-
- Chapter 4
-
- Chapter 5
-
- Chapter 6
-
- Chapter 7
-
- Chapter 8
-
- Chapter 9
-
- Chapter 10
-
- Chapter 11
-
- Chapter 12
-
- Chapter 13
-
- Chapter 14
-
- Chapter 15
-
- Chapter 16
-
- Chapter 17
-
- Chapter 18
-
- Chapter 19
-
- Chapter 20
-
- Chapter 21
-
- Chapter 22
-
- Chapter 23
-
- Chapter 24
-
- Chapter 25
-
- Chapter 26
-
- Chapter 27
-
- Chapter 28
-
- Chapter 29
-
- Chapter 30
-
- Chapter 31
-
- Chapter 32
-
- Chapter 33
-
- Chapter 34
-
- Chapter 35
-
- Chapter 36
-
- Chapter 37
-
- Chapter 38
-
- Chapter 39
-
- Chapter 40
-
- Chapter 41
-
- Chapter 42
-
- Chapter 43
-
- Chapter 44
-
- Chapter 45
-
- Chapter 46
-
- Chapter 47
-
- Chapter 48
-
- Chapter 49
-
- Chapter 50
-
- Chapter 51
-
- Chapter 52
-
- Chapter 53
-
- Chapter 54
-
- Chapter 55
-
- Chapter 56
-
- Chapter 57
-
- Chapter 58
-
- Chapter 59
-
- Chapter 60
-
- Chapter 61
-
-
-
-
- Chapter 1
-
- It is a truth universally acknowledged, that a single man in
- possession of a good fortune, must be in want of a wife.
-
- However little known the feelings or views of such a man may be
- on his first entering a neighbourhood, this truth is so well
- fixed in the minds of the surrounding families, that he is
- considered the rightful property of some one or other of their
- daughters.
-
- “My dear Mr. Bennet,” said his lady to him one day, “have you
- heard that Netherfield Park is let at last?”
-
- Mr. Bennet replied that he had not.
-
- “But it is,” returned she; “for Mrs. Long has just been here, and
- she told me all about it.”
-
- Mr. Bennet made no answer.
-
- “Do you not want to know who has taken it?” cried his wife
- impatiently.
-
- “_You_ want to tell me, and I have no objection to hearing it.”
-
- This was invitation enough.
-
- “Why, my dear, you must know, Mrs. Long says that Netherfield is
- taken by a young man of large fortune from the north of England;
- that he came down on Monday in a chaise and four to see the
- place, and was so much delighted with it, that he agreed with Mr.
- Morris immediately; that he is to take possession before
- Michaelmas, and some of his servants are to be in the house by
- the end of next week.”
-
- “What is his name?”
-
- “Bingley.”
-
- “Is he married or single?”
-
- “Oh! Single, my dear, to be sure! A single man of large fortune;
- four or five thousand a year. What a fine thing for our girls!”
-
- “How so? How can it affect them?”
-
- “My dear Mr. Bennet,” replied his wife, “how can you be so
- tiresome! You must know that I am thinking of his marrying one of
- them.”
-
- “Is that his design in settling here?”
-
- “Design! Nonsense, how can you talk so! But it is very likely
- that he _may_ fall in love with one of them, and therefore you
- must visit him as soon as he comes.”
-
- “I see no occasion for that. You and the girls may go, or you may
- send them by themselves, which perhaps will be still better, for
- as you are as handsome as any of them, Mr. Bingley may like you
- the best of the party.”
-
- “My dear, you flatter me. I certainly _have_ had my share of
- beauty, but I do not pretend to be anything extraordinary now.
- When a woman has five grown-up daughters, she ought to give over
- thinking of her own beauty.”
-
- “In such cases, a woman has not often much beauty to think of.”
-
- “But, my dear, you must indeed go and see Mr. Bingley when he
- comes into the neighbourhood.”
-
- “It is more than I engage for, I assure you.”
-
- “But consider your daughters. Only think what an establishment it
- would be for one of them. Sir William and Lady Lucas are
- determined to go, merely on that account, for in general, you
- know, they visit no newcomers. Indeed you must go, for it will be
- impossible for _us_ to visit him if you do not.”
-
- “You are over-scrupulous, surely. I dare say Mr. Bingley will be
- very glad to see you; and I will send a few lines by you to
- assure him of my hearty consent to his marrying whichever he
- chooses of the girls; though I must throw in a good word for my
- little Lizzy.”
-
- “I desire you will do no such thing. Lizzy is not a bit better
- than the others; and I am sure she is not half so handsome as
- Jane, nor half so good-humoured as Lydia. But you are always
- giving _her_ the preference.”
-
- “They have none of them much to recommend them,” replied he;
- “they are all silly and ignorant like other girls; but Lizzy has
- something more of quickness than her sisters.”
-
- “Mr. Bennet, how can you abuse your own children in such a way?
- You take delight in vexing me. You have no compassion for my poor
- nerves.”
-
- “You mistake me, my dear. I have a high respect for your nerves.
- They are my old friends. I have heard you mention them with
- consideration these last twenty years at least.”
-
- “Ah, you do not know what I suffer.”
-
- “But I hope you will get over it, and live to see many young men
- of four thousand a year come into the neighbourhood.”
-
- “It will be no use to us, if twenty such should come, since you
- will not visit them.”
-
- “Depend upon it, my dear, that when there are twenty, I will
- visit them all.”
-
- Mr. Bennet was so odd a mixture of quick parts, sarcastic humour,
- reserve, and caprice, that the experience of three-and-twenty
- years had been insufficient to make his wife understand his
- character. _Her_ mind was less difficult to develop. She was a
- woman of mean understanding, little information, and uncertain
- temper. When she was discontented, she fancied herself nervous.
- The business of her life was to get her daughters married; its
- solace was visiting and news.
-
-
-
-
- Chapter 2
-
- Mr. Bennet was among the earliest of those who waited on Mr.
- Bingley. He had always intended to visit him, though to the last
- always assuring his wife that he should not go; and till the
- evening after the visit was paid she had no knowledge of it. It
- was then disclosed in the following manner. Observing his second
- daughter employed in trimming a hat, he suddenly addressed her
- with:
-
- “I hope Mr. Bingley will like it, Lizzy.”
-
- “We are not in a way to know _what_ Mr. Bingley likes,” said her
- mother resentfully, “since we are not to visit.”
-
- “But you forget, mamma,” said Elizabeth, “that we shall meet him
- at the assemblies, and that Mrs. Long promised to introduce him.”
-
- “I do not believe Mrs. Long will do any such thing. She has two
- nieces of her own. She is a selfish, hypocritical woman, and I
- have no opinion of her.”
-
- “No more have I,” said Mr. Bennet; “and I am glad to find that
- you do not depend on her serving you.”
-
- Mrs. Bennet deigned not to make any reply, but, unable to contain
- herself, began scolding one of her daughters.
-
- “Don’t keep coughing so, Kitty, for Heaven’s sake! Have a little
- compassion on my nerves. You tear them to pieces.”
-
- “Kitty has no discretion in her coughs,” said her father; “she
- times them ill.”
-
- “I do not cough for my own amusement,” replied Kitty fretfully.
- “When is your next ball to be, Lizzy?”
-
- “To-morrow fortnight.”
-
- “Aye, so it is,” cried her mother, “and Mrs. Long does not come
- back till the day before; so it will be impossible for her to
- introduce him, for she will not know him herself.”
-
- “Then, my dear, you may have the advantage of your friend, and
- introduce Mr. Bingley to _her_.”
-
- “Impossible, Mr. Bennet, impossible, when I am not acquainted
- with him myself; how can you be so teasing?”
-
- “I honour your circumspection. A fortnight’s acquaintance is
- certainly very little. One cannot know what a man really is by
- the end of a fortnight. But if _we_ do not venture somebody else
- will; and after all, Mrs. Long and her nieces must stand their
- chance; and, therefore, as she will think it an act of kindness,
- if you decline the office, I will take it on myself.”
-
- The girls stared at their father. Mrs. Bennet said only,
- “Nonsense, nonsense!”
-
- “What can be the meaning of that emphatic exclamation?” cried he.
- “Do you consider the forms of introduction, and the stress that
- is laid on them, as nonsense? I cannot quite agree with you
- _there_. What say you, Mary? For you are a young lady of deep
- reflection, I know, and read great books and make extracts.”
-
- Mary wished to say something sensible, but knew not how.
-
- “While Mary is adjusting her ideas,” he continued, “let us return
- to Mr. Bingley.”
-
- “I am sick of Mr. Bingley,” cried his wife.
-
- “I am sorry to hear _that_; but why did not you tell me that
- before? If I had known as much this morning I certainly would not
- have called on him. It is very unlucky; but as I have actually
- paid the visit, we cannot escape the acquaintance now.”
-
- The astonishment of the ladies was just what he wished; that of
- Mrs. Bennet perhaps surpassing the rest; though, when the first
- tumult of joy was over, she began to declare that it was what she
- had expected all the while.
-
- “How good it was in you, my dear Mr. Bennet! But I knew I should
- persuade you at last. I was sure you loved your girls too well to
- neglect such an acquaintance. Well, how pleased I am! and it is
- such a good joke, too, that you should have gone this morning and
- never said a word about it till now.”
-
- “Now, Kitty, you may cough as much as you choose,” said Mr.
- Bennet; and, as he spoke, he left the room, fatigued with the
- raptures of his wife.
-
- “What an excellent father you have, girls!” said she, when the
- door was shut. “I do not know how you will ever make him amends
- for his kindness; or me, either, for that matter. At our time of
- life it is not so pleasant, I can tell you, to be making new
- acquaintances every day; but for your sakes, we would do
- anything. Lydia, my love, though you _are_ the youngest, I dare
- say Mr. Bingley will dance with you at the next ball.”
-
- “Oh!” said Lydia stoutly, “I am not afraid; for though I _am_ the
- youngest, I’m the tallest.”
-
- The rest of the evening was spent in conjecturing how soon he
- would return Mr. Bennet’s visit, and determining when they should
- ask him to dinner.
-
-
-
-
- Chapter 3
-
- Not all that Mrs. Bennet, however, with the assistance of her
- five daughters, could ask on the subject, was sufficient to draw
- from her husband any satisfactory description of Mr. Bingley.
- They attacked him in various ways—with barefaced questions,
- ingenious suppositions, and distant surmises; but he eluded the
- skill of them all, and they were at last obliged to accept the
- second-hand intelligence of their neighbour, Lady Lucas. Her
- report was highly favourable. Sir William had been delighted with
- him. He was quite young, wonderfully handsome, extremely
- agreeable, and, to crown the whole, he meant to be at the next
- assembly with a large party. Nothing could be more delightful! To
- be fond of dancing was a certain step towards falling in love;
- and very lively hopes of Mr. Bingley’s heart were entertained.
-
- “If I can but see one of my daughters happily settled at
- Netherfield,” said Mrs. Bennet to her husband, “and all the
- others equally well married, I shall have nothing to wish for.”
-
- In a few days Mr. Bingley returned Mr. Bennet’s visit, and sat
- about ten minutes with him in his library. He had entertained
- hopes of being admitted to a sight of the young ladies, of whose
- beauty he had heard much; but he saw only the father. The ladies
- were somewhat more fortunate, for they had the advantage of
- ascertaining from an upper window that he wore a blue coat, and
- rode a black horse.
-
- An invitation to dinner was soon afterwards dispatched; and
- already had Mrs. Bennet planned the courses that were to do
- credit to her housekeeping, when an answer arrived which deferred
- it all. Mr. Bingley was obliged to be in town the following day,
- and, consequently, unable to accept the honour of their
- invitation, etc. Mrs. Bennet was quite disconcerted. She could
- not imagine what business he could have in town so soon after his
- arrival in Hertfordshire; and she began to fear that he might be
- always flying about from one place to another, and never settled
- at Netherfield as he ought to be. Lady Lucas quieted her fears a
- little by starting the idea of his being gone to London only to
- get a large party for the ball; and a report soon followed that
- Mr. Bingley was to bring twelve ladies and seven gentlemen with
- him to the assembly. The girls grieved over such a number of
- ladies, but were comforted the day before the ball by hearing,
- that instead of twelve he brought only six with him from
- London—his five sisters and a cousin. And when the party entered
- the assembly room it consisted of only five altogether—Mr.
- Bingley, his two sisters, the husband of the eldest, and another
- young man.
-
- Mr. Bingley was good-looking and gentlemanlike; he had a pleasant
- countenance, and easy, unaffected manners. His sisters were fine
- women, with an air of decided fashion. His brother-in-law, Mr.
- Hurst, merely looked the gentleman; but his friend Mr. Darcy soon
- drew the attention of the room by his fine, tall person, handsome
- features, noble mien, and the report which was in general
- circulation within five minutes after his entrance, of his having
- ten thousand a year. The gentlemen pronounced him to be a fine
- figure of a man, the ladies declared he was much handsomer than
- Mr. Bingley, and he was looked at with great admiration for about
- half the evening, till his manners gave a disgust which turned
- the tide of his popularity; for he was discovered to be proud; to
- be above his company, and above being pleased; and not all his
- large estate in Derbyshire could then save him from having a most
- forbidding, disagreeable countenance, and being unworthy to be
- compared with his friend.
-
- Mr. Bingley had soon made himself acquainted with all the
- principal people in the room; he was lively and unreserved,
- danced every dance, was angry that the ball closed so early, and
- talked of giving one himself at Netherfield. Such amiable
- qualities must speak for themselves. What a contrast between him
- and his friend! Mr. Darcy danced only once with Mrs. Hurst and
- once with Miss Bingley, declined being introduced to any other
- lady, and spent the rest of the evening in walking about the
- room, speaking occasionally to one of his own party. His
- character was decided. He was the proudest, most disagreeable man
- in the world, and everybody hoped that he would never come there
- again. Amongst the most violent against him was Mrs. Bennet,
- whose dislike of his general behaviour was sharpened into
- particular resentment by his having slighted one of her
- daughters.
-
- Elizabeth Bennet had been obliged, by the scarcity of gentlemen,
- to sit down for two dances; and during part of that time, Mr.
- Darcy had been standing near enough for her to hear a
- conversation between him and Mr. Bingley, who came from the dance
- for a few minutes, to press his friend to join it.
-
- “Come, Darcy,” said he, “I must have you dance. I hate to see you
- standing about by yourself in this stupid manner. You had much
- better dance.”
-
- “I certainly shall not. You know how I detest it, unless I am
- particularly acquainted with my partner. At such an assembly as
- this it would be insupportable. Your sisters are engaged, and
- there is not another woman in the room whom it would not be a
- punishment to me to stand up with.”
-
- “I would not be so fastidious as you are,” cried Mr. Bingley,
- “for a kingdom! Upon my honour, I never met with so many pleasant
- girls in my life as I have this evening; and there are several of
- them you see uncommonly pretty.”
-
- “_You_ are dancing with the only handsome girl in the room,” said
- Mr. Darcy, looking at the eldest Miss Bennet.
-
- “Oh! She is the most beautiful creature I ever beheld! But there
- is one of her sisters sitting down just behind you, who is very
- pretty, and I dare say very agreeable. Do let me ask my partner
- to introduce you.”
-
- “Which do you mean?” and turning round he looked for a moment at
- Elizabeth, till catching her eye, he withdrew his own and coldly
- said: “She is tolerable, but not handsome enough to tempt _me_; I
- am in no humour at present to give consequence to young ladies
- who are slighted by other men. You had better return to your
- partner and enjoy her smiles, for you are wasting your time with
- me.”
-
- Mr. Bingley followed his advice. Mr. Darcy walked off; and
- Elizabeth remained with no very cordial feelings toward him. She
- told the story, however, with great spirit among her friends; for
- she had a lively, playful disposition, which delighted in
- anything ridiculous.
-
- The evening altogether passed off pleasantly to the whole family.
- Mrs. Bennet had seen her eldest daughter much admired by the
- Netherfield party. Mr. Bingley had danced with her twice, and she
- had been distinguished by his sisters. Jane was as much gratified
- by this as her mother could be, though in a quieter way.
- Elizabeth felt Jane’s pleasure. Mary had heard herself mentioned
- to Miss Bingley as the most accomplished girl in the
- neighbourhood; and Catherine and Lydia had been fortunate enough
- never to be without partners, which was all that they had yet
- learnt to care for at a ball. They returned, therefore, in good
- spirits to Longbourn, the village where they lived, and of which
- they were the principal inhabitants. They found Mr. Bennet still
- up. With a book he was regardless of time; and on the present
- occasion he had a good deal of curiosity as to the event of an
- evening which had raised such splendid expectations. He had
- rather hoped that his wife’s views on the stranger would be
- disappointed; but he soon found out that he had a different story
- to hear.
-
- “Oh, my dear Mr. Bennet,” as she entered the room, “we have had a
- most delightful evening, a most excellent ball. I wish you had
- been there. Jane was so admired, nothing could be like it.
- Everybody said how well she looked; and Mr. Bingley thought her
- quite beautiful, and danced with her twice! Only think of _that_,
- my dear; he actually danced with her twice! and she was the only
- creature in the room that he asked a second time. First of all,
- he asked Miss Lucas. I was so vexed to see him stand up with her!
- But, however, he did not admire her at all; indeed, nobody can,
- you know; and he seemed quite struck with Jane as she was going
- down the dance. So he inquired who she was, and got introduced,
- and asked her for the two next. Then the two third he danced with
- Miss King, and the two fourth with Maria Lucas, and the two fifth
- with Jane again, and the two sixth with Lizzy, and the
- _Boulanger_—”
-
- “If he had had any compassion for _me_,” cried her husband
- impatiently, “he would not have danced half so much! For God’s
- sake, say no more of his partners. Oh that he had sprained his
- ankle in the first dance!”
-
- “Oh! my dear, I am quite delighted with him. He is so excessively
- handsome! And his sisters are charming women. I never in my life
- saw anything more elegant than their dresses. I dare say the lace
- upon Mrs. Hurst’s gown—”
-
- Here she was interrupted again. Mr. Bennet protested against any
- description of finery. She was therefore obliged to seek another
- branch of the subject, and related, with much bitterness of
- spirit and some exaggeration, the shocking rudeness of Mr. Darcy.
-
- “But I can assure you,” she added, “that Lizzy does not lose much
- by not suiting _his_ fancy; for he is a most disagreeable, horrid
- man, not at all worth pleasing. So high and so conceited that
- there was no enduring him! He walked here, and he walked there,
- fancying himself so very great! Not handsome enough to dance
- with! I wish you had been there, my dear, to have given him one
- of your set-downs. I quite detest the man.”
-
-
-
-
- Chapter 4
-
- When Jane and Elizabeth were alone, the former, who had been
- cautious in her praise of Mr. Bingley before, expressed to her
- sister just how very much she admired him.
-
- “He is just what a young man ought to be,” said she, “sensible,
- good-humoured, lively; and I never saw such happy manners!—so
- much ease, with such perfect good breeding!”
-
- “He is also handsome,” replied Elizabeth, “which a young man
- ought likewise to be, if he possibly can. His character is
- thereby complete.”
-
- “I was very much flattered by his asking me to dance a second
- time. I did not expect such a compliment.”
-
- “Did not you? _I_ did for you. But that is one great difference
- between us. Compliments always take _you_ by surprise, and _me_
- never. What could be more natural than his asking you again? He
- could not help seeing that you were about five times as pretty as
- every other woman in the room. No thanks to his gallantry for
- that. Well, he certainly is very agreeable, and I give you leave
- to like him. You have liked many a stupider person.”
-
- “Dear Lizzy!”
-
- “Oh! you are a great deal too apt, you know, to like people in
- general. You never see a fault in anybody. All the world are good
- and agreeable in your eyes. I never heard you speak ill of a
- human being in your life.”
-
- “I would not wish to be hasty in censuring anyone; but I always
- speak what I think.”
-
- “I know you do; and it is _that_ which makes the wonder. With
- _your_ good sense, to be so honestly blind to the follies and
- nonsense of others! Affectation of candour is common enough—one
- meets with it everywhere. But to be candid without ostentation or
- design—to take the good of everybody’s character and make it
- still better, and say nothing of the bad—belongs to you alone.
- And so you like this man’s sisters, too, do you? Their manners
- are not equal to his.”
-
- “Certainly not—at first. But they are very pleasing women when
- you converse with them. Miss Bingley is to live with her brother,
- and keep his house; and I am much mistaken if we shall not find a
- very charming neighbour in her.”
-
- Elizabeth listened in silence, but was not convinced; their
- behaviour at the assembly had not been calculated to please in
- general; and with more quickness of observation and less pliancy
- of temper than her sister, and with a judgement too unassailed by
- any attention to herself, she was very little disposed to approve
- them. They were in fact very fine ladies; not deficient in good
- humour when they were pleased, nor in the power of making
- themselves agreeable when they chose it, but proud and conceited.
- They were rather handsome, had been educated in one of the first
- private seminaries in town, had a fortune of twenty thousand
- pounds, were in the habit of spending more than they ought, and
- of associating with people of rank, and were therefore in every
- respect entitled to think well of themselves, and meanly of
- others. They were of a respectable family in the north of
- England; a circumstance more deeply impressed on their memories
- than that their brother’s fortune and their own had been acquired
- by trade.
-
- Mr. Bingley inherited property to the amount of nearly a hundred
- thousand pounds from his father, who had intended to purchase an
- estate, but did not live to do it. Mr. Bingley intended it
- likewise, and sometimes made choice of his county; but as he was
- now provided with a good house and the liberty of a manor, it was
- doubtful to many of those who best knew the easiness of his
- temper, whether he might not spend the remainder of his days at
- Netherfield, and leave the next generation to purchase.
-
- His sisters were anxious for his having an estate of his own;
- but, though he was now only established as a tenant, Miss Bingley
- was by no means unwilling to preside at his table—nor was Mrs.
- Hurst, who had married a man of more fashion than fortune, less
- disposed to consider his house as her home when it suited her.
- Mr. Bingley had not been of age two years, when he was tempted by
- an accidental recommendation to look at Netherfield House. He did
- look at it, and into it for half-an-hour—was pleased with the
- situation and the principal rooms, satisfied with what the owner
- said in its praise, and took it immediately.
-
- Between him and Darcy there was a very steady friendship, in
- spite of great opposition of character. Bingley was endeared to
- Darcy by the easiness, openness, and ductility of his temper,
- though no disposition could offer a greater contrast to his own,
- and though with his own he never appeared dissatisfied. On the
- strength of Darcy’s regard, Bingley had the firmest reliance, and
- of his judgement the highest opinion. In understanding, Darcy was
- the superior. Bingley was by no means deficient, but Darcy was
- clever. He was at the same time haughty, reserved, and
- fastidious, and his manners, though well-bred, were not inviting.
- In that respect his friend had greatly the advantage. Bingley was
- sure of being liked wherever he appeared, Darcy was continually
- giving offense.
-
- The manner in which they spoke of the Meryton assembly was
- sufficiently characteristic. Bingley had never met with more
- pleasant people or prettier girls in his life; everybody had been
- most kind and attentive to him; there had been no formality, no
- stiffness; he had soon felt acquainted with all the room; and, as
- to Miss Bennet, he could not conceive an angel more beautiful.
- Darcy, on the contrary, had seen a collection of people in whom
- there was little beauty and no fashion, for none of whom he had
- felt the smallest interest, and from none received either
- attention or pleasure. Miss Bennet he acknowledged to be pretty,
- but she smiled too much.
-
- Mrs. Hurst and her sister allowed it to be so—but still they
- admired her and liked her, and pronounced her to be a sweet girl,
- and one whom they would not object to know more of. Miss Bennet
- was therefore established as a sweet girl, and their brother felt
- authorized by such commendation to think of her as he chose.
-
-
-
-
- Chapter 5
-
- Within a short walk of Longbourn lived a family with whom the
- Bennets were particularly intimate. Sir William Lucas had been
- formerly in trade in Meryton, where he had made a tolerable
- fortune, and risen to the honour of knighthood by an address to
- the king during his mayoralty. The distinction had perhaps been
- felt too strongly. It had given him a disgust to his business,
- and to his residence in a small market town; and, in quitting
- them both, he had removed with his family to a house about a mile
- from Meryton, denominated from that period Lucas Lodge, where he
- could think with pleasure of his own importance, and, unshackled
- by business, occupy himself solely in being civil to all the
- world. For, though elated by his rank, it did not render him
- supercilious; on the contrary, he was all attention to everybody.
- By nature inoffensive, friendly, and obliging, his presentation
- at St. James’s had made him courteous.
-
- Lady Lucas was a very good kind of woman, not too clever to be a
- valuable neighbour to Mrs. Bennet. They had several children. The
- eldest of them, a sensible, intelligent young woman, about
- twenty-seven, was Elizabeth’s intimate friend.
-
- That the Miss Lucases and the Miss Bennets should meet to talk
- over a ball was absolutely necessary; and the morning after the
- assembly brought the former to Longbourn to hear and to
- communicate.
-
- “_You_ began the evening well, Charlotte,” said Mrs. Bennet with
- civil self-command to Miss Lucas. “_You_ were Mr. Bingley’s first
- choice.”
-
- “Yes; but he seemed to like his second better.”
-
- “Oh! you mean Jane, I suppose, because he danced with her twice.
- To be sure that _did_ seem as if he admired her—indeed I rather
- believe he _did_—I heard something about it—but I hardly know
- what—something about Mr. Robinson.”
-
- “Perhaps you mean what I overheard between him and Mr. Robinson;
- did not I mention it to you? Mr. Robinson’s asking him how he
- liked our Meryton assemblies, and whether he did not think there
- were a great many pretty women in the room, and _which_ he
- thought the prettiest? and his answering immediately to the last
- question: ‘Oh! the eldest Miss Bennet, beyond a doubt; there
- cannot be two opinions on that point.’”
-
- “Upon my word! Well, that is very decided indeed—that does seem
- as if—but, however, it may all come to nothing, you know.”
-
- “_My_ overhearings were more to the purpose than _yours_, Eliza,”
- said Charlotte. “Mr. Darcy is not so well worth listening to as
- his friend, is he?—poor Eliza!—to be only just _tolerable_.”
-
- “I beg you would not put it into Lizzy’s head to be vexed by his
- ill-treatment, for he is such a disagreeable man, that it would
- be quite a misfortune to be liked by him. Mrs. Long told me last
- night that he sat close to her for half-an-hour without once
- opening his lips.”
-
- “Are you quite sure, ma’am?—is not there a little mistake?” said
- Jane. “I certainly saw Mr. Darcy speaking to her.”
-
- “Aye—because she asked him at last how he liked Netherfield, and
- he could not help answering her; but she said he seemed quite
- angry at being spoke to.”
-
- “Miss Bingley told me,” said Jane, “that he never speaks much,
- unless among his intimate acquaintances. With _them_ he is
- remarkably agreeable.”
-
- “I do not believe a word of it, my dear. If he had been so very
- agreeable, he would have talked to Mrs. Long. But I can guess how
- it was; everybody says that he is eat up with pride, and I dare
- say he had heard somehow that Mrs. Long does not keep a carriage,
- and had come to the ball in a hack chaise.”
-
- “I do not mind his not talking to Mrs. Long,” said Miss Lucas,
- “but I wish he had danced with Eliza.”
-
- “Another time, Lizzy,” said her mother, “I would not dance with
- _him_, if I were you.”
-
- “I believe, ma’am, I may safely promise you _never_ to dance with
- him.”
-
- “His pride,” said Miss Lucas, “does not offend _me_ so much as
- pride often does, because there is an excuse for it. One cannot
- wonder that so very fine a young man, with family, fortune,
- everything in his favour, should think highly of himself. If I
- may so express it, he has a _right_ to be proud.”
-
- “That is very true,” replied Elizabeth, “and I could easily
- forgive _his_ pride, if he had not mortified _mine_.”
-
- “Pride,” observed Mary, who piqued herself upon the solidity of
- her reflections, “is a very common failing, I believe. By all
- that I have ever read, I am convinced that it is very common
- indeed; that human nature is particularly prone to it, and that
- there are very few of us who do not cherish a feeling of
- self-complacency on the score of some quality or other, real or
- imaginary. Vanity and pride are different things, though the
- words are often used synonymously. A person may be proud without
- being vain. Pride relates more to our opinion of ourselves,
- vanity to what we would have others think of us.”
-
- “If I were as rich as Mr. Darcy,” cried a young Lucas, who came
- with his sisters, “I should not care how proud I was. I would
- keep a pack of foxhounds, and drink a bottle of wine a day.”
-
- “Then you would drink a great deal more than you ought,” said
- Mrs. Bennet; “and if I were to see you at it, I should take away
- your bottle directly.”
-
- The boy protested that she should not; she continued to declare
- that she would, and the argument ended only with the visit.
-
-
-
-
- Chapter 6
-
- The ladies of Longbourn soon waited on those of Netherfield. The
- visit was soon returned in due form. Miss Bennet’s pleasing
- manners grew on the goodwill of Mrs. Hurst and Miss Bingley; and
- though the mother was found to be intolerable, and the younger
- sisters not worth speaking to, a wish of being better acquainted
- with _them_ was expressed towards the two eldest. By Jane, this
- attention was received with the greatest pleasure, but Elizabeth
- still saw superciliousness in their treatment of everybody,
- hardly excepting even her sister, and could not like them; though
- their kindness to Jane, such as it was, had a value as arising in
- all probability from the influence of their brother’s admiration.
- It was generally evident whenever they met, that he _did_ admire
- her and to _her_ it was equally evident that Jane was yielding to
- the preference which she had begun to entertain for him from the
- first, and was in a way to be very much in love; but she
- considered with pleasure that it was not likely to be discovered
- by the world in general, since Jane united, with great strength
- of feeling, a composure of temper and a uniform cheerfulness of
- manner which would guard her from the suspicions of the
- impertinent. She mentioned this to her friend Miss Lucas.
-
- “It may perhaps be pleasant,” replied Charlotte, “to be able to
- impose on the public in such a case; but it is sometimes a
- disadvantage to be so very guarded. If a woman conceals her
- affection with the same skill from the object of it, she may lose
- the opportunity of fixing him; and it will then be but poor
- consolation to believe the world equally in the dark. There is so
- much of gratitude or vanity in almost every attachment, that it
- is not safe to leave any to itself. We can all _begin_ freely—a
- slight preference is natural enough; but there are very few of us
- who have heart enough to be really in love without encouragement.
- In nine cases out of ten a woman had better show _more_ affection
- than she feels. Bingley likes your sister undoubtedly; but he may
- never do more than like her, if she does not help him on.”
-
- “But she does help him on, as much as her nature will allow. If
- _I_ can perceive her regard for him, he must be a simpleton,
- indeed, not to discover it too.”
-
- “Remember, Eliza, that he does not know Jane’s disposition as you
- do.”
-
- “But if a woman is partial to a man, and does not endeavour to
- conceal it, he must find it out.”
-
- “Perhaps he must, if he sees enough of her. But, though Bingley
- and Jane meet tolerably often, it is never for many hours
- together; and, as they always see each other in large mixed
- parties, it is impossible that every moment should be employed in
- conversing together. Jane should therefore make the most of every
- half-hour in which she can command his attention. When she is
- secure of him, there will be more leisure for falling in love as
- much as she chooses.”
-
- “Your plan is a good one,” replied Elizabeth, “where nothing is
- in question but the desire of being well married, and if I were
- determined to get a rich husband, or any husband, I dare say I
- should adopt it. But these are not Jane’s feelings; she is not
- acting by design. As yet, she cannot even be certain of the
- degree of her own regard nor of its reasonableness. She has known
- him only a fortnight. She danced four dances with him at Meryton;
- she saw him one morning at his own house, and has since dined
- with him in company four times. This is not quite enough to make
- her understand his character.”
-
- “Not as you represent it. Had she merely _dined_ with him, she
- might only have discovered whether he had a good appetite; but
- you must remember that four evenings have also been spent
- together—and four evenings may do a great deal.”
-
- “Yes; these four evenings have enabled them to ascertain that
- they both like Vingt-un better than Commerce; but with respect to
- any other leading characteristic, I do not imagine that much has
- been unfolded.”
-
- “Well,” said Charlotte, “I wish Jane success with all my heart;
- and if she were married to him to-morrow, I should think she had
- as good a chance of happiness as if she were to be studying his
- character for a twelvemonth. Happiness in marriage is entirely a
- matter of chance. If the dispositions of the parties are ever so
- well known to each other or ever so similar beforehand, it does
- not advance their felicity in the least. They always continue to
- grow sufficiently unlike afterwards to have their share of
- vexation; and it is better to know as little as possible of the
- defects of the person with whom you are to pass your life.”
-
- “You make me laugh, Charlotte; but it is not sound. You know it
- is not sound, and that you would never act in this way yourself.”
-
- Occupied in observing Mr. Bingley’s attentions to her sister,
- Elizabeth was far from suspecting that she was herself becoming
- an object of some interest in the eyes of his friend. Mr. Darcy
- had at first scarcely allowed her to be pretty; he had looked at
- her without admiration at the ball; and when they next met, he
- looked at her only to criticise. But no sooner had he made it
- clear to himself and his friends that she hardly had a good
- feature in her face, than he began to find it was rendered
- uncommonly intelligent by the beautiful expression of her dark
- eyes. To this discovery succeeded some others equally mortifying.
- Though he had detected with a critical eye more than one failure
- of perfect symmetry in her form, he was forced to acknowledge her
- figure to be light and pleasing; and in spite of his asserting
- that her manners were not those of the fashionable world, he was
- caught by their easy playfulness. Of this she was perfectly
- unaware; to her he was only the man who made himself agreeable
- nowhere, and who had not thought her handsome enough to dance
- with.
-
- He began to wish to know more of her, and as a step towards
- conversing with her himself, attended to her conversation with
- others. His doing so drew her notice. It was at Sir William
- Lucas’s, where a large party were assembled.
-
- “What does Mr. Darcy mean,” said she to Charlotte, “by listening
- to my conversation with Colonel Forster?”
-
- “That is a question which Mr. Darcy only can answer.”
-
- “But if he does it any more I shall certainly let him know that I
- see what he is about. He has a very satirical eye, and if I do
- not begin by being impertinent myself, I shall soon grow afraid
- of him.”
-
- On his approaching them soon afterwards, though without seeming
- to have any intention of speaking, Miss Lucas defied her friend
- to mention such a subject to him; which immediately provoking
- Elizabeth to do it, she turned to him and said:
-
- “Did you not think, Mr. Darcy, that I expressed myself uncommonly
- well just now, when I was teasing Colonel Forster to give us a
- ball at Meryton?”
-
- “With great energy; but it is always a subject which makes a lady
- energetic.”
-
- “You are severe on us.”
-
- “It will be _her_ turn soon to be teased,” said Miss Lucas. “I am
- going to open the instrument, Eliza, and you know what follows.”
-
- “You are a very strange creature by way of a friend!—always
- wanting me to play and sing before anybody and everybody! If my
- vanity had taken a musical turn, you would have been invaluable;
- but as it is, I would really rather not sit down before those who
- must be in the habit of hearing the very best performers.” On
- Miss Lucas’s persevering, however, she added, “Very well, if it
- must be so, it must.” And gravely glancing at Mr. Darcy, “There
- is a fine old saying, which everybody here is of course familiar
- with: ‘Keep your breath to cool your porridge’; and I shall keep
- mine to swell my song.”
-
- Her performance was pleasing, though by no means capital. After a
- song or two, and before she could reply to the entreaties of
- several that she would sing again, she was eagerly succeeded at
- the instrument by her sister Mary, who having, in consequence of
- being the only plain one in the family, worked hard for knowledge
- and accomplishments, was always impatient for display.
-
- Mary had neither genius nor taste; and though vanity had given
- her application, it had given her likewise a pedantic air and
- conceited manner, which would have injured a higher degree of
- excellence than she had reached. Elizabeth, easy and unaffected,
- had been listened to with much more pleasure, though not playing
- half so well; and Mary, at the end of a long concerto, was glad
- to purchase praise and gratitude by Scotch and Irish airs, at the
- request of her younger sisters, who, with some of the Lucases,
- and two or three officers, joined eagerly in dancing at one end
- of the room.
-
- Mr. Darcy stood near them in silent indignation at such a mode of
- passing the evening, to the exclusion of all conversation, and
- was too much engrossed by his thoughts to perceive that Sir
- William Lucas was his neighbour, till Sir William thus began:
-
- “What a charming amusement for young people this is, Mr. Darcy!
- There is nothing like dancing after all. I consider it as one of
- the first refinements of polished society.”
-
- “Certainly, sir; and it has the advantage also of being in vogue
- amongst the less polished societies of the world. Every savage
- can dance.”
-
- Sir William only smiled. “Your friend performs delightfully,” he
- continued after a pause, on seeing Bingley join the group; “and I
- doubt not that you are an adept in the science yourself, Mr.
- Darcy.”
-
- “You saw me dance at Meryton, I believe, sir.”
-
- “Yes, indeed, and received no inconsiderable pleasure from the
- sight. Do you often dance at St. James’s?”
-
- “Never, sir.”
-
- “Do you not think it would be a proper compliment to the place?”
-
- “It is a compliment which I never pay to any place if I can avoid
- it.”
-
- “You have a house in town, I conclude?”
-
- Mr. Darcy bowed.
-
- “I had once had some thought of fixing in town myself—for I am
- fond of superior society; but I did not feel quite certain that
- the air of London would agree with Lady Lucas.”
-
- He paused in hopes of an answer; but his companion was not
- disposed to make any; and Elizabeth at that instant moving
- towards them, he was struck with the action of doing a very
- gallant thing, and called out to her:
-
- “My dear Miss Eliza, why are you not dancing? Mr. Darcy, you must
- allow me to present this young lady to you as a very desirable
- partner. You cannot refuse to dance, I am sure when so much
- beauty is before you.” And, taking her hand, he would have given
- it to Mr. Darcy who, though extremely surprised, was not
- unwilling to receive it, when she instantly drew back, and said
- with some discomposure to Sir William:
-
- “Indeed, sir, I have not the least intention of dancing. I
- entreat you not to suppose that I moved this way in order to beg
- for a partner.”
-
- Mr. Darcy, with grave propriety, requested to be allowed the
- honour of her hand, but in vain. Elizabeth was determined; nor
- did Sir William at all shake her purpose by his attempt at
- persuasion.
-
- “You excel so much in the dance, Miss Eliza, that it is cruel to
- deny me the happiness of seeing you; and though this gentleman
- dislikes the amusement in general, he can have no objection, I am
- sure, to oblige us for one half-hour.”
-
- “Mr. Darcy is all politeness,” said Elizabeth, smiling.
-
- “He is, indeed; but, considering the inducement, my dear Miss
- Eliza, we cannot wonder at his complaisance—for who would object
- to such a partner?”
-
- Elizabeth looked archly, and turned away. Her resistance had not
- injured her with the gentleman, and he was thinking of her with
- some complacency, when thus accosted by Miss Bingley:
-
- “I can guess the subject of your reverie.”
-
- “I should imagine not.”
-
- “You are considering how insupportable it would be to pass many
- evenings in this manner—in such society; and indeed I am quite of
- your opinion. I was never more annoyed! The insipidity, and yet
- the noise—the nothingness, and yet the self-importance of all
- those people! What would I give to hear your strictures on them!”
-
- “Your conjecture is totally wrong, I assure you. My mind was more
- agreeably engaged. I have been meditating on the very great
- pleasure which a pair of fine eyes in the face of a pretty woman
- can bestow.”
-
- Miss Bingley immediately fixed her eyes on his face, and desired
- he would tell her what lady had the credit of inspiring such
- reflections. Mr. Darcy replied with great intrepidity:
-
- “Miss Elizabeth Bennet.”
-
- “Miss Elizabeth Bennet!” repeated Miss Bingley. “I am all
- astonishment. How long has she been such a favourite?—and pray,
- when am I to wish you joy?”
-
- “That is exactly the question which I expected you to ask. A
- lady’s imagination is very rapid; it jumps from admiration to
- love, from love to matrimony, in a moment. I knew you would be
- wishing me joy.”
-
- “Nay, if you are serious about it, I shall consider the matter is
- absolutely settled. You will be having a charming mother-in-law,
- indeed; and, of course, she will always be at Pemberley with
- you.”
-
- He listened to her with perfect indifference while she chose to
- entertain herself in this manner; and as his composure convinced
- her that all was safe, her wit flowed long.
-
-
-
-
- Chapter 7
-
- Mr. Bennet’s property consisted almost entirely in an estate of
- two thousand a year, which, unfortunately for his daughters, was
- entailed, in default of heirs male, on a distant relation; and
- their mother’s fortune, though ample for her situation in life,
- could but ill supply the deficiency of his. Her father had been
- an attorney in Meryton, and had left her four thousand pounds.
-
- She had a sister married to a Mr. Phillips, who had been a clerk
- to their father and succeeded him in the business, and a brother
- settled in London in a respectable line of trade.
-
- The village of Longbourn was only one mile from Meryton; a most
- convenient distance for the young ladies, who were usually
- tempted thither three or four times a week, to pay their duty to
- their aunt and to a milliner’s shop just over the way. The two
- youngest of the family, Catherine and Lydia, were particularly
- frequent in these attentions; their minds were more vacant than
- their sisters’, and when nothing better offered, a walk to
- Meryton was necessary to amuse their morning hours and furnish
- conversation for the evening; and however bare of news the
- country in general might be, they always contrived to learn some
- from their aunt. At present, indeed, they were well supplied both
- with news and happiness by the recent arrival of a militia
- regiment in the neighbourhood; it was to remain the whole winter,
- and Meryton was the headquarters.
-
- Their visits to Mrs. Phillips were now productive of the most
- interesting intelligence. Every day added something to their
- knowledge of the officers’ names and connections. Their lodgings
- were not long a secret, and at length they began to know the
- officers themselves. Mr. Phillips visited them all, and this
- opened to his nieces a store of felicity unknown before. They
- could talk of nothing but officers; and Mr. Bingley’s large
- fortune, the mention of which gave animation to their mother, was
- worthless in their eyes when opposed to the regimentals of an
- ensign.
-
- After listening one morning to their effusions on this subject,
- Mr. Bennet coolly observed:
-
- “From all that I can collect by your manner of talking, you must
- be two of the silliest girls in the country. I have suspected it
- some time, but I am now convinced.”
-
- Catherine was disconcerted, and made no answer; but Lydia, with
- perfect indifference, continued to express her admiration of
- Captain Carter, and her hope of seeing him in the course of the
- day, as he was going the next morning to London.
-
- “I am astonished, my dear,” said Mrs. Bennet, “that you should be
- so ready to think your own children silly. If I wished to think
- slightingly of anybody’s children, it should not be of my own,
- however.”
-
- “If my children are silly, I must hope to be always sensible of
- it.”
-
- “Yes—but as it happens, they are all of them very clever.”
-
- “This is the only point, I flatter myself, on which we do not
- agree. I had hoped that our sentiments coincided in every
- particular, but I must so far differ from you as to think our two
- youngest daughters uncommonly foolish.”
-
- “My dear Mr. Bennet, you must not expect such girls to have the
- sense of their father and mother. When they get to our age, I
- dare say they will not think about officers any more than we do.
- I remember the time when I liked a red coat myself very well—and,
- indeed, so I do still at my heart; and if a smart young colonel,
- with five or six thousand a year, should want one of my girls I
- shall not say nay to him; and I thought Colonel Forster looked
- very becoming the other night at Sir William’s in his
- regimentals.”
-
- “Mamma,” cried Lydia, “my aunt says that Colonel Forster and
- Captain Carter do not go so often to Miss Watson’s as they did
- when they first came; she sees them now very often standing in
- Clarke’s library.”
-
- Mrs. Bennet was prevented replying by the entrance of the footman
- with a note for Miss Bennet; it came from Netherfield, and the
- servant waited for an answer. Mrs. Bennet’s eyes sparkled with
- pleasure, and she was eagerly calling out, while her daughter
- read,
-
- “Well, Jane, who is it from? What is it about? What does he say?
- Well, Jane, make haste and tell us; make haste, my love.”
-
- “It is from Miss Bingley,” said Jane, and then read it aloud.
-
- “MY DEAR FRIEND,—
- “If you are not so compassionate as to dine to-day with Louisa
- and me, we shall be in danger of hating each other for the rest
- of our lives, for a whole day’s _tête-à-tête_ between two women
- can never end without a quarrel. Come as soon as you can on
- receipt of this. My brother and the gentlemen are to dine with
- the officers.—Yours ever,
-
- “CAROLINE BINGLEY”
-
- “With the officers!” cried Lydia. “I wonder my aunt did not tell
- us of _that_.”
-
- “Dining out,” said Mrs. Bennet, “that is very unlucky.”
-
- “Can I have the carriage?” said Jane.
-
- “No, my dear, you had better go on horseback, because it seems
- likely to rain; and then you must stay all night.”
-
- “That would be a good scheme,” said Elizabeth, “if you were sure
- that they would not offer to send her home.”
-
- “Oh! but the gentlemen will have Mr. Bingley’s chaise to go to
- Meryton, and the Hursts have no horses to theirs.”
-
- “I had much rather go in the coach.”
-
- “But, my dear, your father cannot spare the horses, I am sure.
- They are wanted in the farm, Mr. Bennet, are they not?”
-
- “They are wanted in the farm much oftener than I can get them.”
-
- “But if you have got them to-day,” said Elizabeth, “my mother’s
- purpose will be answered.”
-
- She did at last extort from her father an acknowledgment that the
- horses were engaged. Jane was therefore obliged to go on
- horseback, and her mother attended her to the door with many
- cheerful prognostics of a bad day. Her hopes were answered; Jane
- had not been gone long before it rained hard. Her sisters were
- uneasy for her, but her mother was delighted. The rain continued
- the whole evening without intermission; Jane certainly could not
- come back.
-
- “This was a lucky idea of mine, indeed!” said Mrs. Bennet more
- than once, as if the credit of making it rain were all her own.
- Till the next morning, however, she was not aware of all the
- felicity of her contrivance. Breakfast was scarcely over when a
- servant from Netherfield brought the following note for
- Elizabeth:
-
- “MY DEAREST LIZZY,—
- “I find myself very unwell this morning, which, I suppose, is to
- be imputed to my getting wet through yesterday. My kind friends
- will not hear of my returning till I am better. They insist also
- on my seeing Mr. Jones—therefore do not be alarmed if you should
- hear of his having been to me—and, excepting a sore throat and
- headache, there is not much the matter with me.—Yours, etc.”
-
- “Well, my dear,” said Mr. Bennet, when Elizabeth had read the
- note aloud, “if your daughter should have a dangerous fit of
- illness—if she should die, it would be a comfort to know that it
- was all in pursuit of Mr. Bingley, and under your orders.”
-
- “Oh! I am not afraid of her dying. People do not die of little
- trifling colds. She will be taken good care of. As long as she
- stays there, it is all very well. I would go and see her if I
- could have the carriage.”
-
- Elizabeth, feeling really anxious, was determined to go to her,
- though the carriage was not to be had; and as she was no
- horsewoman, walking was her only alternative. She declared her
- resolution.
-
- “How can you be so silly,” cried her mother, “as to think of such
- a thing, in all this dirt! You will not be fit to be seen when
- you get there.”
-
- “I shall be very fit to see Jane—which is all I want.”
-
- “Is this a hint to me, Lizzy,” said her father, “to send for the
- horses?”
-
- “No, indeed, I do not wish to avoid the walk. The distance is
- nothing when one has a motive; only three miles. I shall be back
- by dinner.”
-
- “I admire the activity of your benevolence,” observed Mary, “but
- every impulse of feeling should be guided by reason; and, in my
- opinion, exertion should always be in proportion to what is
- required.”
-
- “We will go as far as Meryton with you,” said Catherine and
- Lydia. Elizabeth accepted their company, and the three young
- ladies set off together.
-
- “If we make haste,” said Lydia, as they walked along, “perhaps we
- may see something of Captain Carter before he goes.”
-
- In Meryton they parted; the two youngest repaired to the lodgings
- of one of the officers’ wives, and Elizabeth continued her walk
- alone, crossing field after field at a quick pace, jumping over
- stiles and springing over puddles with impatient activity, and
- finding herself at last within view of the house, with weary
- ankles, dirty stockings, and a face glowing with the warmth of
- exercise.
-
- She was shown into the breakfast-parlour, where all but Jane were
- assembled, and where her appearance created a great deal of
- surprise. That she should have walked three miles so early in the
- day, in such dirty weather, and by herself, was almost incredible
- to Mrs. Hurst and Miss Bingley; and Elizabeth was convinced that
- they held her in contempt for it. She was received, however, very
- politely by them; and in their brother’s manners there was
- something better than politeness; there was good humour and
- kindness. Mr. Darcy said very little, and Mr. Hurst nothing at
- all. The former was divided between admiration of the brilliancy
- which exercise had given to her complexion, and doubt as to the
- occasion’s justifying her coming so far alone. The latter was
- thinking only of his breakfast.
-
- Her inquiries after her sister were not very favourably answered.
- Miss Bennet had slept ill, and though up, was very feverish, and
- not well enough to leave her room. Elizabeth was glad to be taken
- to her immediately; and Jane, who had only been withheld by the
- fear of giving alarm or inconvenience from expressing in her note
- how much she longed for such a visit, was delighted at her
- entrance. She was not equal, however, to much conversation, and
- when Miss Bingley left them together, could attempt little
- besides expressions of gratitude for the extraordinary kindness
- she was treated with. Elizabeth silently attended her.
-
- When breakfast was over they were joined by the sisters; and
- Elizabeth began to like them herself, when she saw how much
- affection and solicitude they showed for Jane. The apothecary
- came, and having examined his patient, said, as might be
- supposed, that she had caught a violent cold, and that they must
- endeavour to get the better of it; advised her to return to bed,
- and promised her some draughts. The advice was followed readily,
- for the feverish symptoms increased, and her head ached acutely.
- Elizabeth did not quit her room for a moment; nor were the other
- ladies often absent; the gentlemen being out, they had, in fact,
- nothing to do elsewhere.
-
- When the clock struck three, Elizabeth felt that she must go, and
- very unwillingly said so. Miss Bingley offered her the carriage,
- and she only wanted a little pressing to accept it, when Jane
- testified such concern in parting with her, that Miss Bingley was
- obliged to convert the offer of the chaise to an invitation to
- remain at Netherfield for the present. Elizabeth most thankfully
- consented, and a servant was dispatched to Longbourn to acquaint
- the family with her stay and bring back a supply of clothes.
-
-
-
-
- Chapter 8
-
- At five o’clock the two ladies retired to dress, and at half-past
- six Elizabeth was summoned to dinner. To the civil inquiries
- which then poured in, and amongst which she had the pleasure of
- distinguishing the much superior solicitude of Mr. Bingley’s, she
- could not make a very favourable answer. Jane was by no means
- better. The sisters, on hearing this, repeated three or four
- times how much they were grieved, how shocking it was to have a
- bad cold, and how excessively they disliked being ill themselves;
- and then thought no more of the matter: and their indifference
- towards Jane when not immediately before them restored Elizabeth
- to the enjoyment of all her former dislike.
-
- Their brother, indeed, was the only one of the party whom she
- could regard with any complacency. His anxiety for Jane was
- evident, and his attentions to herself most pleasing, and they
- prevented her feeling herself so much an intruder as she believed
- she was considered by the others. She had very little notice from
- any but him. Miss Bingley was engrossed by Mr. Darcy, her sister
- scarcely less so; and as for Mr. Hurst, by whom Elizabeth sat, he
- was an indolent man, who lived only to eat, drink, and play at
- cards; who, when he found her to prefer a plain dish to a ragout,
- had nothing to say to her.
-
- When dinner was over, she returned directly to Jane, and Miss
- Bingley began abusing her as soon as she was out of the room. Her
- manners were pronounced to be very bad indeed, a mixture of pride
- and impertinence; she had no conversation, no style, no beauty.
- Mrs. Hurst thought the same, and added:
-
- “She has nothing, in short, to recommend her, but being an
- excellent walker. I shall never forget her appearance this
- morning. She really looked almost wild.”
-
- “She did, indeed, Louisa. I could hardly keep my countenance.
- Very nonsensical to come at all! Why must _she_ be scampering
- about the country, because her sister had a cold? Her hair, so
- untidy, so blowsy!”
-
- “Yes, and her petticoat; I hope you saw her petticoat, six inches
- deep in mud, I am absolutely certain; and the gown which had been
- let down to hide it not doing its office.”
-
- “Your picture may be very exact, Louisa,” said Bingley; “but this
- was all lost upon me. I thought Miss Elizabeth Bennet looked
- remarkably well when she came into the room this morning. Her
- dirty petticoat quite escaped my notice.”
-
- “_You_ observed it, Mr. Darcy, I am sure,” said Miss Bingley;
- “and I am inclined to think that you would not wish to see _your
- sister_ make such an exhibition.”
-
- “Certainly not.”
-
- “To walk three miles, or four miles, or five miles, or whatever
- it is, above her ankles in dirt, and alone, quite alone! What
- could she mean by it? It seems to me to show an abominable sort
- of conceited independence, a most country-town indifference to
- decorum.”
-
- “It shows an affection for her sister that is very pleasing,”
- said Bingley.
-
- “I am afraid, Mr. Darcy,” observed Miss Bingley in a half
- whisper, “that this adventure has rather affected your admiration
- of her fine eyes.”
-
- “Not at all,” he replied; “they were brightened by the exercise.”
- A short pause followed this speech, and Mrs. Hurst began again:
-
- “I have an excessive regard for Miss Jane Bennet, she is really a
- very sweet girl, and I wish with all my heart she were well
- settled. But with such a father and mother, and such low
- connections, I am afraid there is no chance of it.”
-
- “I think I have heard you say that their uncle is an attorney in
- Meryton.”
-
- “Yes; and they have another, who lives somewhere near Cheapside.”
-
- “That is capital,” added her sister, and they both laughed
- heartily.
-
- “If they had uncles enough to fill _all_ Cheapside,” cried
- Bingley, “it would not make them one jot less agreeable.”
-
- “But it must very materially lessen their chance of marrying men
- of any consideration in the world,” replied Darcy.
-
- To this speech Bingley made no answer; but his sisters gave it
- their hearty assent, and indulged their mirth for some time at
- the expense of their dear friend’s vulgar relations.
-
- With a renewal of tenderness, however, they returned to her room
- on leaving the dining-parlour, and sat with her till summoned to
- coffee. She was still very poorly, and Elizabeth would not quit
- her at all, till late in the evening, when she had the comfort of
- seeing her sleep, and when it seemed to her rather right than
- pleasant that she should go downstairs herself. On entering the
- drawing-room she found the whole party at loo, and was
- immediately invited to join them; but suspecting them to be
- playing high she declined it, and making her sister the excuse,
- said she would amuse herself for the short time she could stay
- below, with a book. Mr. Hurst looked at her with astonishment.
-
- “Do you prefer reading to cards?” said he; “that is rather
- singular.”
-
- “Miss Eliza Bennet,” said Miss Bingley, “despises cards. She is a
- great reader, and has no pleasure in anything else.”
-
- “I deserve neither such praise nor such censure,” cried
- Elizabeth; “I am _not_ a great reader, and I have pleasure in
- many things.”
-
- “In nursing your sister I am sure you have pleasure,” said
- Bingley; “and I hope it will be soon increased by seeing her
- quite well.”
-
- Elizabeth thanked him from her heart, and then walked towards the
- table where a few books were lying. He immediately offered to
- fetch her others—all that his library afforded.
-
- “And I wish my collection were larger for your benefit and my own
- credit; but I am an idle fellow, and though I have not many, I
- have more than I ever looked into.”
-
- Elizabeth assured him that she could suit herself perfectly with
- those in the room.
-
- “I am astonished,” said Miss Bingley, “that my father should have
- left so small a collection of books. What a delightful library
- you have at Pemberley, Mr. Darcy!”
-
- “It ought to be good,” he replied, “it has been the work of many
- generations.”
-
- “And then you have added so much to it yourself, you are always
- buying books.”
-
- “I cannot comprehend the neglect of a family library in such days
- as these.”
-
- “Neglect! I am sure you neglect nothing that can add to the
- beauties of that noble place. Charles, when you build _your_
- house, I wish it may be half as delightful as Pemberley.”
-
- “I wish it may.”
-
- “But I would really advise you to make your purchase in that
- neighbourhood, and take Pemberley for a kind of model. There is
- not a finer county in England than Derbyshire.”
-
- “With all my heart; I will buy Pemberley itself if Darcy will
- sell it.”
-
- “I am talking of possibilities, Charles.”
-
- “Upon my word, Caroline, I should think it more possible to get
- Pemberley by purchase than by imitation.”
-
- Elizabeth was so much caught with what passed, as to leave her
- very little attention for her book; and soon laying it wholly
- aside, she drew near the card-table, and stationed herself
- between Mr. Bingley and his eldest sister, to observe the game.
-
- “Is Miss Darcy much grown since the spring?” said Miss Bingley;
- “will she be as tall as I am?”
-
- “I think she will. She is now about Miss Elizabeth Bennet’s
- height, or rather taller.”
-
- “How I long to see her again! I never met with anybody who
- delighted me so much. Such a countenance, such manners! And so
- extremely accomplished for her age! Her performance on the
- pianoforte is exquisite.”
-
- “It is amazing to me,” said Bingley, “how young ladies can have
- patience to be so very accomplished as they all are.”
-
- “All young ladies accomplished! My dear Charles, what do you
- mean?”
-
- “Yes, all of them, I think. They all paint tables, cover screens,
- and net purses. I scarcely know anyone who cannot do all this,
- and I am sure I never heard a young lady spoken of for the first
- time, without being informed that she was very accomplished.”
-
- “Your list of the common extent of accomplishments,” said Darcy,
- “has too much truth. The word is applied to many a woman who
- deserves it no otherwise than by netting a purse or covering a
- screen. But I am very far from agreeing with you in your
- estimation of ladies in general. I cannot boast of knowing more
- than half-a-dozen, in the whole range of my acquaintance, that
- are really accomplished.”
-
- “Nor I, I am sure,” said Miss Bingley.
-
- “Then,” observed Elizabeth, “you must comprehend a great deal in
- your idea of an accomplished woman.”
-
- “Yes, I do comprehend a great deal in it.”
-
- “Oh! certainly,” cried his faithful assistant, “no one can be
- really esteemed accomplished who does not greatly surpass what is
- usually met with. A woman must have a thorough knowledge of
- music, singing, drawing, dancing, and the modern languages, to
- deserve the word; and besides all this, she must possess a
- certain something in her air and manner of walking, the tone of
- her voice, her address and expressions, or the word will be but
- half-deserved.”
-
- “All this she must possess,” added Darcy, “and to all this she
- must yet add something more substantial, in the improvement of
- her mind by extensive reading.”
-
- “I am no longer surprised at your knowing _only_ six accomplished
- women. I rather wonder now at your knowing _any_.”
-
- “Are you so severe upon your own sex as to doubt the possibility
- of all this?”
-
- “_I_ never saw such a woman. _I_ never saw such capacity, and
- taste, and application, and elegance, as you describe united.”
-
- Mrs. Hurst and Miss Bingley both cried out against the injustice
- of her implied doubt, and were both protesting that they knew
- many women who answered this description, when Mr. Hurst called
- them to order, with bitter complaints of their inattention to
- what was going forward. As all conversation was thereby at an
- end, Elizabeth soon afterwards left the room.
-
- “Elizabeth Bennet,” said Miss Bingley, when the door was closed
- on her, “is one of those young ladies who seek to recommend
- themselves to the other sex by undervaluing their own; and with
- many men, I dare say, it succeeds. But, in my opinion, it is a
- paltry device, a very mean art.”
-
- “Undoubtedly,” replied Darcy, to whom this remark was chiefly
- addressed, “there is a meanness in _all_ the arts which ladies
- sometimes condescend to employ for captivation. Whatever bears
- affinity to cunning is despicable.”
-
- Miss Bingley was not so entirely satisfied with this reply as to
- continue the subject.
-
- Elizabeth joined them again only to say that her sister was
- worse, and that she could not leave her. Bingley urged Mr. Jones
- being sent for immediately; while his sisters, convinced that no
- country advice could be of any service, recommended an express to
- town for one of the most eminent physicians. This she would not
- hear of; but she was not so unwilling to comply with their
- brother’s proposal; and it was settled that Mr. Jones should be
- sent for early in the morning, if Miss Bennet were not decidedly
- better. Bingley was quite uncomfortable; his sisters declared
- that they were miserable. They solaced their wretchedness,
- however, by duets after supper, while he could find no better
- relief to his feelings than by giving his housekeeper directions
- that every attention might be paid to the sick lady and her
- sister.
-
-
-
-
- Chapter 9
-
- Elizabeth passed the chief of the night in her sister’s room, and
- in the morning had the pleasure of being able to send a tolerable
- answer to the inquiries which she very early received from Mr.
- Bingley by a housemaid, and some time afterwards from the two
- elegant ladies who waited on his sisters. In spite of this
- amendment, however, she requested to have a note sent to
- Longbourn, desiring her mother to visit Jane, and form her own
- judgement of her situation. The note was immediately dispatched,
- and its contents as quickly complied with. Mrs. Bennet,
- accompanied by her two youngest girls, reached Netherfield soon
- after the family breakfast.
-
- Had she found Jane in any apparent danger, Mrs. Bennet would have
- been very miserable; but being satisfied on seeing her that her
- illness was not alarming, she had no wish of her recovering
- immediately, as her restoration to health would probably remove
- her from Netherfield. She would not listen, therefore, to her
- daughter’s proposal of being carried home; neither did the
- apothecary, who arrived about the same time, think it at all
- advisable. After sitting a little while with Jane, on Miss
- Bingley’s appearance and invitation, the mother and three
- daughters all attended her into the breakfast parlour. Bingley
- met them with hopes that Mrs. Bennet had not found Miss Bennet
- worse than she expected.
-
- “Indeed I have, sir,” was her answer. “She is a great deal too
- ill to be moved. Mr. Jones says we must not think of moving her.
- We must trespass a little longer on your kindness.”
-
- “Removed!” cried Bingley. “It must not be thought of. My sister,
- I am sure, will not hear of her removal.”
-
- “You may depend upon it, Madam,” said Miss Bingley, with cold
- civility, “that Miss Bennet will receive every possible attention
- while she remains with us.”
-
- Mrs. Bennet was profuse in her acknowledgments.
-
- “I am sure,” she added, “if it was not for such good friends I do
- not know what would become of her, for she is very ill indeed,
- and suffers a vast deal, though with the greatest patience in the
- world, which is always the way with her, for she has, without
- exception, the sweetest temper I have ever met with. I often tell
- my other girls they are nothing to _her_. You have a sweet room
- here, Mr. Bingley, and a charming prospect over the gravel walk.
- I do not know a place in the country that is equal to
- Netherfield. You will not think of quitting it in a hurry, I
- hope, though you have but a short lease.”
-
- “Whatever I do is done in a hurry,” replied he; “and therefore if
- I should resolve to quit Netherfield, I should probably be off in
- five minutes. At present, however, I consider myself as quite
- fixed here.”
-
- “That is exactly what I should have supposed of you,” said
- Elizabeth.
-
- “You begin to comprehend me, do you?” cried he, turning towards
- her.
-
- “Oh! yes—I understand you perfectly.”
-
- “I wish I might take this for a compliment; but to be so easily
- seen through I am afraid is pitiful.”
-
- “That is as it happens. It does not follow that a deep, intricate
- character is more or less estimable than such a one as yours.”
-
- “Lizzy,” cried her mother, “remember where you are, and do not
- run on in the wild manner that you are suffered to do at home.”
-
- “I did not know before,” continued Bingley immediately, “that you
- were a studier of character. It must be an amusing study.”
-
- “Yes, but intricate characters are the _most_ amusing. They have
- at least that advantage.”
-
- “The country,” said Darcy, “can in general supply but a few
- subjects for such a study. In a country neighbourhood you move in
- a very confined and unvarying society.”
-
- “But people themselves alter so much, that there is something new
- to be observed in them for ever.”
-
- “Yes, indeed,” cried Mrs. Bennet, offended by his manner of
- mentioning a country neighbourhood. “I assure you there is quite
- as much of _that_ going on in the country as in town.”
-
- Everybody was surprised, and Darcy, after looking at her for a
- moment, turned silently away. Mrs. Bennet, who fancied she had
- gained a complete victory over him, continued her triumph.
-
- “I cannot see that London has any great advantage over the
- country, for my part, except the shops and public places. The
- country is a vast deal pleasanter, is it not, Mr. Bingley?”
-
- “When I am in the country,” he replied, “I never wish to leave
- it; and when I am in town it is pretty much the same. They have
- each their advantages, and I can be equally happy in either.”
-
- “Aye—that is because you have the right disposition. But that
- gentleman,” looking at Darcy, “seemed to think the country was
- nothing at all.”
-
- “Indeed, Mamma, you are mistaken,” said Elizabeth, blushing for
- her mother. “You quite mistook Mr. Darcy. He only meant that
- there was not such a variety of people to be met with in the
- country as in the town, which you must acknowledge to be true.”
-
- “Certainly, my dear, nobody said there were; but as to not
- meeting with many people in this neighbourhood, I believe there
- are few neighbourhoods larger. I know we dine with
- four-and-twenty families.”
-
- Nothing but concern for Elizabeth could enable Bingley to keep
- his countenance. His sister was less delicate, and directed her
- eyes towards Mr. Darcy with a very expressive smile. Elizabeth,
- for the sake of saying something that might turn her mother’s
- thoughts, now asked her if Charlotte Lucas had been at Longbourn
- since _her_ coming away.
-
- “Yes, she called yesterday with her father. What an agreeable man
- Sir William is, Mr. Bingley, is not he? So much the man of
- fashion! So genteel and easy! He has always something to say to
- everybody. _That_ is my idea of good breeding; and those persons
- who fancy themselves very important, and never open their mouths,
- quite mistake the matter.”
-
- “Did Charlotte dine with you?”
-
- “No, she would go home. I fancy she was wanted about the
- mince-pies. For my part, Mr. Bingley, _I_ always keep servants
- that can do their own work; _my_ daughters are brought up very
- differently. But everybody is to judge for themselves, and the
- Lucases are a very good sort of girls, I assure you. It is a pity
- they are not handsome! Not that _I_ think Charlotte so _very_
- plain—but then she is our particular friend.”
-
- “She seems a very pleasant young woman.”
-
- “Oh! dear, yes; but you must own she is very plain. Lady Lucas
- herself has often said so, and envied me Jane’s beauty. I do not
- like to boast of my own child, but to be sure, Jane—one does not
- often see anybody better looking. It is what everybody says. I do
- not trust my own partiality. When she was only fifteen, there was
- a man at my brother Gardiner’s in town so much in love with her
- that my sister-in-law was sure he would make her an offer before
- we came away. But, however, he did not. Perhaps he thought her
- too young. However, he wrote some verses on her, and very pretty
- they were.”
-
- “And so ended his affection,” said Elizabeth impatiently. “There
- has been many a one, I fancy, overcome in the same way. I wonder
- who first discovered the efficacy of poetry in driving away
- love!”
-
- “I have been used to consider poetry as the _food_ of love,” said
- Darcy.
-
- “Of a fine, stout, healthy love it may. Everything nourishes what
- is strong already. But if it be only a slight, thin sort of
- inclination, I am convinced that one good sonnet will starve it
- entirely away.”
-
- Darcy only smiled; and the general pause which ensued made
- Elizabeth tremble lest her mother should be exposing herself
- again. She longed to speak, but could think of nothing to say;
- and after a short silence Mrs. Bennet began repeating her thanks
- to Mr. Bingley for his kindness to Jane, with an apology for
- troubling him also with Lizzy. Mr. Bingley was unaffectedly civil
- in his answer, and forced his younger sister to be civil also,
- and say what the occasion required. She performed her part indeed
- without much graciousness, but Mrs. Bennet was satisfied, and
- soon afterwards ordered her carriage. Upon this signal, the
- youngest of her daughters put herself forward. The two girls had
- been whispering to each other during the whole visit, and the
- result of it was, that the youngest should tax Mr. Bingley with
- having promised on his first coming into the country to give a
- ball at Netherfield.
-
- Lydia was a stout, well-grown girl of fifteen, with a fine
- complexion and good-humoured countenance; a favourite with her
- mother, whose affection had brought her into public at an early
- age. She had high animal spirits, and a sort of natural
- self-consequence, which the attention of the officers, to whom
- her uncle’s good dinners, and her own easy manners recommended
- her, had increased into assurance. She was very equal, therefore,
- to address Mr. Bingley on the subject of the ball, and abruptly
- reminded him of his promise; adding, that it would be the most
- shameful thing in the world if he did not keep it. His answer to
- this sudden attack was delightful to their mother’s ear:
-
- “I am perfectly ready, I assure you, to keep my engagement; and
- when your sister is recovered, you shall, if you please, name the
- very day of the ball. But you would not wish to be dancing when
- she is ill.”
-
- Lydia declared herself satisfied. “Oh! yes—it would be much
- better to wait till Jane was well, and by that time most likely
- Captain Carter would be at Meryton again. And when you have given
- _your_ ball,” she added, “I shall insist on their giving one
- also. I shall tell Colonel Forster it will be quite a shame if he
- does not.”
-
- Mrs. Bennet and her daughters then departed, and Elizabeth
- returned instantly to Jane, leaving her own and her relations’
- behaviour to the remarks of the two ladies and Mr. Darcy; the
- latter of whom, however, could not be prevailed on to join in
- their censure of _her_, in spite of all Miss Bingley’s witticisms
- on _fine eyes_.
-
-
-
-
- Chapter 10
-
- The day passed much as the day before had done. Mrs. Hurst and
- Miss Bingley had spent some hours of the morning with the
- invalid, who continued, though slowly, to mend; and in the
- evening Elizabeth joined their party in the drawing-room. The
- loo-table, however, did not appear. Mr. Darcy was writing, and
- Miss Bingley, seated near him, was watching the progress of his
- letter and repeatedly calling off his attention by messages to
- his sister. Mr. Hurst and Mr. Bingley were at piquet, and Mrs.
- Hurst was observing their game.
-
- Elizabeth took up some needlework, and was sufficiently amused in
- attending to what passed between Darcy and his companion. The
- perpetual commendations of the lady, either on his handwriting,
- or on the evenness of his lines, or on the length of his letter,
- with the perfect unconcern with which her praises were received,
- formed a curious dialogue, and was exactly in union with her
- opinion of each.
-
- “How delighted Miss Darcy will be to receive such a letter!”
-
- He made no answer.
-
- “You write uncommonly fast.”
-
- “You are mistaken. I write rather slowly.”
-
- “How many letters you must have occasion to write in the course
- of a year! Letters of business, too! How odious I should think
- them!”
-
- “It is fortunate, then, that they fall to my lot instead of
- yours.”
-
- “Pray tell your sister that I long to see her.”
-
- “I have already told her so once, by your desire.”
-
- “I am afraid you do not like your pen. Let me mend it for you. I
- mend pens remarkably well.”
-
- “Thank you—but I always mend my own.”
-
- “How can you contrive to write so even?”
-
- He was silent.
-
- “Tell your sister I am delighted to hear of her improvement on
- the harp; and pray let her know that I am quite in raptures with
- her beautiful little design for a table, and I think it
- infinitely superior to Miss Grantley’s.”
-
- “Will you give me leave to defer your raptures till I write
- again? At present I have not room to do them justice.”
-
- “Oh! it is of no consequence. I shall see her in January. But do
- you always write such charming long letters to her, Mr. Darcy?”
-
- “They are generally long; but whether always charming it is not
- for me to determine.”
-
- “It is a rule with me, that a person who can write a long letter
- with ease, cannot write ill.”
-
- “That will not do for a compliment to Darcy, Caroline,” cried her
- brother, “because he does _not_ write with ease. He studies too
- much for words of four syllables. Do not you, Darcy?”
-
- “My style of writing is very different from yours.”
-
- “Oh!” cried Miss Bingley, “Charles writes in the most careless
- way imaginable. He leaves out half his words, and blots the
- rest.”
-
- “My ideas flow so rapidly that I have not time to express them—by
- which means my letters sometimes convey no ideas at all to my
- correspondents.”
-
- “Your humility, Mr. Bingley,” said Elizabeth, “must disarm
- reproof.”
-
- “Nothing is more deceitful,” said Darcy, “than the appearance of
- humility. It is often only carelessness of opinion, and sometimes
- an indirect boast.”
-
- “And which of the two do you call _my_ little recent piece of
- modesty?”
-
- “The indirect boast; for you are really proud of your defects in
- writing, because you consider them as proceeding from a rapidity
- of thought and carelessness of execution, which, if not
- estimable, you think at least highly interesting. The power of
- doing anything with quickness is always prized much by the
- possessor, and often without any attention to the imperfection of
- the performance. When you told Mrs. Bennet this morning that if
- you ever resolved upon quitting Netherfield you should be gone in
- five minutes, you meant it to be a sort of panegyric, of
- compliment to yourself—and yet what is there so very laudable in
- a precipitance which must leave very necessary business undone,
- and can be of no real advantage to yourself or anyone else?”
-
- “Nay,” cried Bingley, “this is too much, to remember at night all
- the foolish things that were said in the morning. And yet, upon
- my honour, I believe what I said of myself to be true, and I
- believe it at this moment. At least, therefore, I did not assume
- the character of needless precipitance merely to show off before
- the ladies.”
-
- “I dare say you believed it; but I am by no means convinced that
- you would be gone with such celerity. Your conduct would be quite
- as dependent on chance as that of any man I know; and if, as you
- were mounting your horse, a friend were to say, ‘Bingley, you had
- better stay till next week,’ you would probably do it, you would
- probably not go—and at another word, might stay a month.”
-
- “You have only proved by this,” cried Elizabeth, “that Mr.
- Bingley did not do justice to his own disposition. You have shown
- him off now much more than he did himself.”
-
- “I am exceedingly gratified,” said Bingley, “by your converting
- what my friend says into a compliment on the sweetness of my
- temper. But I am afraid you are giving it a turn which that
- gentleman did by no means intend; for he would certainly think
- better of me, if under such a circumstance I were to give a flat
- denial, and ride off as fast as I could.”
-
- “Would Mr. Darcy then consider the rashness of your original
- intentions as atoned for by your obstinacy in adhering to it?”
-
- “Upon my word, I cannot exactly explain the matter; Darcy must
- speak for himself.”
-
- “You expect me to account for opinions which you choose to call
- mine, but which I have never acknowledged. Allowing the case,
- however, to stand according to your representation, you must
- remember, Miss Bennet, that the friend who is supposed to desire
- his return to the house, and the delay of his plan, has merely
- desired it, asked it without offering one argument in favour of
- its propriety.”
-
- “To yield readily—easily—to the _persuasion_ of a friend is no
- merit with you.”
-
- “To yield without conviction is no compliment to the
- understanding of either.”
-
- “You appear to me, Mr. Darcy, to allow nothing for the influence
- of friendship and affection. A regard for the requester would
- often make one readily yield to a request, without waiting for
- arguments to reason one into it. I am not particularly speaking
- of such a case as you have supposed about Mr. Bingley. We may as
- well wait, perhaps, till the circumstance occurs before we
- discuss the discretion of his behaviour thereupon. But in general
- and ordinary cases between friend and friend, where one of them
- is desired by the other to change a resolution of no very great
- moment, should you think ill of that person for complying with
- the desire, without waiting to be argued into it?”
-
- “Will it not be advisable, before we proceed on this subject, to
- arrange with rather more precision the degree of importance which
- is to appertain to this request, as well as the degree of
- intimacy subsisting between the parties?”
-
- “By all means,” cried Bingley; “let us hear all the particulars,
- not forgetting their comparative height and size; for that will
- have more weight in the argument, Miss Bennet, than you may be
- aware of. I assure you, that if Darcy were not such a great tall
- fellow, in comparison with myself, I should not pay him half so
- much deference. I declare I do not know a more awful object than
- Darcy, on particular occasions, and in particular places; at his
- own house especially, and of a Sunday evening, when he has
- nothing to do.”
-
- Mr. Darcy smiled; but Elizabeth thought she could perceive that
- he was rather offended, and therefore checked her laugh. Miss
- Bingley warmly resented the indignity he had received, in an
- expostulation with her brother for talking such nonsense.
-
- “I see your design, Bingley,” said his friend. “You dislike an
- argument, and want to silence this.”
-
- “Perhaps I do. Arguments are too much like disputes. If you and
- Miss Bennet will defer yours till I am out of the room, I shall
- be very thankful; and then you may say whatever you like of me.”
-
- “What you ask,” said Elizabeth, “is no sacrifice on my side; and
- Mr. Darcy had much better finish his letter.”
-
- Mr. Darcy took her advice, and did finish his letter.
-
- When that business was over, he applied to Miss Bingley and
- Elizabeth for an indulgence of some music. Miss Bingley moved
- with some alacrity to the pianoforte; and, after a polite request
- that Elizabeth would lead the way which the other as politely and
- more earnestly negatived, she seated herself.
-
- Mrs. Hurst sang with her sister, and while they were thus
- employed, Elizabeth could not help observing, as she turned over
- some music-books that lay on the instrument, how frequently Mr.
- Darcy’s eyes were fixed on her. She hardly knew how to suppose
- that she could be an object of admiration to so great a man; and
- yet that he should look at her because he disliked her, was still
- more strange. She could only imagine, however, at last that she
- drew his notice because there was something more wrong and
- reprehensible, according to his ideas of right, than in any other
- person present. The supposition did not pain her. She liked him
- too little to care for his approbation.
-
- After playing some Italian songs, Miss Bingley varied the charm
- by a lively Scotch air; and soon afterwards Mr. Darcy, drawing
- near Elizabeth, said to her:
-
- “Do not you feel a great inclination, Miss Bennet, to seize such
- an opportunity of dancing a reel?”
-
- She smiled, but made no answer. He repeated the question, with
- some surprise at her silence.
-
- “Oh!” said she, “I heard you before, but I could not immediately
- determine what to say in reply. You wanted me, I know, to say
- ‘Yes,’ that you might have the pleasure of despising my taste;
- but I always delight in overthrowing those kind of schemes, and
- cheating a person of their premeditated contempt. I have,
- therefore, made up my mind to tell you, that I do not want to
- dance a reel at all—and now despise me if you dare.”
-
- “Indeed I do not dare.”
-
- Elizabeth, having rather expected to affront him, was amazed at
- his gallantry; but there was a mixture of sweetness and archness
- in her manner which made it difficult for her to affront anybody;
- and Darcy had never been so bewitched by any woman as he was by
- her. He really believed, that were it not for the inferiority of
- her connections, he should be in some danger.
-
- Miss Bingley saw, or suspected enough to be jealous; and her
- great anxiety for the recovery of her dear friend Jane received
- some assistance from her desire of getting rid of Elizabeth.
-
- She often tried to provoke Darcy into disliking her guest, by
- talking of their supposed marriage, and planning his happiness in
- such an alliance.
-
- “I hope,” said she, as they were walking together in the
- shrubbery the next day, “you will give your mother-in-law a few
- hints, when this desirable event takes place, as to the advantage
- of holding her tongue; and if you can compass it, do cure the
- younger girls of running after officers. And, if I may mention so
- delicate a subject, endeavour to check that little something,
- bordering on conceit and impertinence, which your lady
- possesses.”
-
- “Have you anything else to propose for my domestic felicity?”
-
- “Oh! yes. Do let the portraits of your uncle and aunt Phillips be
- placed in the gallery at Pemberley. Put them next to your
- great-uncle the judge. They are in the same profession, you know,
- only in different lines. As for your Elizabeth’s picture, you
- must not have it taken, for what painter could do justice to
- those beautiful eyes?”
-
- “It would not be easy, indeed, to catch their expression, but
- their colour and shape, and the eyelashes, so remarkably fine,
- might be copied.”
-
- At that moment they were met from another walk by Mrs. Hurst and
- Elizabeth herself.
-
- “I did not know that you intended to walk,” said Miss Bingley, in
- some confusion, lest they had been overheard.
-
- “You used us abominably ill,” answered Mrs. Hurst, “running away
- without telling us that you were coming out.”
-
- Then taking the disengaged arm of Mr. Darcy, she left Elizabeth
- to walk by herself. The path just admitted three. Mr. Darcy felt
- their rudeness, and immediately said:
-
- “This walk is not wide enough for our party. We had better go
- into the avenue.”
-
- But Elizabeth, who had not the least inclination to remain with
- them, laughingly answered:
-
- “No, no; stay where you are. You are charmingly grouped, and
- appear to uncommon advantage. The picturesque would be spoilt by
- admitting a fourth. Good-bye.”
-
- She then ran gaily off, rejoicing as she rambled about, in the
- hope of being at home again in a day or two. Jane was already so
- much recovered as to intend leaving her room for a couple of
- hours that evening.
-
-
-
-
- Chapter 11
-
- When the ladies removed after dinner, Elizabeth ran up to her
- sister, and seeing her well guarded from cold, attended her into
- the drawing-room, where she was welcomed by her two friends with
- many professions of pleasure; and Elizabeth had never seen them
- so agreeable as they were during the hour which passed before the
- gentlemen appeared. Their powers of conversation were
- considerable. They could describe an entertainment with accuracy,
- relate an anecdote with humour, and laugh at their acquaintance
- with spirit.
-
- But when the gentlemen entered, Jane was no longer the first
- object; Miss Bingley’s eyes were instantly turned toward Darcy,
- and she had something to say to him before he had advanced many
- steps. He addressed himself to Miss Bennet, with a polite
- congratulation; Mr. Hurst also made her a slight bow, and said he
- was “very glad;” but diffuseness and warmth remained for
- Bingley’s salutation. He was full of joy and attention. The first
- half-hour was spent in piling up the fire, lest she should suffer
- from the change of room; and she removed at his desire to the
- other side of the fireplace, that she might be further from the
- door. He then sat down by her, and talked scarcely to anyone
- else. Elizabeth, at work in the opposite corner, saw it all with
- great delight.
-
- When tea was over, Mr. Hurst reminded his sister-in-law of the
- card-table—but in vain. She had obtained private intelligence
- that Mr. Darcy did not wish for cards; and Mr. Hurst soon found
- even his open petition rejected. She assured him that no one
- intended to play, and the silence of the whole party on the
- subject seemed to justify her. Mr. Hurst had therefore nothing to
- do, but to stretch himself on one of the sofas and go to sleep.
- Darcy took up a book; Miss Bingley did the same; and Mrs. Hurst,
- principally occupied in playing with her bracelets and rings,
- joined now and then in her brother’s conversation with Miss
- Bennet.
-
- Miss Bingley’s attention was quite as much engaged in watching
- Mr. Darcy’s progress through _his_ book, as in reading her own;
- and she was perpetually either making some inquiry, or looking at
- his page. She could not win him, however, to any conversation; he
- merely answered her question, and read on. At length, quite
- exhausted by the attempt to be amused with her own book, which
- she had only chosen because it was the second volume of his, she
- gave a great yawn and said, “How pleasant it is to spend an
- evening in this way! I declare after all there is no enjoyment
- like reading! How much sooner one tires of anything than of a
- book! When I have a house of my own, I shall be miserable if I
- have not an excellent library.”
-
- No one made any reply. She then yawned again, threw aside her
- book, and cast her eyes round the room in quest for some
- amusement; when hearing her brother mentioning a ball to Miss
- Bennet, she turned suddenly towards him and said:
-
- “By the bye, Charles, are you really serious in meditating a
- dance at Netherfield? I would advise you, before you determine on
- it, to consult the wishes of the present party; I am much
- mistaken if there are not some among us to whom a ball would be
- rather a punishment than a pleasure.”
-
- “If you mean Darcy,” cried her brother, “he may go to bed, if he
- chooses, before it begins—but as for the ball, it is quite a
- settled thing; and as soon as Nicholls has made white soup
- enough, I shall send round my cards.”
-
- “I should like balls infinitely better,” she replied, “if they
- were carried on in a different manner; but there is something
- insufferably tedious in the usual process of such a meeting. It
- would surely be much more rational if conversation instead of
- dancing were made the order of the day.”
-
- “Much more rational, my dear Caroline, I dare say, but it would
- not be near so much like a ball.”
-
- Miss Bingley made no answer, and soon afterwards she got up and
- walked about the room. Her figure was elegant, and she walked
- well; but Darcy, at whom it was all aimed, was still inflexibly
- studious. In the desperation of her feelings, she resolved on one
- effort more, and, turning to Elizabeth, said:
-
- “Miss Eliza Bennet, let me persuade you to follow my example, and
- take a turn about the room. I assure you it is very refreshing
- after sitting so long in one attitude.”
-
- Elizabeth was surprised, but agreed to it immediately. Miss
- Bingley succeeded no less in the real object of her civility; Mr.
- Darcy looked up. He was as much awake to the novelty of attention
- in that quarter as Elizabeth herself could be, and unconsciously
- closed his book. He was directly invited to join their party, but
- he declined it, observing that he could imagine but two motives
- for their choosing to walk up and down the room together, with
- either of which motives his joining them would interfere. “What
- could he mean? She was dying to know what could be his
- meaning?”—and asked Elizabeth whether she could at all understand
- him?
-
- “Not at all,” was her answer; “but depend upon it, he means to be
- severe on us, and our surest way of disappointing him will be to
- ask nothing about it.”
-
- Miss Bingley, however, was incapable of disappointing Mr. Darcy
- in anything, and persevered therefore in requiring an explanation
- of his two motives.
-
- “I have not the smallest objection to explaining them,” said he,
- as soon as she allowed him to speak. “You either choose this
- method of passing the evening because you are in each other’s
- confidence, and have secret affairs to discuss, or because you
- are conscious that your figures appear to the greatest advantage
- in walking; if the first, I would be completely in your way, and
- if the second, I can admire you much better as I sit by the
- fire.”
-
- “Oh! shocking!” cried Miss Bingley. “I never heard anything so
- abominable. How shall we punish him for such a speech?”
-
- “Nothing so easy, if you have but the inclination,” said
- Elizabeth. “We can all plague and punish one another. Tease
- him—laugh at him. Intimate as you are, you must know how it is to
- be done.”
-
- “But upon my honour, I do _not_. I do assure you that my intimacy
- has not yet taught me _that_. Tease calmness of manner and
- presence of mind! No, no; I feel he may defy us there. And as to
- laughter, we will not expose ourselves, if you please, by
- attempting to laugh without a subject. Mr. Darcy may hug
- himself.”
-
- “Mr. Darcy is not to be laughed at!” cried Elizabeth. “That is an
- uncommon advantage, and uncommon I hope it will continue, for it
- would be a great loss to _me_ to have many such acquaintances. I
- dearly love a laugh.”
-
- “Miss Bingley,” said he, “has given me more credit than can be.
- The wisest and the best of men—nay, the wisest and best of their
- actions—may be rendered ridiculous by a person whose first object
- in life is a joke.”
-
- “Certainly,” replied Elizabeth—“there are such people, but I hope
- I am not one of _them_. I hope I never ridicule what is wise and
- good. Follies and nonsense, whims and inconsistencies, _do_
- divert me, I own, and I laugh at them whenever I can. But these,
- I suppose, are precisely what you are without.”
-
- “Perhaps that is not possible for anyone. But it has been the
- study of my life to avoid those weaknesses which often expose a
- strong understanding to ridicule.”
-
- “Such as vanity and pride.”
-
- “Yes, vanity is a weakness indeed. But pride—where there is a
- real superiority of mind, pride will be always under good
- regulation.”
-
- Elizabeth turned away to hide a smile.
-
- “Your examination of Mr. Darcy is over, I presume,” said Miss
- Bingley; “and pray what is the result?”
-
- “I am perfectly convinced by it that Mr. Darcy has no defect. He
- owns it himself without disguise.”
-
- “No,” said Darcy, “I have made no such pretension. I have faults
- enough, but they are not, I hope, of understanding. My temper I
- dare not vouch for. It is, I believe, too little
- yielding—certainly too little for the convenience of the world. I
- cannot forget the follies and vices of others so soon as I ought,
- nor their offenses against myself. My feelings are not puffed
- about with every attempt to move them. My temper would perhaps be
- called resentful. My good opinion once lost, is lost forever.”
-
- “_That_ is a failing indeed!” cried Elizabeth. “Implacable
- resentment _is_ a shade in a character. But you have chosen your
- fault well. I really cannot _laugh_ at it. You are safe from me.”
-
- “There is, I believe, in every disposition a tendency to some
- particular evil—a natural defect, which not even the best
- education can overcome.”
-
- “And _your_ defect is to hate everybody.”
-
- “And yours,” he replied with a smile, “is willfully to
- misunderstand them.”
-
- “Do let us have a little music,” cried Miss Bingley, tired of a
- conversation in which she had no share. “Louisa, you will not
- mind my waking Mr. Hurst?”
-
- Her sister had not the smallest objection, and the pianoforte was
- opened; and Darcy, after a few moments’ recollection, was not
- sorry for it. He began to feel the danger of paying Elizabeth too
- much attention.
-
-
-
-
- Chapter 12
-
- In consequence of an agreement between the sisters, Elizabeth
- wrote the next morning to their mother, to beg that the carriage
- might be sent for them in the course of the day. But Mrs. Bennet,
- who had calculated on her daughters remaining at Netherfield till
- the following Tuesday, which would exactly finish Jane’s week,
- could not bring herself to receive them with pleasure before. Her
- answer, therefore, was not propitious, at least not to
- Elizabeth’s wishes, for she was impatient to get home. Mrs.
- Bennet sent them word that they could not possibly have the
- carriage before Tuesday; and in her postscript it was added, that
- if Mr. Bingley and his sister pressed them to stay longer, she
- could spare them very well. Against staying longer, however,
- Elizabeth was positively resolved—nor did she much expect it
- would be asked; and fearful, on the contrary, as being considered
- as intruding themselves needlessly long, she urged Jane to borrow
- Mr. Bingley’s carriage immediately, and at length it was settled
- that their original design of leaving Netherfield that morning
- should be mentioned, and the request made.
-
- The communication excited many professions of concern; and enough
- was said of wishing them to stay at least till the following day
- to work on Jane; and till the morrow their going was deferred.
- Miss Bingley was then sorry that she had proposed the delay, for
- her jealousy and dislike of one sister much exceeded her
- affection for the other.
-
- The master of the house heard with real sorrow that they were to
- go so soon, and repeatedly tried to persuade Miss Bennet that it
- would not be safe for her—that she was not enough recovered; but
- Jane was firm where she felt herself to be right.
-
- To Mr. Darcy it was welcome intelligence—Elizabeth had been at
- Netherfield long enough. She attracted him more than he liked—and
- Miss Bingley was uncivil to _her_, and more teasing than usual to
- himself. He wisely resolved to be particularly careful that no
- sign of admiration should _now_ escape him, nothing that could
- elevate her with the hope of influencing his felicity; sensible
- that if such an idea had been suggested, his behaviour during the
- last day must have material weight in confirming or crushing it.
- Steady to his purpose, he scarcely spoke ten words to her through
- the whole of Saturday, and though they were at one time left by
- themselves for half-an-hour, he adhered most conscientiously to
- his book, and would not even look at her.
-
- On Sunday, after morning service, the separation, so agreeable to
- almost all, took place. Miss Bingley’s civility to Elizabeth
- increased at last very rapidly, as well as her affection for
- Jane; and when they parted, after assuring the latter of the
- pleasure it would always give her to see her either at Longbourn
- or Netherfield, and embracing her most tenderly, she even shook
- hands with the former. Elizabeth took leave of the whole party in
- the liveliest of spirits.
-
- They were not welcomed home very cordially by their mother. Mrs.
- Bennet wondered at their coming, and thought them very wrong to
- give so much trouble, and was sure Jane would have caught cold
- again. But their father, though very laconic in his expressions
- of pleasure, was really glad to see them; he had felt their
- importance in the family circle. The evening conversation, when
- they were all assembled, had lost much of its animation, and
- almost all its sense by the absence of Jane and Elizabeth.
-
- They found Mary, as usual, deep in the study of thorough-bass and
- human nature; and had some extracts to admire, and some new
- observations of threadbare morality to listen to. Catherine and
- Lydia had information for them of a different sort. Much had been
- done and much had been said in the regiment since the preceding
- Wednesday; several of the officers had dined lately with their
- uncle, a private had been flogged, and it had actually been
- hinted that Colonel Forster was going to be married.
-
-
-
-
- Chapter 13
-
- “I hope, my dear,” said Mr. Bennet to his wife, as they were at
- breakfast the next morning, “that you have ordered a good dinner
- to-day, because I have reason to expect an addition to our family
- party.”
-
- “Who do you mean, my dear? I know of nobody that is coming, I am
- sure, unless Charlotte Lucas should happen to call in—and I hope
- _my_ dinners are good enough for her. I do not believe she often
- sees such at home.”
-
- “The person of whom I speak is a gentleman, and a stranger.”
-
- Mrs. Bennet’s eyes sparkled. “A gentleman and a stranger! It is
- Mr. Bingley, I am sure! Well, I am sure I shall be extremely glad
- to see Mr. Bingley. But—good Lord! how unlucky! There is not a
- bit of fish to be got to-day. Lydia, my love, ring the bell—I
- must speak to Hill this moment.”
-
- “It is _not_ Mr. Bingley,” said her husband; “it is a person whom
- I never saw in the whole course of my life.”
-
- This roused a general astonishment; and he had the pleasure of
- being eagerly questioned by his wife and his five daughters at
- once.
-
- After amusing himself some time with their curiosity, he thus
- explained:
-
- “About a month ago I received this letter; and about a fortnight
- ago I answered it, for I thought it a case of some delicacy, and
- requiring early attention. It is from my cousin, Mr. Collins,
- who, when I am dead, may turn you all out of this house as soon
- as he pleases.”
-
- “Oh! my dear,” cried his wife, “I cannot bear to hear that
- mentioned. Pray do not talk of that odious man. I do think it is
- the hardest thing in the world, that your estate should be
- entailed away from your own children; and I am sure, if I had
- been you, I should have tried long ago to do something or other
- about it.”
-
- Jane and Elizabeth tried to explain to her the nature of an
- entail. They had often attempted to do it before, but it was a
- subject on which Mrs. Bennet was beyond the reach of reason, and
- she continued to rail bitterly against the cruelty of settling an
- estate away from a family of five daughters, in favour of a man
- whom nobody cared anything about.
-
- “It certainly is a most iniquitous affair,” said Mr. Bennet, “and
- nothing can clear Mr. Collins from the guilt of inheriting
- Longbourn. But if you will listen to his letter, you may perhaps
- be a little softened by his manner of expressing himself.”
-
- “No, that I am sure I shall not; and I think it is very
- impertinent of him to write to you at all, and very hypocritical.
- I hate such false friends. Why could he not keep on quarreling
- with you, as his father did before him?”
-
- “Why, indeed; he does seem to have had some filial scruples on
- that head, as you will hear.”
-
- “Hunsford, near Westerham, Kent, 15_th October_.
-
- “Dear Sir,—
- “The disagreement subsisting between yourself and my late
- honoured father always gave me much uneasiness, and since I have
- had the misfortune to lose him, I have frequently wished to heal
- the breach; but for some time I was kept back by my own doubts,
- fearing lest it might seem disrespectful to his memory for me to
- be on good terms with anyone with whom it had always pleased him
- to be at variance.—‘There, Mrs. Bennet.’—My mind, however, is now
- made up on the subject, for having received ordination at Easter,
- I have been so fortunate as to be distinguished by the patronage
- of the Right Honourable Lady Catherine de Bourgh, widow of Sir
- Lewis de Bourgh, whose bounty and beneficence has preferred me to
- the valuable rectory of this parish, where it shall be my earnest
- endeavour to demean myself with grateful respect towards her
- ladyship, and be ever ready to perform those rites and ceremonies
- which are instituted by the Church of England. As a clergyman,
- moreover, I feel it my duty to promote and establish the blessing
- of peace in all families within the reach of my influence; and on
- these grounds I flatter myself that my present overtures are
- highly commendable, and that the circumstance of my being next in
- the entail of Longbourn estate will be kindly overlooked on your
- side, and not lead you to reject the offered olive-branch. I
- cannot be otherwise than concerned at being the means of injuring
- your amiable daughters, and beg leave to apologise for it, as
- well as to assure you of my readiness to make them every possible
- amends—but of this hereafter. If you should have no objection to
- receive me into your house, I propose myself the satisfaction of
- waiting on you and your family, Monday, November 18th, by four
- o’clock, and shall probably trespass on your hospitality till the
- Saturday se’ennight following, which I can do without any
- inconvenience, as Lady Catherine is far from objecting to my
- occasional absence on a Sunday, provided that some other
- clergyman is engaged to do the duty of the day.—I remain, dear
- sir, with respectful compliments to your lady and daughters, your
- well-wisher and friend,
-
- “WILLIAM COLLINS”
-
- “At four o’clock, therefore, we may expect this peace-making
- gentleman,” said Mr. Bennet, as he folded up the letter. “He
- seems to be a most conscientious and polite young man, upon my
- word, and I doubt not will prove a valuable acquaintance,
- especially if Lady Catherine should be so indulgent as to let him
- come to us again.”
-
- “There is some sense in what he says about the girls, however,
- and if he is disposed to make them any amends, I shall not be the
- person to discourage him.”
-
- “Though it is difficult,” said Jane, “to guess in what way he can
- mean to make us the atonement he thinks our due, the wish is
- certainly to his credit.”
-
- Elizabeth was chiefly struck by his extraordinary deference for
- Lady Catherine, and his kind intention of christening, marrying,
- and burying his parishioners whenever it were required.
-
- “He must be an oddity, I think,” said she. “I cannot make him
- out.—There is something very pompous in his style.—And what can
- he mean by apologising for being next in the entail?—We cannot
- suppose he would help it if he could.—Could he be a sensible man,
- sir?”
-
- “No, my dear, I think not. I have great hopes of finding him
- quite the reverse. There is a mixture of servility and
- self-importance in his letter, which promises well. I am
- impatient to see him.”
-
- “In point of composition,” said Mary, “the letter does not seem
- defective. The idea of the olive-branch perhaps is not wholly
- new, yet I think it is well expressed.”
-
- To Catherine and Lydia, neither the letter nor its writer were in
- any degree interesting. It was next to impossible that their
- cousin should come in a scarlet coat, and it was now some weeks
- since they had received pleasure from the society of a man in any
- other colour. As for their mother, Mr. Collins’s letter had done
- away much of her ill-will, and she was preparing to see him with
- a degree of composure which astonished her husband and daughters.
-
- Mr. Collins was punctual to his time, and was received with great
- politeness by the whole family. Mr. Bennet indeed said little;
- but the ladies were ready enough to talk, and Mr. Collins seemed
- neither in need of encouragement, nor inclined to be silent
- himself. He was a tall, heavy-looking young man of
- five-and-twenty. His air was grave and stately, and his manners
- were very formal. He had not been long seated before he
- complimented Mrs. Bennet on having so fine a family of daughters;
- said he had heard much of their beauty, but that in this instance
- fame had fallen short of the truth; and added, that he did not
- doubt her seeing them all in due time disposed of in marriage.
- This gallantry was not much to the taste of some of his hearers;
- but Mrs. Bennet, who quarreled with no compliments, answered most
- readily.
-
- “You are very kind, I am sure; and I wish with all my heart it
- may prove so, for else they will be destitute enough. Things are
- settled so oddly.”
-
- “You allude, perhaps, to the entail of this estate.”
-
- “Ah! sir, I do indeed. It is a grievous affair to my poor girls,
- you must confess. Not that I mean to find fault with _you_, for
- such things I know are all chance in this world. There is no
- knowing how estates will go when once they come to be entailed.”
-
- “I am very sensible, madam, of the hardship to my fair cousins,
- and could say much on the subject, but that I am cautious of
- appearing forward and precipitate. But I can assure the young
- ladies that I come prepared to admire them. At present I will not
- say more; but, perhaps, when we are better acquainted—”
-
- He was interrupted by a summons to dinner; and the girls smiled
- on each other. They were not the only objects of Mr. Collins’s
- admiration. The hall, the dining-room, and all its furniture,
- were examined and praised; and his commendation of everything
- would have touched Mrs. Bennet’s heart, but for the mortifying
- supposition of his viewing it all as his own future property. The
- dinner too in its turn was highly admired; and he begged to know
- to which of his fair cousins the excellency of its cooking was
- owing. But he was set right there by Mrs. Bennet, who assured him
- with some asperity that they were very well able to keep a good
- cook, and that her daughters had nothing to do in the kitchen. He
- begged pardon for having displeased her. In a softened tone she
- declared herself not at all offended; but he continued to
- apologise for about a quarter of an hour.
-
-
-
-
- Chapter 14
-
- During dinner, Mr. Bennet scarcely spoke at all; but when the
- servants were withdrawn, he thought it time to have some
- conversation with his guest, and therefore started a subject in
- which he expected him to shine, by observing that he seemed very
- fortunate in his patroness. Lady Catherine de Bourgh’s attention
- to his wishes, and consideration for his comfort, appeared very
- remarkable. Mr. Bennet could not have chosen better. Mr. Collins
- was eloquent in her praise. The subject elevated him to more than
- usual solemnity of manner, and with a most important aspect he
- protested that “he had never in his life witnessed such behaviour
- in a person of rank—such affability and condescension, as he had
- himself experienced from Lady Catherine. She had been graciously
- pleased to approve of both of the discourses which he had already
- had the honour of preaching before her. She had also asked him
- twice to dine at Rosings, and had sent for him only the Saturday
- before, to make up her pool of quadrille in the evening. Lady
- Catherine was reckoned proud by many people he knew, but _he_ had
- never seen anything but affability in her. She had always spoken
- to him as she would to any other gentleman; she made not the
- smallest objection to his joining in the society of the
- neighbourhood nor to his leaving the parish occasionally for a
- week or two, to visit his relations. She had even condescended to
- advise him to marry as soon as he could, provided he chose with
- discretion; and had once paid him a visit in his humble
- parsonage, where she had perfectly approved all the alterations
- he had been making, and had even vouchsafed to suggest some
- herself—some shelves in the closet up stairs.”
-
- “That is all very proper and civil, I am sure,” said Mrs. Bennet,
- “and I dare say she is a very agreeable woman. It is a pity that
- great ladies in general are not more like her. Does she live near
- you, sir?”
-
- “The garden in which stands my humble abode is separated only by
- a lane from Rosings Park, her ladyship’s residence.”
-
- “I think you said she was a widow, sir? Has she any family?”
-
- “She has only one daughter, the heiress of Rosings, and of very
- extensive property.”
-
- “Ah!” said Mrs. Bennet, shaking her head, “then she is better off
- than many girls. And what sort of young lady is she? Is she
- handsome?”
-
- “She is a most charming young lady indeed. Lady Catherine herself
- says that, in point of true beauty, Miss de Bourgh is far
- superior to the handsomest of her sex, because there is that in
- her features which marks the young lady of distinguished birth.
- She is unfortunately of a sickly constitution, which has
- prevented her from making that progress in many accomplishments
- which she could not have otherwise failed of, as I am informed by
- the lady who superintended her education, and who still resides
- with them. But she is perfectly amiable, and often condescends to
- drive by my humble abode in her little phaeton and ponies.”
-
- “Has she been presented? I do not remember her name among the
- ladies at court.”
-
- “Her indifferent state of health unhappily prevents her being in
- town; and by that means, as I told Lady Catherine one day, has
- deprived the British court of its brightest ornament. Her
- ladyship seemed pleased with the idea; and you may imagine that I
- am happy on every occasion to offer those little delicate
- compliments which are always acceptable to ladies. I have more
- than once observed to Lady Catherine, that her charming daughter
- seemed born to be a duchess, and that the most elevated rank,
- instead of giving her consequence, would be adorned by her. These
- are the kind of little things which please her ladyship, and it
- is a sort of attention which I conceive myself peculiarly bound
- to pay.”
-
- “You judge very properly,” said Mr. Bennet, “and it is happy for
- you that you possess the talent of flattering with delicacy. May
- I ask whether these pleasing attentions proceed from the impulse
- of the moment, or are the result of previous study?”
-
- “They arise chiefly from what is passing at the time, and though
- I sometimes amuse myself with suggesting and arranging such
- little elegant compliments as may be adapted to ordinary
- occasions, I always wish to give them as unstudied an air as
- possible.”
-
- Mr. Bennet’s expectations were fully answered. His cousin was as
- absurd as he had hoped, and he listened to him with the keenest
- enjoyment, maintaining at the same time the most resolute
- composure of countenance, and, except in an occasional glance at
- Elizabeth, requiring no partner in his pleasure.
-
- By tea-time, however, the dose had been enough, and Mr. Bennet
- was glad to take his guest into the drawing-room again, and, when
- tea was over, glad to invite him to read aloud to the ladies. Mr.
- Collins readily assented, and a book was produced; but, on
- beholding it (for everything announced it to be from a
- circulating library), he started back, and begging pardon,
- protested that he never read novels. Kitty stared at him, and
- Lydia exclaimed. Other books were produced, and after some
- deliberation he chose Fordyce’s Sermons. Lydia gaped as he opened
- the volume, and before he had, with very monotonous solemnity,
- read three pages, she interrupted him with:
-
- “Do you know, mamma, that my uncle Phillips talks of turning away
- Richard; and if he does, Colonel Forster will hire him. My aunt
- told me so herself on Saturday. I shall walk to Meryton to-morrow
- to hear more about it, and to ask when Mr. Denny comes back from
- town.”
-
- Lydia was bid by her two eldest sisters to hold her tongue; but
- Mr. Collins, much offended, laid aside his book, and said:
-
- “I have often observed how little young ladies are interested by
- books of a serious stamp, though written solely for their
- benefit. It amazes me, I confess; for, certainly, there can be
- nothing so advantageous to them as instruction. But I will no
- longer importune my young cousin.”
-
- Then turning to Mr. Bennet, he offered himself as his antagonist
- at backgammon. Mr. Bennet accepted the challenge, observing that
- he acted very wisely in leaving the girls to their own trifling
- amusements. Mrs. Bennet and her daughters apologised most civilly
- for Lydia’s interruption, and promised that it should not occur
- again, if he would resume his book; but Mr. Collins, after
- assuring them that he bore his young cousin no ill-will, and
- should never resent her behaviour as any affront, seated himself
- at another table with Mr. Bennet, and prepared for backgammon.
-
-
-
-
- Chapter 15
-
- Mr. Collins was not a sensible man, and the deficiency of nature
- had been but little assisted by education or society; the
- greatest part of his life having been spent under the guidance of
- an illiterate and miserly father; and though he belonged to one
- of the universities, he had merely kept the necessary terms,
- without forming at it any useful acquaintance. The subjection in
- which his father had brought him up had given him originally
- great humility of manner; but it was now a good deal counteracted
- by the self-conceit of a weak head, living in retirement, and the
- consequential feelings of early and unexpected prosperity. A
- fortunate chance had recommended him to Lady Catherine de Bourgh
- when the living of Hunsford was vacant; and the respect which he
- felt for her high rank, and his veneration for her as his
- patroness, mingling with a very good opinion of himself, of his
- authority as a clergyman, and his right as a rector, made him
- altogether a mixture of pride and obsequiousness, self-importance
- and humility.
-
- Having now a good house and a very sufficient income, he intended
- to marry; and in seeking a reconciliation with the Longbourn
- family he had a wife in view, as he meant to choose one of the
- daughters, if he found them as handsome and amiable as they were
- represented by common report. This was his plan of amends—of
- atonement—for inheriting their father’s estate; and he thought it
- an excellent one, full of eligibility and suitableness, and
- excessively generous and disinterested on his own part.
-
- His plan did not vary on seeing them. Miss Bennet’s lovely face
- confirmed his views, and established all his strictest notions of
- what was due to seniority; and for the first evening _she_ was
- his settled choice. The next morning, however, made an
- alteration; for in a quarter of an hour’s _tête-à-tête_ with Mrs.
- Bennet before breakfast, a conversation beginning with his
- parsonage-house, and leading naturally to the avowal of his
- hopes, that a mistress might be found for it at Longbourn,
- produced from her, amid very complaisant smiles and general
- encouragement, a caution against the very Jane he had fixed on.
- “As to her _younger_ daughters, she could not take upon her to
- say—she could not positively answer—but she did not _know_ of any
- prepossession; her _eldest_ daughter, she must just mention—she
- felt it incumbent on her to hint, was likely to be very soon
- engaged.”
-
- Mr. Collins had only to change from Jane to Elizabeth—and it was
- soon done—done while Mrs. Bennet was stirring the fire.
- Elizabeth, equally next to Jane in birth and beauty, succeeded
- her of course.
-
- Mrs. Bennet treasured up the hint, and trusted that she might
- soon have two daughters married; and the man whom she could not
- bear to speak of the day before was now high in her good graces.
-
- Lydia’s intention of walking to Meryton was not forgotten; every
- sister except Mary agreed to go with her; and Mr. Collins was to
- attend them, at the request of Mr. Bennet, who was most anxious
- to get rid of him, and have his library to himself; for thither
- Mr. Collins had followed him after breakfast; and there he would
- continue, nominally engaged with one of the largest folios in the
- collection, but really talking to Mr. Bennet, with little
- cessation, of his house and garden at Hunsford. Such doings
- discomposed Mr. Bennet exceedingly. In his library he had been
- always sure of leisure and tranquillity; and though prepared, as
- he told Elizabeth, to meet with folly and conceit in every other
- room of the house, he was used to be free from them there; his
- civility, therefore, was most prompt in inviting Mr. Collins to
- join his daughters in their walk; and Mr. Collins, being in fact
- much better fitted for a walker than a reader, was extremely
- pleased to close his large book, and go.
-
- In pompous nothings on his side, and civil assents on that of his
- cousins, their time passed till they entered Meryton. The
- attention of the younger ones was then no longer to be gained by
- _him_. Their eyes were immediately wandering up in the street in
- quest of the officers, and nothing less than a very smart bonnet
- indeed, or a really new muslin in a shop window, could recall
- them.
-
- But the attention of every lady was soon caught by a young man,
- whom they had never seen before, of most gentlemanlike
- appearance, walking with another officer on the other side of the
- way. The officer was the very Mr. Denny concerning whose return
- from London Lydia came to inquire, and he bowed as they passed.
- All were struck with the stranger’s air, all wondered who he
- could be; and Kitty and Lydia, determined if possible to find
- out, led the way across the street, under pretense of wanting
- something in an opposite shop, and fortunately had just gained
- the pavement when the two gentlemen, turning back, had reached
- the same spot. Mr. Denny addressed them directly, and entreated
- permission to introduce his friend, Mr. Wickham, who had returned
- with him the day before from town, and he was happy to say had
- accepted a commission in their corps. This was exactly as it
- should be; for the young man wanted only regimentals to make him
- completely charming. His appearance was greatly in his favour; he
- had all the best part of beauty, a fine countenance, a good
- figure, and very pleasing address. The introduction was followed
- up on his side by a happy readiness of conversation—a readiness
- at the same time perfectly correct and unassuming; and the whole
- party were still standing and talking together very agreeably,
- when the sound of horses drew their notice, and Darcy and Bingley
- were seen riding down the street. On distinguishing the ladies of
- the group, the two gentlemen came directly towards them, and
- began the usual civilities. Bingley was the principal spokesman,
- and Miss Bennet the principal object. He was then, he said, on
- his way to Longbourn on purpose to inquire after her. Mr. Darcy
- corroborated it with a bow, and was beginning to determine not to
- fix his eyes on Elizabeth, when they were suddenly arrested by
- the sight of the stranger, and Elizabeth happening to see the
- countenance of both as they looked at each other, was all
- astonishment at the effect of the meeting. Both changed colour,
- one looked white, the other red. Mr. Wickham, after a few
- moments, touched his hat—a salutation which Mr. Darcy just
- deigned to return. What could be the meaning of it? It was
- impossible to imagine; it was impossible not to long to know.
-
- In another minute, Mr. Bingley, but without seeming to have
- noticed what passed, took leave and rode on with his friend.
-
- Mr. Denny and Mr. Wickham walked with the young ladies to the
- door of Mr. Phillip’s house, and then made their bows, in spite
- of Miss Lydia’s pressing entreaties that they should come in, and
- even in spite of Mrs. Phillips’s throwing up the parlour window
- and loudly seconding the invitation.
-
- Mrs. Phillips was always glad to see her nieces; and the two
- eldest, from their recent absence, were particularly welcome, and
- she was eagerly expressing her surprise at their sudden return
- home, which, as their own carriage had not fetched them, she
- should have known nothing about, if she had not happened to see
- Mr. Jones’s shop-boy in the street, who had told her that they
- were not to send any more draughts to Netherfield because the
- Miss Bennets were come away, when her civility was claimed
- towards Mr. Collins by Jane’s introduction of him. She received
- him with her very best politeness, which he returned with as much
- more, apologising for his intrusion, without any previous
- acquaintance with her, which he could not help flattering
- himself, however, might be justified by his relationship to the
- young ladies who introduced him to her notice. Mrs. Phillips was
- quite awed by such an excess of good breeding; but her
- contemplation of one stranger was soon put to an end by
- exclamations and inquiries about the other; of whom, however, she
- could only tell her nieces what they already knew, that Mr. Denny
- had brought him from London, and that he was to have a
- lieutenant’s commission in the ——shire. She had been watching him
- the last hour, she said, as he walked up and down the street, and
- had Mr. Wickham appeared, Kitty and Lydia would certainly have
- continued the occupation, but unluckily no one passed windows now
- except a few of the officers, who, in comparison with the
- stranger, were become “stupid, disagreeable fellows.” Some of
- them were to dine with the Phillipses the next day, and their
- aunt promised to make her husband call on Mr. Wickham, and give
- him an invitation also, if the family from Longbourn would come
- in the evening. This was agreed to, and Mrs. Phillips protested
- that they would have a nice comfortable noisy game of lottery
- tickets, and a little bit of hot supper afterwards. The prospect
- of such delights was very cheering, and they parted in mutual
- good spirits. Mr. Collins repeated his apologies in quitting the
- room, and was assured with unwearying civility that they were
- perfectly needless.
-
- As they walked home, Elizabeth related to Jane what she had seen
- pass between the two gentlemen; but though Jane would have
- defended either or both, had they appeared to be in the wrong,
- she could no more explain such behaviour than her sister.
-
- Mr. Collins on his return highly gratified Mrs. Bennet by
- admiring Mrs. Phillips’s manners and politeness. He protested
- that, except Lady Catherine and her daughter, he had never seen a
- more elegant woman; for she had not only received him with the
- utmost civility, but even pointedly included him in her
- invitation for the next evening, although utterly unknown to her
- before. Something, he supposed, might be attributed to his
- connection with them, but yet he had never met with so much
- attention in the whole course of his life.
-
-
-
-
- Chapter 16
-
- As no objection was made to the young people’s engagement with
- their aunt, and all Mr. Collins’s scruples of leaving Mr. and
- Mrs. Bennet for a single evening during his visit were most
- steadily resisted, the coach conveyed him and his five cousins at
- a suitable hour to Meryton; and the girls had the pleasure of
- hearing, as they entered the drawing-room, that Mr. Wickham had
- accepted their uncle’s invitation, and was then in the house.
-
- When this information was given, and they had all taken their
- seats, Mr. Collins was at leisure to look around him and admire,
- and he was so much struck with the size and furniture of the
- apartment, that he declared he might almost have supposed himself
- in the small summer breakfast parlour at Rosings; a comparison
- that did not at first convey much gratification; but when Mrs.
- Phillips understood from him what Rosings was, and who was its
- proprietor—when she had listened to the description of only one
- of Lady Catherine’s drawing-rooms, and found that the
- chimney-piece alone had cost eight hundred pounds, she felt all
- the force of the compliment, and would hardly have resented a
- comparison with the housekeeper’s room.
-
- In describing to her all the grandeur of Lady Catherine and her
- mansion, with occasional digressions in praise of his own humble
- abode, and the improvements it was receiving, he was happily
- employed until the gentlemen joined them; and he found in Mrs.
- Phillips a very attentive listener, whose opinion of his
- consequence increased with what she heard, and who was resolving
- to retail it all among her neighbours as soon as she could. To
- the girls, who could not listen to their cousin, and who had
- nothing to do but to wish for an instrument, and examine their
- own indifferent imitations of china on the mantelpiece, the
- interval of waiting appeared very long. It was over at last,
- however. The gentlemen did approach, and when Mr. Wickham walked
- into the room, Elizabeth felt that she had neither been seeing
- him before, nor thinking of him since, with the smallest degree
- of unreasonable admiration. The officers of the ——shire were in
- general a very creditable, gentlemanlike set, and the best of
- them were of the present party; but Mr. Wickham was as far beyond
- them all in person, countenance, air, and walk, as _they_ were
- superior to the broad-faced, stuffy uncle Phillips, breathing
- port wine, who followed them into the room.
-
- Mr. Wickham was the happy man towards whom almost every female
- eye was turned, and Elizabeth was the happy woman by whom he
- finally seated himself; and the agreeable manner in which he
- immediately fell into conversation, though it was only on its
- being a wet night, made her feel that the commonest, dullest,
- most threadbare topic might be rendered interesting by the skill
- of the speaker.
-
- With such rivals for the notice of the fair as Mr. Wickham and
- the officers, Mr. Collins seemed to sink into insignificance; to
- the young ladies he certainly was nothing; but he had still at
- intervals a kind listener in Mrs. Phillips, and was by her
- watchfulness, most abundantly supplied with coffee and muffin.
- When the card-tables were placed, he had the opportunity of
- obliging her in turn, by sitting down to whist.
-
- “I know little of the game at present,” said he, “but I shall be
- glad to improve myself, for in my situation in life—” Mrs.
- Phillips was very glad for his compliance, but could not wait for
- his reason.
-
- Mr. Wickham did not play at whist, and with ready delight was he
- received at the other table between Elizabeth and Lydia. At first
- there seemed danger of Lydia’s engrossing him entirely, for she
- was a most determined talker; but being likewise extremely fond
- of lottery tickets, she soon grew too much interested in the
- game, too eager in making bets and exclaiming after prizes to
- have attention for anyone in particular. Allowing for the common
- demands of the game, Mr. Wickham was therefore at leisure to talk
- to Elizabeth, and she was very willing to hear him, though what
- she chiefly wished to hear she could not hope to be told—the
- history of his acquaintance with Mr. Darcy. She dared not even
- mention that gentleman. Her curiosity, however, was unexpectedly
- relieved. Mr. Wickham began the subject himself. He inquired how
- far Netherfield was from Meryton; and, after receiving her
- answer, asked in a hesitating manner how long Mr. Darcy had been
- staying there.
-
- “About a month,” said Elizabeth; and then, unwilling to let the
- subject drop, added, “He is a man of very large property in
- Derbyshire, I understand.”
-
- “Yes,” replied Mr. Wickham; “his estate there is a noble one. A
- clear ten thousand per annum. You could not have met with a
- person more capable of giving you certain information on that
- head than myself, for I have been connected with his family in a
- particular manner from my infancy.”
-
- Elizabeth could not but look surprised.
-
- “You may well be surprised, Miss Bennet, at such an assertion,
- after seeing, as you probably might, the very cold manner of our
- meeting yesterday. Are you much acquainted with Mr. Darcy?”
-
- “As much as I ever wish to be,” cried Elizabeth very warmly. “I
- have spent four days in the same house with him, and I think him
- very disagreeable.”
-
- “I have no right to give _my_ opinion,” said Wickham, “as to his
- being agreeable or otherwise. I am not qualified to form one. I
- have known him too long and too well to be a fair judge. It is
- impossible for _me_ to be impartial. But I believe your opinion
- of him would in general astonish—and perhaps you would not
- express it quite so strongly anywhere else. Here you are in your
- own family.”
-
- “Upon my word, I say no more _here_ than I might say in any house
- in the neighbourhood, except Netherfield. He is not at all liked
- in Hertfordshire. Everybody is disgusted with his pride. You will
- not find him more favourably spoken of by anyone.”
-
- “I cannot pretend to be sorry,” said Wickham, after a short
- interruption, “that he or that any man should not be estimated
- beyond their deserts; but with _him_ I believe it does not often
- happen. The world is blinded by his fortune and consequence, or
- frightened by his high and imposing manners, and sees him only as
- he chooses to be seen.”
-
- “I should take him, even on _my_ slight acquaintance, to be an
- ill-tempered man.” Wickham only shook his head.
-
- “I wonder,” said he, at the next opportunity of speaking,
- “whether he is likely to be in this country much longer.”
-
- “I do not at all know; but I _heard_ nothing of his going away
- when I was at Netherfield. I hope your plans in favour of the
- ——shire will not be affected by his being in the neighbourhood.”
-
- “Oh! no—it is not for _me_ to be driven away by Mr. Darcy. If
- _he_ wishes to avoid seeing _me_, he must go. We are not on
- friendly terms, and it always gives me pain to meet him, but I
- have no reason for avoiding _him_ but what I might proclaim
- before all the world, a sense of very great ill-usage, and most
- painful regrets at his being what he is. His father, Miss Bennet,
- the late Mr. Darcy, was one of the best men that ever breathed,
- and the truest friend I ever had; and I can never be in company
- with this Mr. Darcy without being grieved to the soul by a
- thousand tender recollections. His behaviour to myself has been
- scandalous; but I verily believe I could forgive him anything and
- everything, rather than his disappointing the hopes and
- disgracing the memory of his father.”
-
- Elizabeth found the interest of the subject increase, and
- listened with all her heart; but the delicacy of it prevented
- further inquiry.
-
- Mr. Wickham began to speak on more general topics, Meryton, the
- neighbourhood, the society, appearing highly pleased with all
- that he had yet seen, and speaking of the latter with gentle but
- very intelligible gallantry.
-
- “It was the prospect of constant society, and good society,” he
- added, “which was my chief inducement to enter the ——shire. I
- knew it to be a most respectable, agreeable corps, and my friend
- Denny tempted me further by his account of their present
- quarters, and the very great attentions and excellent
- acquaintances Meryton had procured them. Society, I own, is
- necessary to me. I have been a disappointed man, and my spirits
- will not bear solitude. I _must_ have employment and society. A
- military life is not what I was intended for, but circumstances
- have now made it eligible. The church _ought_ to have been my
- profession—I was brought up for the church, and I should at this
- time have been in possession of a most valuable living, had it
- pleased the gentleman we were speaking of just now.”
-
- “Indeed!”
-
- “Yes—the late Mr. Darcy bequeathed me the next presentation of
- the best living in his gift. He was my godfather, and excessively
- attached to me. I cannot do justice to his kindness. He meant to
- provide for me amply, and thought he had done it; but when the
- living fell, it was given elsewhere.”
-
- “Good heavens!” cried Elizabeth; “but how could _that_ be? How
- could his will be disregarded? Why did you not seek legal
- redress?”
-
- “There was just such an informality in the terms of the bequest
- as to give me no hope from law. A man of honour could not have
- doubted the intention, but Mr. Darcy chose to doubt it—or to
- treat it as a merely conditional recommendation, and to assert
- that I had forfeited all claim to it by extravagance,
- imprudence—in short anything or nothing. Certain it is, that the
- living became vacant two years ago, exactly as I was of an age to
- hold it, and that it was given to another man; and no less
- certain is it, that I cannot accuse myself of having really done
- anything to deserve to lose it. I have a warm, unguarded temper,
- and I may have spoken my opinion _of_ him, and _to_ him, too
- freely. I can recall nothing worse. But the fact is, that we are
- very different sort of men, and that he hates me.”
-
- “This is quite shocking! He deserves to be publicly disgraced.”
-
- “Some time or other he _will_ be—but it shall not be by _me_.
- Till I can forget his father, I can never defy or expose _him_.”
-
- Elizabeth honoured him for such feelings, and thought him
- handsomer than ever as he expressed them.
-
- “But what,” said she, after a pause, “can have been his motive?
- What can have induced him to behave so cruelly?”
-
- “A thorough, determined dislike of me—a dislike which I cannot
- but attribute in some measure to jealousy. Had the late Mr. Darcy
- liked me less, his son might have borne with me better; but his
- father’s uncommon attachment to me irritated him, I believe, very
- early in life. He had not a temper to bear the sort of
- competition in which we stood—the sort of preference which was
- often given me.”
-
- “I had not thought Mr. Darcy so bad as this—though I have never
- liked him. I had not thought so very ill of him. I had supposed
- him to be despising his fellow-creatures in general, but did not
- suspect him of descending to such malicious revenge, such
- injustice, such inhumanity as this.”
-
- After a few minutes’ reflection, however, she continued, “I _do_
- remember his boasting one day, at Netherfield, of the
- implacability of his resentments, of his having an unforgiving
- temper. His disposition must be dreadful.”
-
- “I will not trust myself on the subject,” replied Wickham; “_I_
- can hardly be just to him.”
-
- Elizabeth was again deep in thought, and after a time exclaimed,
- “To treat in such a manner the godson, the friend, the favourite
- of his father!” She could have added, “A young man, too, like
- _you_, whose very countenance may vouch for your being
- amiable”—but she contented herself with, “and one, too, who had
- probably been his companion from childhood, connected together,
- as I think you said, in the closest manner!”
-
- “We were born in the same parish, within the same park; the
- greatest part of our youth was passed together; inmates of the
- same house, sharing the same amusements, objects of the same
- parental care. _My_ father began life in the profession which
- your uncle, Mr. Phillips, appears to do so much credit to—but he
- gave up everything to be of use to the late Mr. Darcy and devoted
- all his time to the care of the Pemberley property. He was most
- highly esteemed by Mr. Darcy, a most intimate, confidential
- friend. Mr. Darcy often acknowledged himself to be under the
- greatest obligations to my father’s active superintendence, and
- when, immediately before my father’s death, Mr. Darcy gave him a
- voluntary promise of providing for me, I am convinced that he
- felt it to be as much a debt of gratitude to _him_, as of his
- affection to myself.”
-
- “How strange!” cried Elizabeth. “How abominable! I wonder that
- the very pride of this Mr. Darcy has not made him just to you! If
- from no better motive, that he should not have been too proud to
- be dishonest—for dishonesty I must call it.”
-
- “It _is_ wonderful,” replied Wickham, “for almost all his actions
- may be traced to pride; and pride had often been his best friend.
- It has connected him nearer with virtue than with any other
- feeling. But we are none of us consistent, and in his behaviour
- to me there were stronger impulses even than pride.”
-
- “Can such abominable pride as his have ever done him good?”
-
- “Yes. It has often led him to be liberal and generous, to give
- his money freely, to display hospitality, to assist his tenants,
- and relieve the poor. Family pride, and _filial_ pride—for he is
- very proud of what his father was—have done this. Not to appear
- to disgrace his family, to degenerate from the popular qualities,
- or lose the influence of the Pemberley House, is a powerful
- motive. He has also _brotherly_ pride, which, with _some_
- brotherly affection, makes him a very kind and careful guardian
- of his sister, and you will hear him generally cried up as the
- most attentive and best of brothers.”
-
- “What sort of girl is Miss Darcy?”
-
- He shook his head. “I wish I could call her amiable. It gives me
- pain to speak ill of a Darcy. But she is too much like her
- brother—very, very proud. As a child, she was affectionate and
- pleasing, and extremely fond of me; and I have devoted hours and
- hours to her amusement. But she is nothing to me now. She is a
- handsome girl, about fifteen or sixteen, and, I understand,
- highly accomplished. Since her father’s death, her home has been
- London, where a lady lives with her, and superintends her
- education.”
-
- After many pauses and many trials of other subjects, Elizabeth
- could not help reverting once more to the first, and saying:
-
- “I am astonished at his intimacy with Mr. Bingley! How can Mr.
- Bingley, who seems good humour itself, and is, I really believe,
- truly amiable, be in friendship with such a man? How can they
- suit each other? Do you know Mr. Bingley?”
-
- “Not at all.”
-
- “He is a sweet-tempered, amiable, charming man. He cannot know
- what Mr. Darcy is.”
-
- “Probably not; but Mr. Darcy can please where he chooses. He does
- not want abilities. He can be a conversible companion if he
- thinks it worth his while. Among those who are at all his equals
- in consequence, he is a very different man from what he is to the
- less prosperous. His pride never deserts him; but with the rich
- he is liberal-minded, just, sincere, rational, honourable, and
- perhaps agreeable—allowing something for fortune and figure.”
-
- The whist party soon afterwards breaking up, the players gathered
- round the other table and Mr. Collins took his station between
- his cousin Elizabeth and Mrs. Phillips. The usual inquiries as to
- his success were made by the latter. It had not been very great;
- he had lost every point; but when Mrs. Phillips began to express
- her concern thereupon, he assured her with much earnest gravity
- that it was not of the least importance, that he considered the
- money as a mere trifle, and begged that she would not make
- herself uneasy.
-
- “I know very well, madam,” said he, “that when persons sit down
- to a card-table, they must take their chances of these things,
- and happily I am not in such circumstances as to make five
- shillings any object. There are undoubtedly many who could not
- say the same, but thanks to Lady Catherine de Bourgh, I am
- removed far beyond the necessity of regarding little matters.”
-
- Mr. Wickham’s attention was caught; and after observing Mr.
- Collins for a few moments, he asked Elizabeth in a low voice
- whether her relation was very intimately acquainted with the
- family of de Bourgh.
-
- “Lady Catherine de Bourgh,” she replied, “has very lately given
- him a living. I hardly know how Mr. Collins was first introduced
- to her notice, but he certainly has not known her long.”
-
- “You know of course that Lady Catherine de Bourgh and Lady Anne
- Darcy were sisters; consequently that she is aunt to the present
- Mr. Darcy.”
-
- “No, indeed, I did not. I knew nothing at all of Lady Catherine’s
- connections. I never heard of her existence till the day before
- yesterday.”
-
- “Her daughter, Miss de Bourgh, will have a very large fortune,
- and it is believed that she and her cousin will unite the two
- estates.”
-
- This information made Elizabeth smile, as she thought of poor
- Miss Bingley. Vain indeed must be all her attentions, vain and
- useless her affection for his sister and her praise of himself,
- if he were already self-destined for another.
-
- “Mr. Collins,” said she, “speaks highly both of Lady Catherine
- and her daughter; but from some particulars that he has related
- of her ladyship, I suspect his gratitude misleads him, and that
- in spite of her being his patroness, she is an arrogant,
- conceited woman.”
-
- “I believe her to be both in a great degree,” replied Wickham; “I
- have not seen her for many years, but I very well remember that I
- never liked her, and that her manners were dictatorial and
- insolent. She has the reputation of being remarkably sensible and
- clever; but I rather believe she derives part of her abilities
- from her rank and fortune, part from her authoritative manner,
- and the rest from the pride for her nephew, who chooses that
- everyone connected with him should have an understanding of the
- first class.”
-
- Elizabeth allowed that he had given a very rational account of
- it, and they continued talking together, with mutual satisfaction
- till supper put an end to cards, and gave the rest of the ladies
- their share of Mr. Wickham’s attentions. There could be no
- conversation in the noise of Mrs. Phillips’s supper party, but
- his manners recommended him to everybody. Whatever he said, was
- said well; and whatever he did, done gracefully. Elizabeth went
- away with her head full of him. She could think of nothing but of
- Mr. Wickham, and of what he had told her, all the way home; but
- there was not time for her even to mention his name as they went,
- for neither Lydia nor Mr. Collins were once silent. Lydia talked
- incessantly of lottery tickets, of the fish she had lost and the
- fish she had won; and Mr. Collins in describing the civility of
- Mr. and Mrs. Phillips, protesting that he did not in the least
- regard his losses at whist, enumerating all the dishes at supper,
- and repeatedly fearing that he crowded his cousins, had more to
- say than he could well manage before the carriage stopped at
- Longbourn House.
-
-
-
-
- Chapter 17
-
- Elizabeth related to Jane the next day what had passed between
- Mr. Wickham and herself. Jane listened with astonishment and
- concern; she knew not how to believe that Mr. Darcy could be so
- unworthy of Mr. Bingley’s regard; and yet, it was not in her
- nature to question the veracity of a young man of such amiable
- appearance as Wickham. The possibility of his having endured such
- unkindness, was enough to interest all her tender feelings; and
- nothing remained therefore to be done, but to think well of them
- both, to defend the conduct of each, and throw into the account
- of accident or mistake whatever could not be otherwise explained.
-
- “They have both,” said she, “been deceived, I dare say, in some
- way or other, of which we can form no idea. Interested people
- have perhaps misrepresented each to the other. It is, in short,
- impossible for us to conjecture the causes or circumstances which
- may have alienated them, without actual blame on either side.”
-
- “Very true, indeed; and now, my dear Jane, what have you got to
- say on behalf of the interested people who have probably been
- concerned in the business? Do clear _them_ too, or we shall be
- obliged to think ill of somebody.”
-
- “Laugh as much as you choose, but you will not laugh me out of my
- opinion. My dearest Lizzy, do but consider in what a disgraceful
- light it places Mr. Darcy, to be treating his father’s favourite
- in such a manner, one whom his father had promised to provide
- for. It is impossible. No man of common humanity, no man who had
- any value for his character, could be capable of it. Can his most
- intimate friends be so excessively deceived in him? Oh! no.”
-
- “I can much more easily believe Mr. Bingley’s being imposed on,
- than that Mr. Wickham should invent such a history of himself as
- he gave me last night; names, facts, everything mentioned without
- ceremony. If it be not so, let Mr. Darcy contradict it. Besides,
- there was truth in his looks.”
-
- “It is difficult indeed—it is distressing. One does not know what
- to think.”
-
- “I beg your pardon; one knows exactly what to think.”
-
- But Jane could think with certainty on only one point—that Mr.
- Bingley, if he _had been_ imposed on, would have much to suffer
- when the affair became public.
-
- The two young ladies were summoned from the shrubbery, where this
- conversation passed, by the arrival of the very persons of whom
- they had been speaking; Mr. Bingley and his sisters came to give
- their personal invitation for the long-expected ball at
- Netherfield, which was fixed for the following Tuesday. The two
- ladies were delighted to see their dear friend again, called it
- an age since they had met, and repeatedly asked what she had been
- doing with herself since their separation. To the rest of the
- family they paid little attention; avoiding Mrs. Bennet as much
- as possible, saying not much to Elizabeth, and nothing at all to
- the others. They were soon gone again, rising from their seats
- with an activity which took their brother by surprise, and
- hurrying off as if eager to escape from Mrs. Bennet’s civilities.
-
- The prospect of the Netherfield ball was extremely agreeable to
- every female of the family. Mrs. Bennet chose to consider it as
- given in compliment to her eldest daughter, and was particularly
- flattered by receiving the invitation from Mr. Bingley himself,
- instead of a ceremonious card. Jane pictured to herself a happy
- evening in the society of her two friends, and the attentions of
- their brother; and Elizabeth thought with pleasure of dancing a
- great deal with Mr. Wickham, and of seeing a confirmation of
- everything in Mr. Darcy’s look and behaviour. The happiness
- anticipated by Catherine and Lydia depended less on any single
- event, or any particular person, for though they each, like
- Elizabeth, meant to dance half the evening with Mr. Wickham, he
- was by no means the only partner who could satisfy them, and a
- ball was, at any rate, a ball. And even Mary could assure her
- family that she had no disinclination for it.
-
- “While I can have my mornings to myself,” said she, “it is
- enough—I think it is no sacrifice to join occasionally in evening
- engagements. Society has claims on us all; and I profess myself
- one of those who consider intervals of recreation and amusement
- as desirable for everybody.”
-
- Elizabeth’s spirits were so high on this occasion, that though
- she did not often speak unnecessarily to Mr. Collins, she could
- not help asking him whether he intended to accept Mr. Bingley’s
- invitation, and if he did, whether he would think it proper to
- join in the evening’s amusement; and she was rather surprised to
- find that he entertained no scruple whatever on that head, and
- was very far from dreading a rebuke either from the Archbishop,
- or Lady Catherine de Bourgh, by venturing to dance.
-
- “I am by no means of the opinion, I assure you,” said he, “that a
- ball of this kind, given by a young man of character, to
- respectable people, can have any evil tendency; and I am so far
- from objecting to dancing myself, that I shall hope to be
- honoured with the hands of all my fair cousins in the course of
- the evening; and I take this opportunity of soliciting yours,
- Miss Elizabeth, for the two first dances especially, a preference
- which I trust my cousin Jane will attribute to the right cause,
- and not to any disrespect for her.”
-
- Elizabeth felt herself completely taken in. She had fully
- proposed being engaged by Mr. Wickham for those very dances; and
- to have Mr. Collins instead! her liveliness had never been worse
- timed. There was no help for it, however. Mr. Wickham’s happiness
- and her own were perforce delayed a little longer, and Mr.
- Collins’s proposal accepted with as good a grace as she could.
- She was not the better pleased with his gallantry from the idea
- it suggested of something more. It now first struck her, that
- _she_ was selected from among her sisters as worthy of being
- mistress of Hunsford Parsonage, and of assisting to form a
- quadrille table at Rosings, in the absence of more eligible
- visitors. The idea soon reached to conviction, as she observed
- his increasing civilities toward herself, and heard his frequent
- attempt at a compliment on her wit and vivacity; and though more
- astonished than gratified herself by this effect of her charms,
- it was not long before her mother gave her to understand that the
- probability of their marriage was extremely agreeable to _her_.
- Elizabeth, however, did not choose to take the hint, being well
- aware that a serious dispute must be the consequence of any
- reply. Mr. Collins might never make the offer, and till he did,
- it was useless to quarrel about him.
-
- If there had not been a Netherfield ball to prepare for and talk
- of, the younger Miss Bennets would have been in a very pitiable
- state at this time, for from the day of the invitation, to the
- day of the ball, there was such a succession of rain as prevented
- their walking to Meryton once. No aunt, no officers, no news
- could be sought after—the very shoe-roses for Netherfield were
- got by proxy. Even Elizabeth might have found some trial of her
- patience in weather which totally suspended the improvement of
- her acquaintance with Mr. Wickham; and nothing less than a dance
- on Tuesday, could have made such a Friday, Saturday, Sunday, and
- Monday endurable to Kitty and Lydia.
-
-
-
-
- Chapter 18
-
- Till Elizabeth entered the drawing-room at Netherfield, and
- looked in vain for Mr. Wickham among the cluster of red coats
- there assembled, a doubt of his being present had never occurred
- to her. The certainty of meeting him had not been checked by any
- of those recollections that might not unreasonably have alarmed
- her. She had dressed with more than usual care, and prepared in
- the highest spirits for the conquest of all that remained
- unsubdued of his heart, trusting that it was not more than might
- be won in the course of the evening. But in an instant arose the
- dreadful suspicion of his being purposely omitted for Mr. Darcy’s
- pleasure in the Bingleys’ invitation to the officers; and though
- this was not exactly the case, the absolute fact of his absence
- was pronounced by his friend Denny, to whom Lydia eagerly
- applied, and who told them that Wickham had been obliged to go to
- town on business the day before, and was not yet returned;
- adding, with a significant smile, “I do not imagine his business
- would have called him away just now, if he had not wanted to
- avoid a certain gentleman here.”
-
- This part of his intelligence, though unheard by Lydia, was
- caught by Elizabeth, and, as it assured her that Darcy was not
- less answerable for Wickham’s absence than if her first surmise
- had been just, every feeling of displeasure against the former
- was so sharpened by immediate disappointment, that she could
- hardly reply with tolerable civility to the polite inquiries
- which he directly afterwards approached to make. Attendance,
- forbearance, patience with Darcy, was injury to Wickham. She was
- resolved against any sort of conversation with him, and turned
- away with a degree of ill-humour which she could not wholly
- surmount even in speaking to Mr. Bingley, whose blind partiality
- provoked her.
-
- But Elizabeth was not formed for ill-humour; and though every
- prospect of her own was destroyed for the evening, it could not
- dwell long on her spirits; and having told all her griefs to
- Charlotte Lucas, whom she had not seen for a week, she was soon
- able to make a voluntary transition to the oddities of her
- cousin, and to point him out to her particular notice. The first
- two dances, however, brought a return of distress; they were
- dances of mortification. Mr. Collins, awkward and solemn,
- apologising instead of attending, and often moving wrong without
- being aware of it, gave her all the shame and misery which a
- disagreeable partner for a couple of dances can give. The moment
- of her release from him was ecstasy.
-
- She danced next with an officer, and had the refreshment of
- talking of Wickham, and of hearing that he was universally liked.
- When those dances were over, she returned to Charlotte Lucas, and
- was in conversation with her, when she found herself suddenly
- addressed by Mr. Darcy who took her so much by surprise in his
- application for her hand, that, without knowing what she did, she
- accepted him. He walked away again immediately, and she was left
- to fret over her own want of presence of mind; Charlotte tried to
- console her:
-
- “I dare say you will find him very agreeable.”
-
- “Heaven forbid! _That_ would be the greatest misfortune of all!
- To find a man agreeable whom one is determined to hate! Do not
- wish me such an evil.”
-
- When the dancing recommenced, however, and Darcy approached to
- claim her hand, Charlotte could not help cautioning her in a
- whisper, not to be a simpleton, and allow her fancy for Wickham
- to make her appear unpleasant in the eyes of a man ten times his
- consequence. Elizabeth made no answer, and took her place in the
- set, amazed at the dignity to which she was arrived in being
- allowed to stand opposite to Mr. Darcy, and reading in her
- neighbours’ looks, their equal amazement in beholding it. They
- stood for some time without speaking a word; and she began to
- imagine that their silence was to last through the two dances,
- and at first was resolved not to break it; till suddenly fancying
- that it would be the greater punishment to her partner to oblige
- him to talk, she made some slight observation on the dance. He
- replied, and was again silent. After a pause of some minutes, she
- addressed him a second time with:—“It is _your_ turn to say
- something now, Mr. Darcy. _I_ talked about the dance, and _you_
- ought to make some sort of remark on the size of the room, or the
- number of couples.”
-
- He smiled, and assured her that whatever she wished him to say
- should be said.
-
- “Very well. That reply will do for the present. Perhaps by and by
- I may observe that private balls are much pleasanter than public
- ones. But _now_ we may be silent.”
-
- “Do you talk by rule, then, while you are dancing?”
-
- “Sometimes. One must speak a little, you know. It would look odd
- to be entirely silent for half an hour together; and yet for the
- advantage of _some_, conversation ought to be so arranged, as
- that they may have the trouble of saying as little as possible.”
-
- “Are you consulting your own feelings in the present case, or do
- you imagine that you are gratifying mine?”
-
- “Both,” replied Elizabeth archly; “for I have always seen a great
- similarity in the turn of our minds. We are each of an unsocial,
- taciturn disposition, unwilling to speak, unless we expect to say
- something that will amaze the whole room, and be handed down to
- posterity with all the _éclat_ of a proverb.”
-
- “This is no very striking resemblance of your own character, I am
- sure,” said he. “How near it may be to _mine_, I cannot pretend
- to say. _You_ think it a faithful portrait undoubtedly.”
-
- “I must not decide on my own performance.”
-
- He made no answer, and they were again silent till they had gone
- down the dance, when he asked her if she and her sisters did not
- very often walk to Meryton. She answered in the affirmative, and,
- unable to resist the temptation, added, “When you met us there
- the other day, we had just been forming a new acquaintance.”
-
- The effect was immediate. A deeper shade of _hauteur_ overspread
- his features, but he said not a word, and Elizabeth, though
- blaming herself for her own weakness, could not go on. At length
- Darcy spoke, and in a constrained manner said, “Mr. Wickham is
- blessed with such happy manners as may ensure his _making_
- friends—whether he may be equally capable of _retaining_ them, is
- less certain.”
-
- “He has been so unlucky as to lose your friendship,” replied
- Elizabeth with emphasis, “and in a manner which he is likely to
- suffer from all his life.”
-
- Darcy made no answer, and seemed desirous of changing the
- subject. At that moment, Sir William Lucas appeared close to
- them, meaning to pass through the set to the other side of the
- room; but on perceiving Mr. Darcy, he stopped with a bow of
- superior courtesy to compliment him on his dancing and his
- partner.
-
- “I have been most highly gratified indeed, my dear sir. Such very
- superior dancing is not often seen. It is evident that you belong
- to the first circles. Allow me to say, however, that your fair
- partner does not disgrace you, and that I must hope to have this
- pleasure often repeated, especially when a certain desirable
- event, my dear Eliza (glancing at her sister and Bingley) shall
- take place. What congratulations will then flow in! I appeal to
- Mr. Darcy:—but let me not interrupt you, sir. You will not thank
- me for detaining you from the bewitching converse of that young
- lady, whose bright eyes are also upbraiding me.”
-
- The latter part of this address was scarcely heard by Darcy; but
- Sir William’s allusion to his friend seemed to strike him
- forcibly, and his eyes were directed with a very serious
- expression towards Bingley and Jane, who were dancing together.
- Recovering himself, however, shortly, he turned to his partner,
- and said, “Sir William’s interruption has made me forget what we
- were talking of.”
-
- “I do not think we were speaking at all. Sir William could not
- have interrupted two people in the room who had less to say for
- themselves. We have tried two or three subjects already without
- success, and what we are to talk of next I cannot imagine.”
-
- “What think you of books?” said he, smiling.
-
- “Books—oh! no. I am sure we never read the same, or not with the
- same feelings.”
-
- “I am sorry you think so; but if that be the case, there can at
- least be no want of subject. We may compare our different
- opinions.”
-
- “No—I cannot talk of books in a ball-room; my head is always full
- of something else.”
-
- “The _present_ always occupies you in such scenes—does it?” said
- he, with a look of doubt.
-
- “Yes, always,” she replied, without knowing what she said, for
- her thoughts had wandered far from the subject, as soon
- afterwards appeared by her suddenly exclaiming, “I remember
- hearing you once say, Mr. Darcy, that you hardly ever forgave,
- that your resentment once created was unappeasable. You are very
- cautious, I suppose, as to its _being created?_”
-
- “I am,” said he, with a firm voice.
-
- “And never allow yourself to be blinded by prejudice?”
-
- “I hope not.”
-
- “It is particularly incumbent on those who never change their
- opinion, to be secure of judging properly at first.”
-
- “May I ask to what these questions tend?”
-
- “Merely to the illustration of _your_ character,” said she,
- endeavouring to shake off her gravity. “I am trying to make it
- out.”
-
- “And what is your success?”
-
- She shook her head. “I do not get on at all. I hear such
- different accounts of you as puzzle me exceedingly.”
-
- “I can readily believe,” answered he gravely, “that reports may
- vary greatly with respect to me; and I could wish, Miss Bennet,
- that you were not to sketch my character at the present moment,
- as there is reason to fear that the performance would reflect no
- credit on either.”
-
- “But if I do not take your likeness now, I may never have another
- opportunity.”
-
- “I would by no means suspend any pleasure of yours,” he coldly
- replied. She said no more, and they went down the other dance and
- parted in silence; and on each side dissatisfied, though not to
- an equal degree, for in Darcy’s breast there was a tolerably
- powerful feeling towards her, which soon procured her pardon, and
- directed all his anger against another.
-
- They had not long separated, when Miss Bingley came towards her,
- and with an expression of civil disdain accosted her:
-
- “So, Miss Eliza, I hear you are quite delighted with George
- Wickham! Your sister has been talking to me about him, and asking
- me a thousand questions; and I find that the young man quite
- forgot to tell you, among his other communication, that he was
- the son of old Wickham, the late Mr. Darcy’s steward. Let me
- recommend you, however, as a friend, not to give implicit
- confidence to all his assertions; for as to Mr. Darcy’s using him
- ill, it is perfectly false; for, on the contrary, he has always
- been remarkably kind to him, though George Wickham has treated
- Mr. Darcy in a most infamous manner. I do not know the
- particulars, but I know very well that Mr. Darcy is not in the
- least to blame, that he cannot bear to hear George Wickham
- mentioned, and that though my brother thought that he could not
- well avoid including him in his invitation to the officers, he
- was excessively glad to find that he had taken himself out of the
- way. His coming into the country at all is a most insolent thing,
- indeed, and I wonder how he could presume to do it. I pity you,
- Miss Eliza, for this discovery of your favourite’s guilt; but
- really, considering his descent, one could not expect much
- better.”
-
- “His guilt and his descent appear by your account to be the
- same,” said Elizabeth angrily; “for I have heard you accuse him
- of nothing worse than of being the son of Mr. Darcy’s steward,
- and of _that_, I can assure you, he informed me himself.”
-
- “I beg your pardon,” replied Miss Bingley, turning away with a
- sneer. “Excuse my interference—it was kindly meant.”
-
- “Insolent girl!” said Elizabeth to herself. “You are much
- mistaken if you expect to influence me by such a paltry attack as
- this. I see nothing in it but your own wilful ignorance and the
- malice of Mr. Darcy.” She then sought her eldest sister, who had
- undertaken to make inquiries on the same subject of Bingley. Jane
- met her with a smile of such sweet complacency, a glow of such
- happy expression, as sufficiently marked how well she was
- satisfied with the occurrences of the evening. Elizabeth
- instantly read her feelings, and at that moment solicitude for
- Wickham, resentment against his enemies, and everything else,
- gave way before the hope of Jane’s being in the fairest way for
- happiness.
-
- “I want to know,” said she, with a countenance no less smiling
- than her sister’s, “what you have learnt about Mr. Wickham. But
- perhaps you have been too pleasantly engaged to think of any
- third person; in which case you may be sure of my pardon.”
-
- “No,” replied Jane, “I have not forgotten him; but I have nothing
- satisfactory to tell you. Mr. Bingley does not know the whole of
- his history, and is quite ignorant of the circumstances which
- have principally offended Mr. Darcy; but he will vouch for the
- good conduct, the probity, and honour of his friend, and is
- perfectly convinced that Mr. Wickham has deserved much less
- attention from Mr. Darcy than he has received; and I am sorry to
- say by his account as well as his sister’s, Mr. Wickham is by no
- means a respectable young man. I am afraid he has been very
- imprudent, and has deserved to lose Mr. Darcy’s regard.”
-
- “Mr. Bingley does not know Mr. Wickham himself?”
-
- “No; he never saw him till the other morning at Meryton.”
-
- “This account then is what he has received from Mr. Darcy. I am
- satisfied. But what does he say of the living?”
-
- “He does not exactly recollect the circumstances, though he has
- heard them from Mr. Darcy more than once, but he believes that it
- was left to him _conditionally_ only.”
-
- “I have not a doubt of Mr. Bingley’s sincerity,” said Elizabeth
- warmly; “but you must excuse my not being convinced by assurances
- only. Mr. Bingley’s defense of his friend was a very able one, I
- dare say; but since he is unacquainted with several parts of the
- story, and has learnt the rest from that friend himself, I shall
- venture to still think of both gentlemen as I did before.”
-
- She then changed the discourse to one more gratifying to each,
- and on which there could be no difference of sentiment. Elizabeth
- listened with delight to the happy, though modest hopes which
- Jane entertained of Mr. Bingley’s regard, and said all in her
- power to heighten her confidence in it. On their being joined by
- Mr. Bingley himself, Elizabeth withdrew to Miss Lucas; to whose
- inquiry after the pleasantness of her last partner she had
- scarcely replied, before Mr. Collins came up to them, and told
- her with great exultation that he had just been so fortunate as
- to make a most important discovery.
-
- “I have found out,” said he, “by a singular accident, that there
- is now in the room a near relation of my patroness. I happened to
- overhear the gentleman himself mentioning to the young lady who
- does the honours of the house the names of his cousin Miss de
- Bourgh, and of her mother Lady Catherine. How wonderfully these
- sort of things occur! Who would have thought of my meeting with,
- perhaps, a nephew of Lady Catherine de Bourgh in this assembly! I
- am most thankful that the discovery is made in time for me to pay
- my respects to him, which I am now going to do, and trust he will
- excuse my not having done it before. My total ignorance of the
- connection must plead my apology.”
-
- “You are not going to introduce yourself to Mr. Darcy!”
-
- “Indeed I am. I shall entreat his pardon for not having done it
- earlier. I believe him to be Lady Catherine’s _nephew_. It will
- be in my power to assure him that her ladyship was quite well
- yesterday se’nnight.”
-
- Elizabeth tried hard to dissuade him from such a scheme, assuring
- him that Mr. Darcy would consider his addressing him without
- introduction as an impertinent freedom, rather than a compliment
- to his aunt; that it was not in the least necessary there should
- be any notice on either side; and that if it were, it must belong
- to Mr. Darcy, the superior in consequence, to begin the
- acquaintance. Mr. Collins listened to her with the determined air
- of following his own inclination, and, when she ceased speaking,
- replied thus:
-
- “My dear Miss Elizabeth, I have the highest opinion in the world
- in your excellent judgement in all matters within the scope of
- your understanding; but permit me to say, that there must be a
- wide difference between the established forms of ceremony amongst
- the laity, and those which regulate the clergy; for, give me
- leave to observe that I consider the clerical office as equal in
- point of dignity with the highest rank in the kingdom—provided
- that a proper humility of behaviour is at the same time
- maintained. You must therefore allow me to follow the dictates of
- my conscience on this occasion, which leads me to perform what I
- look on as a point of duty. Pardon me for neglecting to profit by
- your advice, which on every other subject shall be my constant
- guide, though in the case before us I consider myself more fitted
- by education and habitual study to decide on what is right than a
- young lady like yourself.” And with a low bow he left her to
- attack Mr. Darcy, whose reception of his advances she eagerly
- watched, and whose astonishment at being so addressed was very
- evident. Her cousin prefaced his speech with a solemn bow and
- though she could not hear a word of it, she felt as if hearing it
- all, and saw in the motion of his lips the words “apology,”
- “Hunsford,” and “Lady Catherine de Bourgh.” It vexed her to see
- him expose himself to such a man. Mr. Darcy was eyeing him with
- unrestrained wonder, and when at last Mr. Collins allowed him
- time to speak, replied with an air of distant civility. Mr.
- Collins, however, was not discouraged from speaking again, and
- Mr. Darcy’s contempt seemed abundantly increasing with the length
- of his second speech, and at the end of it he only made him a
- slight bow, and moved another way. Mr. Collins then returned to
- Elizabeth.
-
- “I have no reason, I assure you,” said he, “to be dissatisfied
- with my reception. Mr. Darcy seemed much pleased with the
- attention. He answered me with the utmost civility, and even paid
- me the compliment of saying that he was so well convinced of Lady
- Catherine’s discernment as to be certain she could never bestow a
- favour unworthily. It was really a very handsome thought. Upon
- the whole, I am much pleased with him.”
-
- As Elizabeth had no longer any interest of her own to pursue, she
- turned her attention almost entirely on her sister and Mr.
- Bingley; and the train of agreeable reflections which her
- observations gave birth to, made her perhaps almost as happy as
- Jane. She saw her in idea settled in that very house, in all the
- felicity which a marriage of true affection could bestow; and she
- felt capable, under such circumstances, of endeavouring even to
- like Bingley’s two sisters. Her mother’s thoughts she plainly saw
- were bent the same way, and she determined not to venture near
- her, lest she might hear too much. When they sat down to supper,
- therefore, she considered it a most unlucky perverseness which
- placed them within one of each other; and deeply was she vexed to
- find that her mother was talking to that one person (Lady Lucas)
- freely, openly, and of nothing else but her expectation that Jane
- would soon be married to Mr. Bingley. It was an animating
- subject, and Mrs. Bennet seemed incapable of fatigue while
- enumerating the advantages of the match. His being such a
- charming young man, and so rich, and living but three miles from
- them, were the first points of self-gratulation; and then it was
- such a comfort to think how fond the two sisters were of Jane,
- and to be certain that they must desire the connection as much as
- she could do. It was, moreover, such a promising thing for her
- younger daughters, as Jane’s marrying so greatly must throw them
- in the way of other rich men; and lastly, it was so pleasant at
- her time of life to be able to consign her single daughters to
- the care of their sister, that she might not be obliged to go
- into company more than she liked. It was necessary to make this
- circumstance a matter of pleasure, because on such occasions it
- is the etiquette; but no one was less likely than Mrs. Bennet to
- find comfort in staying home at any period of her life. She
- concluded with many good wishes that Lady Lucas might soon be
- equally fortunate, though evidently and triumphantly believing
- there was no chance of it.
-
- In vain did Elizabeth endeavour to check the rapidity of her
- mother’s words, or persuade her to describe her felicity in a
- less audible whisper; for, to her inexpressible vexation, she
- could perceive that the chief of it was overheard by Mr. Darcy,
- who sat opposite to them. Her mother only scolded her for being
- nonsensical.
-
- “What is Mr. Darcy to me, pray, that I should be afraid of him? I
- am sure we owe him no such particular civility as to be obliged
- to say nothing _he_ may not like to hear.”
-
- “For heaven’s sake, madam, speak lower. What advantage can it be
- for you to offend Mr. Darcy? You will never recommend yourself to
- his friend by so doing!”
-
- Nothing that she could say, however, had any influence. Her
- mother would talk of her views in the same intelligible tone.
- Elizabeth blushed and blushed again with shame and vexation. She
- could not help frequently glancing her eye at Mr. Darcy, though
- every glance convinced her of what she dreaded; for though he was
- not always looking at her mother, she was convinced that his
- attention was invariably fixed by her. The expression of his face
- changed gradually from indignant contempt to a composed and
- steady gravity.
-
- At length, however, Mrs. Bennet had no more to say; and Lady
- Lucas, who had been long yawning at the repetition of delights
- which she saw no likelihood of sharing, was left to the comforts
- of cold ham and chicken. Elizabeth now began to revive. But not
- long was the interval of tranquillity; for, when supper was over,
- singing was talked of, and she had the mortification of seeing
- Mary, after very little entreaty, preparing to oblige the
- company. By many significant looks and silent entreaties, did she
- endeavour to prevent such a proof of complaisance, but in vain;
- Mary would not understand them; such an opportunity of exhibiting
- was delightful to her, and she began her song. Elizabeth’s eyes
- were fixed on her with most painful sensations, and she watched
- her progress through the several stanzas with an impatience which
- was very ill rewarded at their close; for Mary, on receiving,
- amongst the thanks of the table, the hint of a hope that she
- might be prevailed on to favour them again, after the pause of
- half a minute began another. Mary’s powers were by no means
- fitted for such a display; her voice was weak, and her manner
- affected. Elizabeth was in agonies. She looked at Jane, to see
- how she bore it; but Jane was very composedly talking to Bingley.
- She looked at his two sisters, and saw them making signs of
- derision at each other, and at Darcy, who continued, however,
- imperturbably grave. She looked at her father to entreat his
- interference, lest Mary should be singing all night. He took the
- hint, and when Mary had finished her second song, said aloud,
- “That will do extremely well, child. You have delighted us long
- enough. Let the other young ladies have time to exhibit.”
-
- Mary, though pretending not to hear, was somewhat disconcerted;
- and Elizabeth, sorry for her, and sorry for her father’s speech,
- was afraid her anxiety had done no good. Others of the party were
- now applied to.
-
- “If I,” said Mr. Collins, “were so fortunate as to be able to
- sing, I should have great pleasure, I am sure, in obliging the
- company with an air; for I consider music as a very innocent
- diversion, and perfectly compatible with the profession of a
- clergyman. I do not mean, however, to assert that we can be
- justified in devoting too much of our time to music, for there
- are certainly other things to be attended to. The rector of a
- parish has much to do. In the first place, he must make such an
- agreement for tithes as may be beneficial to himself and not
- offensive to his patron. He must write his own sermons; and the
- time that remains will not be too much for his parish duties, and
- the care and improvement of his dwelling, which he cannot be
- excused from making as comfortable as possible. And I do not
- think it of light importance that he should have attentive and
- conciliatory manners towards everybody, especially towards those
- to whom he owes his preferment. I cannot acquit him of that duty;
- nor could I think well of the man who should omit an occasion of
- testifying his respect towards anybody connected with the
- family.” And with a bow to Mr. Darcy, he concluded his speech,
- which had been spoken so loud as to be heard by half the room.
- Many stared—many smiled; but no one looked more amused than Mr.
- Bennet himself, while his wife seriously commended Mr. Collins
- for having spoken so sensibly, and observed in a half-whisper to
- Lady Lucas, that he was a remarkably clever, good kind of young
- man.
-
- To Elizabeth it appeared that, had her family made an agreement
- to expose themselves as much as they could during the evening, it
- would have been impossible for them to play their parts with more
- spirit or finer success; and happy did she think it for Bingley
- and her sister that some of the exhibition had escaped his
- notice, and that his feelings were not of a sort to be much
- distressed by the folly which he must have witnessed. That his
- two sisters and Mr. Darcy, however, should have such an
- opportunity of ridiculing her relations, was bad enough, and she
- could not determine whether the silent contempt of the gentleman,
- or the insolent smiles of the ladies, were more intolerable.
-
- The rest of the evening brought her little amusement. She was
- teased by Mr. Collins, who continued most perseveringly by her
- side, and though he could not prevail on her to dance with him
- again, put it out of her power to dance with others. In vain did
- she entreat him to stand up with somebody else, and offer to
- introduce him to any young lady in the room. He assured her, that
- as to dancing, he was perfectly indifferent to it; that his chief
- object was by delicate attentions to recommend himself to her and
- that he should therefore make a point of remaining close to her
- the whole evening. There was no arguing upon such a project. She
- owed her greatest relief to her friend Miss Lucas, who often
- joined them, and good-naturedly engaged Mr. Collins’s
- conversation to herself.
-
- She was at least free from the offense of Mr. Darcy’s further
- notice; though often standing within a very short distance of
- her, quite disengaged, he never came near enough to speak. She
- felt it to be the probable consequence of her allusions to Mr.
- Wickham, and rejoiced in it.
-
- The Longbourn party were the last of all the company to depart,
- and, by a manoeuvre of Mrs. Bennet, had to wait for their
- carriage a quarter of an hour after everybody else was gone,
- which gave them time to see how heartily they were wished away by
- some of the family. Mrs. Hurst and her sister scarcely opened
- their mouths, except to complain of fatigue, and were evidently
- impatient to have the house to themselves. They repulsed every
- attempt of Mrs. Bennet at conversation, and by so doing threw a
- languor over the whole party, which was very little relieved by
- the long speeches of Mr. Collins, who was complimenting Mr.
- Bingley and his sisters on the elegance of their entertainment,
- and the hospitality and politeness which had marked their
- behaviour to their guests. Darcy said nothing at all. Mr. Bennet,
- in equal silence, was enjoying the scene. Mr. Bingley and Jane
- were standing together, a little detached from the rest, and
- talked only to each other. Elizabeth preserved as steady a
- silence as either Mrs. Hurst or Miss Bingley; and even Lydia was
- too much fatigued to utter more than the occasional exclamation
- of “Lord, how tired I am!” accompanied by a violent yawn.
-
- When at length they arose to take leave, Mrs. Bennet was most
- pressingly civil in her hope of seeing the whole family soon at
- Longbourn, and addressed herself especially to Mr. Bingley, to
- assure him how happy he would make them by eating a family dinner
- with them at any time, without the ceremony of a formal
- invitation. Bingley was all grateful pleasure, and he readily
- engaged for taking the earliest opportunity of waiting on her,
- after his return from London, whither he was obliged to go the
- next day for a short time.
-
- Mrs. Bennet was perfectly satisfied, and quitted the house under
- the delightful persuasion that, allowing for the necessary
- preparations of settlements, new carriages, and wedding clothes,
- she should undoubtedly see her daughter settled at Netherfield in
- the course of three or four months. Of having another daughter
- married to Mr. Collins, she thought with equal certainty, and
- with considerable, though not equal, pleasure. Elizabeth was the
- least dear to her of all her children; and though the man and the
- match were quite good enough for _her_, the worth of each was
- eclipsed by Mr. Bingley and Netherfield.
-
-
-
-
- Chapter 19
-
- The next day opened a new scene at Longbourn. Mr. Collins made
- his declaration in form. Having resolved to do it without loss of
- time, as his leave of absence extended only to the following
- Saturday, and having no feelings of diffidence to make it
- distressing to himself even at the moment, he set about it in a
- very orderly manner, with all the observances, which he supposed
- a regular part of the business. On finding Mrs. Bennet,
- Elizabeth, and one of the younger girls together, soon after
- breakfast, he addressed the mother in these words:
-
- “May I hope, madam, for your interest with your fair daughter
- Elizabeth, when I solicit for the honour of a private audience
- with her in the course of this morning?”
-
- Before Elizabeth had time for anything but a blush of surprise,
- Mrs. Bennet answered instantly, “Oh dear!—yes—certainly. I am
- sure Lizzy will be very happy—I am sure she can have no
- objection. Come, Kitty, I want you up stairs.” And, gathering her
- work together, she was hastening away, when Elizabeth called out:
-
- “Dear madam, do not go. I beg you will not go. Mr. Collins must
- excuse me. He can have nothing to say to me that anybody need not
- hear. I am going away myself.”
-
- “No, no, nonsense, Lizzy. I desire you to stay where you are.”
- And upon Elizabeth’s seeming really, with vexed and embarrassed
- looks, about to escape, she added: “Lizzy, I _insist_ upon your
- staying and hearing Mr. Collins.”
-
- Elizabeth would not oppose such an injunction—and a moment’s
- consideration making her also sensible that it would be wisest to
- get it over as soon and as quietly as possible, she sat down
- again and tried to conceal, by incessant employment the feelings
- which were divided between distress and diversion. Mrs. Bennet
- and Kitty walked off, and as soon as they were gone, Mr. Collins
- began.
-
- “Believe me, my dear Miss Elizabeth, that your modesty, so far
- from doing you any disservice, rather adds to your other
- perfections. You would have been less amiable in my eyes had
- there _not_ been this little unwillingness; but allow me to
- assure you, that I have your respected mother’s permission for
- this address. You can hardly doubt the purport of my discourse,
- however your natural delicacy may lead you to dissemble; my
- attentions have been too marked to be mistaken. Almost as soon as
- I entered the house, I singled you out as the companion of my
- future life. But before I am run away with by my feelings on this
- subject, perhaps it would be advisable for me to state my reasons
- for marrying—and, moreover, for coming into Hertfordshire with
- the design of selecting a wife, as I certainly did.”
-
- The idea of Mr. Collins, with all his solemn composure, being run
- away with by his feelings, made Elizabeth so near laughing, that
- she could not use the short pause he allowed in any attempt to
- stop him further, and he continued:
-
- “My reasons for marrying are, first, that I think it a right
- thing for every clergyman in easy circumstances (like myself) to
- set the example of matrimony in his parish; secondly, that I am
- convinced that it will add very greatly to my happiness; and
- thirdly—which perhaps I ought to have mentioned earlier, that it
- is the particular advice and recommendation of the very noble
- lady whom I have the honour of calling patroness. Twice has she
- condescended to give me her opinion (unasked too!) on this
- subject; and it was but the very Saturday night before I left
- Hunsford—between our pools at quadrille, while Mrs. Jenkinson was
- arranging Miss de Bourgh’s footstool, that she said, ‘Mr.
- Collins, you must marry. A clergyman like you must marry. Choose
- properly, choose a gentlewoman for _my_ sake; and for your _own_,
- let her be an active, useful sort of person, not brought up high,
- but able to make a small income go a good way. This is my advice.
- Find such a woman as soon as you can, bring her to Hunsford, and
- I will visit her.’ Allow me, by the way, to observe, my fair
- cousin, that I do not reckon the notice and kindness of Lady
- Catherine de Bourgh as among the least of the advantages in my
- power to offer. You will find her manners beyond anything I can
- describe; and your wit and vivacity, I think, must be acceptable
- to her, especially when tempered with the silence and respect
- which her rank will inevitably excite. Thus much for my general
- intention in favour of matrimony; it remains to be told why my
- views were directed towards Longbourn instead of my own
- neighbourhood, where I can assure you there are many amiable
- young women. But the fact is, that being, as I am, to inherit
- this estate after the death of your honoured father (who,
- however, may live many years longer), I could not satisfy myself
- without resolving to choose a wife from among his daughters, that
- the loss to them might be as little as possible, when the
- melancholy event takes place—which, however, as I have already
- said, may not be for several years. This has been my motive, my
- fair cousin, and I flatter myself it will not sink me in your
- esteem. And now nothing remains for me but to assure you in the
- most animated language of the violence of my affection. To
- fortune I am perfectly indifferent, and shall make no demand of
- that nature on your father, since I am well aware that it could
- not be complied with; and that one thousand pounds in the four
- per cents, which will not be yours till after your mother’s
- decease, is all that you may ever be entitled to. On that head,
- therefore, I shall be uniformly silent; and you may assure
- yourself that no ungenerous reproach shall ever pass my lips when
- we are married.”
-
- It was absolutely necessary to interrupt him now.
-
- “You are too hasty, sir,” she cried. “You forget that I have made
- no answer. Let me do it without further loss of time. Accept my
- thanks for the compliment you are paying me. I am very sensible
- of the honour of your proposals, but it is impossible for me to
- do otherwise than to decline them.”
-
- “I am not now to learn,” replied Mr. Collins, with a formal wave
- of the hand, “that it is usual with young ladies to reject the
- addresses of the man whom they secretly mean to accept, when he
- first applies for their favour; and that sometimes the refusal is
- repeated a second, or even a third time. I am therefore by no
- means discouraged by what you have just said, and shall hope to
- lead you to the altar ere long.”
-
- “Upon my word, sir,” cried Elizabeth, “your hope is a rather
- extraordinary one after my declaration. I do assure you that I am
- not one of those young ladies (if such young ladies there are)
- who are so daring as to risk their happiness on the chance of
- being asked a second time. I am perfectly serious in my refusal.
- You could not make _me_ happy, and I am convinced that I am the
- last woman in the world who could make you so. Nay, were your
- friend Lady Catherine to know me, I am persuaded she would find
- me in every respect ill qualified for the situation.”
-
- “Were it certain that Lady Catherine would think so,” said Mr.
- Collins very gravely—“but I cannot imagine that her ladyship
- would at all disapprove of you. And you may be certain when I
- have the honour of seeing her again, I shall speak in the very
- highest terms of your modesty, economy, and other amiable
- qualification.”
-
- “Indeed, Mr. Collins, all praise of me will be unnecessary. You
- must give me leave to judge for myself, and pay me the compliment
- of believing what I say. I wish you very happy and very rich, and
- by refusing your hand, do all in my power to prevent your being
- otherwise. In making me the offer, you must have satisfied the
- delicacy of your feelings with regard to my family, and may take
- possession of Longbourn estate whenever it falls, without any
- self-reproach. This matter may be considered, therefore, as
- finally settled.” And rising as she thus spoke, she would have
- quitted the room, had Mr. Collins not thus addressed her:
-
- “When I do myself the honour of speaking to you next on the
- subject, I shall hope to receive a more favourable answer than
- you have now given me; though I am far from accusing you of
- cruelty at present, because I know it to be the established
- custom of your sex to reject a man on the first application, and
- perhaps you have even now said as much to encourage my suit as
- would be consistent with the true delicacy of the female
- character.”
-
- “Really, Mr. Collins,” cried Elizabeth with some warmth, “you
- puzzle me exceedingly. If what I have hitherto said can appear to
- you in the form of encouragement, I know not how to express my
- refusal in such a way as to convince you of its being one.”
-
- “You must give me leave to flatter myself, my dear cousin, that
- your refusal of my addresses is merely words of course. My
- reasons for believing it are briefly these: It does not appear to
- me that my hand is unworthy of your acceptance, or that the
- establishment I can offer would be any other than highly
- desirable. My situation in life, my connections with the family
- of de Bourgh, and my relationship to your own, are circumstances
- highly in my favour; and you should take it into further
- consideration, that in spite of your manifold attractions, it is
- by no means certain that another offer of marriage may ever be
- made you. Your portion is unhappily so small that it will in all
- likelihood undo the effects of your loveliness and amiable
- qualifications. As I must therefore conclude that you are not
- serious in your rejection of me, I shall choose to attribute it
- to your wish of increasing my love by suspense, according to the
- usual practice of elegant females.”
-
- “I do assure you, sir, that I have no pretensions whatever to
- that kind of elegance which consists in tormenting a respectable
- man. I would rather be paid the compliment of being believed
- sincere. I thank you again and again for the honour you have done
- me in your proposals, but to accept them is absolutely
- impossible. My feelings in every respect forbid it. Can I speak
- plainer? Do not consider me now as an elegant female, intending
- to plague you, but as a rational creature, speaking the truth
- from her heart.”
-
- “You are uniformly charming!” cried he, with an air of awkward
- gallantry; “and I am persuaded that when sanctioned by the
- express authority of both your excellent parents, my proposals
- will not fail of being acceptable.”
-
- To such perseverance in wilful self-deception Elizabeth would
- make no reply, and immediately and in silence withdrew;
- determined, if he persisted in considering her repeated refusals
- as flattering encouragement, to apply to her father, whose
- negative might be uttered in such a manner as to be decisive, and
- whose behaviour at least could not be mistaken for the
- affectation and coquetry of an elegant female.
-
-
-
-
- Chapter 20
-
- Mr. Collins was not left long to the silent contemplation of his
- successful love; for Mrs. Bennet, having dawdled about in the
- vestibule to watch for the end of the conference, no sooner saw
- Elizabeth open the door and with quick step pass her towards the
- staircase, than she entered the breakfast-room, and congratulated
- both him and herself in warm terms on the happy prospect of their
- nearer connection. Mr. Collins received and returned these
- felicitations with equal pleasure, and then proceeded to relate
- the particulars of their interview, with the result of which he
- trusted he had every reason to be satisfied, since the refusal
- which his cousin had steadfastly given him would naturally flow
- from her bashful modesty and the genuine delicacy of her
- character.
-
- This information, however, startled Mrs. Bennet; she would have
- been glad to be equally satisfied that her daughter had meant to
- encourage him by protesting against his proposals, but she dared
- not believe it, and could not help saying so.
-
- “But, depend upon it, Mr. Collins,” she added, “that Lizzy shall
- be brought to reason. I will speak to her about it directly. She
- is a very headstrong, foolish girl, and does not know her own
- interest but I will _make_ her know it.”
-
- “Pardon me for interrupting you, madam,” cried Mr. Collins; “but
- if she is really headstrong and foolish, I know not whether she
- would altogether be a very desirable wife to a man in my
- situation, who naturally looks for happiness in the marriage
- state. If therefore she actually persists in rejecting my suit,
- perhaps it were better not to force her into accepting me,
- because if liable to such defects of temper, she could not
- contribute much to my felicity.”
-
- “Sir, you quite misunderstand me,” said Mrs. Bennet, alarmed.
- “Lizzy is only headstrong in such matters as these. In everything
- else she is as good-natured a girl as ever lived. I will go
- directly to Mr. Bennet, and we shall very soon settle it with
- her, I am sure.”
-
- She would not give him time to reply, but hurrying instantly to
- her husband, called out as she entered the library, “Oh! Mr.
- Bennet, you are wanted immediately; we are all in an uproar. You
- must come and make Lizzy marry Mr. Collins, for she vows she will
- not have him, and if you do not make haste he will change his
- mind and not have _her_.”
-
- Mr. Bennet raised his eyes from his book as she entered, and
- fixed them on her face with a calm unconcern which was not in the
- least altered by her communication.
-
- “I have not the pleasure of understanding you,” said he, when she
- had finished her speech. “Of what are you talking?”
-
- “Of Mr. Collins and Lizzy. Lizzy declares she will not have Mr.
- Collins, and Mr. Collins begins to say that he will not have
- Lizzy.”
-
- “And what am I to do on the occasion? It seems an hopeless
- business.”
-
- “Speak to Lizzy about it yourself. Tell her that you insist upon
- her marrying him.”
-
- “Let her be called down. She shall hear my opinion.”
-
- Mrs. Bennet rang the bell, and Miss Elizabeth was summoned to the
- library.
-
- “Come here, child,” cried her father as she appeared. “I have
- sent for you on an affair of importance. I understand that Mr.
- Collins has made you an offer of marriage. Is it true?” Elizabeth
- replied that it was. “Very well—and this offer of marriage you
- have refused?”
-
- “I have, sir.”
-
- “Very well. We now come to the point. Your mother insists upon
- your accepting it. Is it not so, Mrs. Bennet?”
-
- “Yes, or I will never see her again.”
-
- “An unhappy alternative is before you, Elizabeth. From this day
- you must be a stranger to one of your parents. Your mother will
- never see you again if you do _not_ marry Mr. Collins, and I will
- never see you again if you _do_.”
-
- Elizabeth could not but smile at such a conclusion of such a
- beginning, but Mrs. Bennet, who had persuaded herself that her
- husband regarded the affair as she wished, was excessively
- disappointed.
-
- “What do you mean, Mr. Bennet, in talking this way? You promised
- me to _insist_ upon her marrying him.”
-
- “My dear,” replied her husband, “I have two small favours to
- request. First, that you will allow me the free use of my
- understanding on the present occasion; and secondly, of my room.
- I shall be glad to have the library to myself as soon as may be.”
-
- Not yet, however, in spite of her disappointment in her husband,
- did Mrs. Bennet give up the point. She talked to Elizabeth again
- and again; coaxed and threatened her by turns. She endeavoured to
- secure Jane in her interest; but Jane, with all possible
- mildness, declined interfering; and Elizabeth, sometimes with
- real earnestness, and sometimes with playful gaiety, replied to
- her attacks. Though her manner varied, however, her determination
- never did.
-
- Mr. Collins, meanwhile, was meditating in solitude on what had
- passed. He thought too well of himself to comprehend on what
- motives his cousin could refuse him; and though his pride was
- hurt, he suffered in no other way. His regard for her was quite
- imaginary; and the possibility of her deserving her mother’s
- reproach prevented his feeling any regret.
-
- While the family were in this confusion, Charlotte Lucas came to
- spend the day with them. She was met in the vestibule by Lydia,
- who, flying to her, cried in a half whisper, “I am glad you are
- come, for there is such fun here! What do you think has happened
- this morning? Mr. Collins has made an offer to Lizzy, and she
- will not have him.”
-
- Charlotte hardly had time to answer, before they were joined by
- Kitty, who came to tell the same news; and no sooner had they
- entered the breakfast-room, where Mrs. Bennet was alone, than she
- likewise began on the subject, calling on Miss Lucas for her
- compassion, and entreating her to persuade her friend Lizzy to
- comply with the wishes of all her family. “Pray do, my dear Miss
- Lucas,” she added in a melancholy tone, “for nobody is on my
- side, nobody takes part with me. I am cruelly used, nobody feels
- for my poor nerves.”
-
- Charlotte’s reply was spared by the entrance of Jane and
- Elizabeth.
-
- “Aye, there she comes,” continued Mrs. Bennet, “looking as
- unconcerned as may be, and caring no more for us than if we were
- at York, provided she can have her own way. But I tell you, Miss
- Lizzy—if you take it into your head to go on refusing every offer
- of marriage in this way, you will never get a husband at all—and
- I am sure I do not know who is to maintain you when your father
- is dead. _I_ shall not be able to keep you—and so I warn you. I
- have done with you from this very day. I told you in the library,
- you know, that I should never speak to you again, and you will
- find me as good as my word. I have no pleasure in talking to
- undutiful children. Not that I have much pleasure, indeed, in
- talking to anybody. People who suffer as I do from nervous
- complaints can have no great inclination for talking. Nobody can
- tell what I suffer! But it is always so. Those who do not
- complain are never pitied.”
-
- Her daughters listened in silence to this effusion, sensible that
- any attempt to reason with her or soothe her would only increase
- the irritation. She talked on, therefore, without interruption
- from any of them, till they were joined by Mr. Collins, who
- entered the room with an air more stately than usual, and on
- perceiving whom, she said to the girls, “Now, I do insist upon
- it, that you, all of you, hold your tongues, and let me and Mr.
- Collins have a little conversation together.”
-
- Elizabeth passed quietly out of the room, Jane and Kitty
- followed, but Lydia stood her ground, determined to hear all she
- could; and Charlotte, detained first by the civility of Mr.
- Collins, whose inquiries after herself and all her family were
- very minute, and then by a little curiosity, satisfied herself
- with walking to the window and pretending not to hear. In a
- doleful voice Mrs. Bennet began the projected conversation: “Oh!
- Mr. Collins!”
-
- “My dear madam,” replied he, “let us be for ever silent on this
- point. Far be it from me,” he presently continued, in a voice
- that marked his displeasure, “to resent the behaviour of your
- daughter. Resignation to inevitable evils is the duty of us all;
- the peculiar duty of a young man who has been so fortunate as I
- have been in early preferment; and I trust I am resigned. Perhaps
- not the less so from feeling a doubt of my positive happiness had
- my fair cousin honoured me with her hand; for I have often
- observed that resignation is never so perfect as when the
- blessing denied begins to lose somewhat of its value in our
- estimation. You will not, I hope, consider me as showing any
- disrespect to your family, my dear madam, by thus withdrawing my
- pretensions to your daughter’s favour, without having paid
- yourself and Mr. Bennet the compliment of requesting you to
- interpose your authority in my behalf. My conduct may, I fear, be
- objectionable in having accepted my dismission from your
- daughter’s lips instead of your own. But we are all liable to
- error. I have certainly meant well through the whole affair. My
- object has been to secure an amiable companion for myself, with
- due consideration for the advantage of all your family, and if my
- _manner_ has been at all reprehensible, I here beg leave to
- apologise.”
-
-
-
-
- Chapter 21
-
- The discussion of Mr. Collins’s offer was now nearly at an end,
- and Elizabeth had only to suffer from the uncomfortable feelings
- necessarily attending it, and occasionally from some peevish
- allusions of her mother. As for the gentleman himself, _his_
- feelings were chiefly expressed, not by embarrassment or
- dejection, or by trying to avoid her, but by stiffness of manner
- and resentful silence. He scarcely ever spoke to her, and the
- assiduous attentions which he had been so sensible of himself
- were transferred for the rest of the day to Miss Lucas, whose
- civility in listening to him was a seasonable relief to them all,
- and especially to her friend.
-
- The morrow produced no abatement of Mrs. Bennet’s ill-humour or
- ill health. Mr. Collins was also in the same state of angry
- pride. Elizabeth had hoped that his resentment might shorten his
- visit, but his plan did not appear in the least affected by it.
- He was always to have gone on Saturday, and to Saturday he meant
- to stay.
-
- After breakfast, the girls walked to Meryton to inquire if Mr.
- Wickham were returned, and to lament over his absence from the
- Netherfield ball. He joined them on their entering the town, and
- attended them to their aunt’s where his regret and vexation, and
- the concern of everybody, was well talked over. To Elizabeth,
- however, he voluntarily acknowledged that the necessity of his
- absence _had_ been self-imposed.
-
- “I found,” said he, “as the time drew near that I had better not
- meet Mr. Darcy; that to be in the same room, the same party with
- him for so many hours together, might be more than I could bear,
- and that scenes might arise unpleasant to more than myself.”
-
- She highly approved his forbearance, and they had leisure for a
- full discussion of it, and for all the commendation which they
- civilly bestowed on each other, as Wickham and another officer
- walked back with them to Longbourn, and during the walk he
- particularly attended to her. His accompanying them was a double
- advantage; she felt all the compliment it offered to herself, and
- it was most acceptable as an occasion of introducing him to her
- father and mother.
-
- Soon after their return, a letter was delivered to Miss Bennet;
- it came from Netherfield. The envelope contained a sheet of
- elegant, little, hot-pressed paper, well covered with a lady’s
- fair, flowing hand; and Elizabeth saw her sister’s countenance
- change as she read it, and saw her dwelling intently on some
- particular passages. Jane recollected herself soon, and putting
- the letter away, tried to join with her usual cheerfulness in the
- general conversation; but Elizabeth felt an anxiety on the
- subject which drew off her attention even from Wickham; and no
- sooner had he and his companion taken leave, than a glance from
- Jane invited her to follow her up stairs. When they had gained
- their own room, Jane, taking out the letter, said:
-
- “This is from Caroline Bingley; what it contains has surprised me
- a good deal. The whole party have left Netherfield by this time,
- and are on their way to town—and without any intention of coming
- back again. You shall hear what she says.”
-
- She then read the first sentence aloud, which comprised the
- information of their having just resolved to follow their brother
- to town directly, and of their meaning to dine in Grosvenor
- Street, where Mr. Hurst had a house. The next was in these words:
- “I do not pretend to regret anything I shall leave in
- Hertfordshire, except your society, my dearest friend; but we
- will hope, at some future period, to enjoy many returns of that
- delightful intercourse we have known, and in the meanwhile may
- lessen the pain of separation by a very frequent and most
- unreserved correspondence. I depend on you for that.” To these
- highflown expressions Elizabeth listened with all the
- insensibility of distrust; and though the suddenness of their
- removal surprised her, she saw nothing in it really to lament; it
- was not to be supposed that their absence from Netherfield would
- prevent Mr. Bingley’s being there; and as to the loss of their
- society, she was persuaded that Jane must cease to regard it, in
- the enjoyment of his.
-
- “It is unlucky,” said she, after a short pause, “that you should
- not be able to see your friends before they leave the country.
- But may we not hope that the period of future happiness to which
- Miss Bingley looks forward may arrive earlier than she is aware,
- and that the delightful intercourse you have known as friends
- will be renewed with yet greater satisfaction as sisters? Mr.
- Bingley will not be detained in London by them.”
-
- “Caroline decidedly says that none of the party will return into
- Hertfordshire this winter. I will read it to you:”
-
- “When my brother left us yesterday, he imagined that the business
- which took him to London might be concluded in three or four
- days; but as we are certain it cannot be so, and at the same time
- convinced that when Charles gets to town he will be in no hurry
- to leave it again, we have determined on following him thither,
- that he may not be obliged to spend his vacant hours in a
- comfortless hotel. Many of my acquaintances are already there for
- the winter; I wish that I could hear that you, my dearest friend,
- had any intention of making one of the crowd—but of that I
- despair. I sincerely hope your Christmas in Hertfordshire may
- abound in the gaieties which that season generally brings, and
- that your beaux will be so numerous as to prevent your feeling
- the loss of the three of whom we shall deprive you.”
-
- “It is evident by this,” added Jane, “that he comes back no more
- this winter.”
-
- “It is only evident that Miss Bingley does not mean that he
- _should_.”
-
- “Why will you think so? It must be his own doing. He is his own
- master. But you do not know _all_. I _will_ read you the passage
- which particularly hurts me. I will have no reserves from _you_.”
-
- “Mr. Darcy is impatient to see his sister; and, to confess the
- truth, _we_ are scarcely less eager to meet her again. I really
- do not think Georgiana Darcy has her equal for beauty, elegance,
- and accomplishments; and the affection she inspires in Louisa and
- myself is heightened into something still more interesting, from
- the hope we dare entertain of her being hereafter our sister. I
- do not know whether I ever before mentioned to you my feelings on
- this subject; but I will not leave the country without confiding
- them, and I trust you will not esteem them unreasonable. My
- brother admires her greatly already; he will have frequent
- opportunity now of seeing her on the most intimate footing; her
- relations all wish the connection as much as his own; and a
- sister’s partiality is not misleading me, I think, when I call
- Charles most capable of engaging any woman’s heart. With all
- these circumstances to favour an attachment, and nothing to
- prevent it, am I wrong, my dearest Jane, in indulging the hope of
- an event which will secure the happiness of so many?”
-
- “What do you think of _this_ sentence, my dear Lizzy?” said Jane
- as she finished it. “Is it not clear enough? Does it not
- expressly declare that Caroline neither expects nor wishes me to
- be her sister; that she is perfectly convinced of her brother’s
- indifference; and that if she suspects the nature of my feelings
- for him, she means (most kindly!) to put me on my guard? Can
- there be any other opinion on the subject?”
-
- “Yes, there can; for mine is totally different. Will you hear
- it?”
-
- “Most willingly.”
-
- “You shall have it in a few words. Miss Bingley sees that her
- brother is in love with you, and wants him to marry Miss Darcy.
- She follows him to town in hope of keeping him there, and tries
- to persuade you that he does not care about you.”
-
- Jane shook her head.
-
- “Indeed, Jane, you ought to believe me. No one who has ever seen
- you together can doubt his affection. Miss Bingley, I am sure,
- cannot. She is not such a simpleton. Could she have seen half as
- much love in Mr. Darcy for herself, she would have ordered her
- wedding clothes. But the case is this: We are not rich enough or
- grand enough for them; and she is the more anxious to get Miss
- Darcy for her brother, from the notion that when there has been
- _one_ intermarriage, she may have less trouble in achieving a
- second; in which there is certainly some ingenuity, and I dare
- say it would succeed, if Miss de Bourgh were out of the way. But,
- my dearest Jane, you cannot seriously imagine that because Miss
- Bingley tells you her brother greatly admires Miss Darcy, he is
- in the smallest degree less sensible of _your_ merit than when he
- took leave of you on Tuesday, or that it will be in her power to
- persuade him that, instead of being in love with you, he is very
- much in love with her friend.”
-
- “If we thought alike of Miss Bingley,” replied Jane, “your
- representation of all this might make me quite easy. But I know
- the foundation is unjust. Caroline is incapable of wilfully
- deceiving anyone; and all that I can hope in this case is that
- she is deceiving herself.”
-
- “That is right. You could not have started a more happy idea,
- since you will not take comfort in mine. Believe her to be
- deceived, by all means. You have now done your duty by her, and
- must fret no longer.”
-
- “But, my dear sister, can I be happy, even supposing the best, in
- accepting a man whose sisters and friends are all wishing him to
- marry elsewhere?”
-
- “You must decide for yourself,” said Elizabeth; “and if, upon
- mature deliberation, you find that the misery of disobliging his
- two sisters is more than equivalent to the happiness of being his
- wife, I advise you by all means to refuse him.”
-
- “How can you talk so?” said Jane, faintly smiling. “You must know
- that though I should be exceedingly grieved at their
- disapprobation, I could not hesitate.”
-
- “I did not think you would; and that being the case, I cannot
- consider your situation with much compassion.”
-
- “But if he returns no more this winter, my choice will never be
- required. A thousand things may arise in six months!”
-
- The idea of his returning no more Elizabeth treated with the
- utmost contempt. It appeared to her merely the suggestion of
- Caroline’s interested wishes, and she could not for a moment
- suppose that those wishes, however openly or artfully spoken,
- could influence a young man so totally independent of everyone.
-
- She represented to her sister as forcibly as possible what she
- felt on the subject, and had soon the pleasure of seeing its
- happy effect. Jane’s temper was not desponding, and she was
- gradually led to hope, though the diffidence of affection
- sometimes overcame the hope, that Bingley would return to
- Netherfield and answer every wish of her heart.
-
- They agreed that Mrs. Bennet should only hear of the departure of
- the family, without being alarmed on the score of the gentleman’s
- conduct; but even this partial communication gave her a great
- deal of concern, and she bewailed it as exceedingly unlucky that
- the ladies should happen to go away just as they were all getting
- so intimate together. After lamenting it, however, at some
- length, she had the consolation that Mr. Bingley would be soon
- down again and soon dining at Longbourn, and the conclusion of
- all was the comfortable declaration, that though he had been
- invited only to a family dinner, she would take care to have two
- full courses.
-
-
-
-
- Chapter 22
-
- The Bennets were engaged to dine with the Lucases and again
- during the chief of the day was Miss Lucas so kind as to listen
- to Mr. Collins. Elizabeth took an opportunity of thanking her.
- “It keeps him in good humour,” said she, “and I am more obliged
- to you than I can express.” Charlotte assured her friend of her
- satisfaction in being useful, and that it amply repaid her for
- the little sacrifice of her time. This was very amiable, but
- Charlotte’s kindness extended farther than Elizabeth had any
- conception of; its object was nothing else than to secure her
- from any return of Mr. Collins’s addresses, by engaging them
- towards herself. Such was Miss Lucas’s scheme; and appearances
- were so favourable, that when they parted at night, she would
- have felt almost secure of success if he had not been to leave
- Hertfordshire so very soon. But here she did injustice to the
- fire and independence of his character, for it led him to escape
- out of Longbourn House the next morning with admirable slyness,
- and hasten to Lucas Lodge to throw himself at her feet. He was
- anxious to avoid the notice of his cousins, from a conviction
- that if they saw him depart, they could not fail to conjecture
- his design, and he was not willing to have the attempt known till
- its success might be known likewise; for though feeling almost
- secure, and with reason, for Charlotte had been tolerably
- encouraging, he was comparatively diffident since the adventure
- of Wednesday. His reception, however, was of the most flattering
- kind. Miss Lucas perceived him from an upper window as he walked
- towards the house, and instantly set out to meet him accidentally
- in the lane. But little had she dared to hope that so much love
- and eloquence awaited her there.
-
- In as short a time as Mr. Collins’s long speeches would allow,
- everything was settled between them to the satisfaction of both;
- and as they entered the house he earnestly entreated her to name
- the day that was to make him the happiest of men; and though such
- a solicitation must be waived for the present, the lady felt no
- inclination to trifle with his happiness. The stupidity with
- which he was favoured by nature must guard his courtship from any
- charm that could make a woman wish for its continuance; and Miss
- Lucas, who accepted him solely from the pure and disinterested
- desire of an establishment, cared not how soon that establishment
- were gained.
-
- Sir William and Lady Lucas were speedily applied to for their
- consent; and it was bestowed with a most joyful alacrity. Mr.
- Collins’s present circumstances made it a most eligible match for
- their daughter, to whom they could give little fortune; and his
- prospects of future wealth were exceedingly fair. Lady Lucas
- began directly to calculate, with more interest than the matter
- had ever excited before, how many years longer Mr. Bennet was
- likely to live; and Sir William gave it as his decided opinion,
- that whenever Mr. Collins should be in possession of the
- Longbourn estate, it would be highly expedient that both he and
- his wife should make their appearance at St. James’s. The whole
- family, in short, were properly overjoyed on the occasion. The
- younger girls formed hopes of _coming out_ a year or two sooner
- than they might otherwise have done; and the boys were relieved
- from their apprehension of Charlotte’s dying an old maid.
- Charlotte herself was tolerably composed. She had gained her
- point, and had time to consider of it. Her reflections were in
- general satisfactory. Mr. Collins, to be sure, was neither
- sensible nor agreeable; his society was irksome, and his
- attachment to her must be imaginary. But still he would be her
- husband. Without thinking highly either of men or matrimony,
- marriage had always been her object; it was the only provision
- for well-educated young women of small fortune, and however
- uncertain of giving happiness, must be their pleasantest
- preservative from want. This preservative she had now obtained;
- and at the age of twenty-seven, without having ever been
- handsome, she felt all the good luck of it. The least agreeable
- circumstance in the business was the surprise it must occasion to
- Elizabeth Bennet, whose friendship she valued beyond that of any
- other person. Elizabeth would wonder, and probably would blame
- her; and though her resolution was not to be shaken, her feelings
- must be hurt by such a disapprobation. She resolved to give her
- the information herself, and therefore charged Mr. Collins, when
- he returned to Longbourn to dinner, to drop no hint of what had
- passed before any of the family. A promise of secrecy was of
- course very dutifully given, but it could not be kept without
- difficulty; for the curiosity excited by his long absence burst
- forth in such very direct questions on his return as required
- some ingenuity to evade, and he was at the same time exercising
- great self-denial, for he was longing to publish his prosperous
- love.
-
- As he was to begin his journey too early on the morrow to see any
- of the family, the ceremony of leave-taking was performed when
- the ladies moved for the night; and Mrs. Bennet, with great
- politeness and cordiality, said how happy they should be to see
- him at Longbourn again, whenever his engagements might allow him
- to visit them.
-
- “My dear madam,” he replied, “this invitation is particularly
- gratifying, because it is what I have been hoping to receive; and
- you may be very certain that I shall avail myself of it as soon
- as possible.”
-
- They were all astonished; and Mr. Bennet, who could by no means
- wish for so speedy a return, immediately said:
-
- “But is there not danger of Lady Catherine’s disapprobation here,
- my good sir? You had better neglect your relations than run the
- risk of offending your patroness.”
-
- “My dear sir,” replied Mr. Collins, “I am particularly obliged to
- you for this friendly caution, and you may depend upon my not
- taking so material a step without her ladyship’s concurrence.”
-
- “You cannot be too much upon your guard. Risk anything rather
- than her displeasure; and if you find it likely to be raised by
- your coming to us again, which I should think exceedingly
- probable, stay quietly at home, and be satisfied that _we_ shall
- take no offence.”
-
- “Believe me, my dear sir, my gratitude is warmly excited by such
- affectionate attention; and depend upon it, you will speedily
- receive from me a letter of thanks for this, and for every other
- mark of your regard during my stay in Hertfordshire. As for my
- fair cousins, though my absence may not be long enough to render
- it necessary, I shall now take the liberty of wishing them health
- and happiness, not excepting my cousin Elizabeth.”
-
- With proper civilities the ladies then withdrew; all of them
- equally surprised that he meditated a quick return. Mrs. Bennet
- wished to understand by it that he thought of paying his
- addresses to one of her younger girls, and Mary might have been
- prevailed on to accept him. She rated his abilities much higher
- than any of the others; there was a solidity in his reflections
- which often struck her, and though by no means so clever as
- herself, she thought that if encouraged to read and improve
- himself by such an example as hers, he might become a very
- agreeable companion. But on the following morning, every hope of
- this kind was done away. Miss Lucas called soon after breakfast,
- and in a private conference with Elizabeth related the event of
- the day before.
-
- The possibility of Mr. Collins’s fancying himself in love with
- her friend had once occurred to Elizabeth within the last day or
- two; but that Charlotte could encourage him seemed almost as far
- from possibility as she could encourage him herself, and her
- astonishment was consequently so great as to overcome at first
- the bounds of decorum, and she could not help crying out:
-
- “Engaged to Mr. Collins! My dear Charlotte—impossible!”
-
- The steady countenance which Miss Lucas had commanded in telling
- her story, gave way to a momentary confusion here on receiving so
- direct a reproach; though, as it was no more than she expected,
- she soon regained her composure, and calmly replied:
-
- “Why should you be surprised, my dear Eliza? Do you think it
- incredible that Mr. Collins should be able to procure any woman’s
- good opinion, because he was not so happy as to succeed with
- you?”
-
- But Elizabeth had now recollected herself, and making a strong
- effort for it, was able to assure with tolerable firmness that
- the prospect of their relationship was highly grateful to her,
- and that she wished her all imaginable happiness.
-
- “I see what you are feeling,” replied Charlotte. “You must be
- surprised, very much surprised—so lately as Mr. Collins was
- wishing to marry you. But when you have had time to think it
- over, I hope you will be satisfied with what I have done. I am
- not romantic, you know; I never was. I ask only a comfortable
- home; and considering Mr. Collins’s character, connection, and
- situation in life, I am convinced that my chance of happiness
- with him is as fair as most people can boast on entering the
- marriage state.”
-
- Elizabeth quietly answered “Undoubtedly;” and after an awkward
- pause, they returned to the rest of the family. Charlotte did not
- stay much longer, and Elizabeth was then left to reflect on what
- she had heard. It was a long time before she became at all
- reconciled to the idea of so unsuitable a match. The strangeness
- of Mr. Collins’s making two offers of marriage within three days
- was nothing in comparison of his being now accepted. She had
- always felt that Charlotte’s opinion of matrimony was not exactly
- like her own, but she had not supposed it to be possible that,
- when called into action, she would have sacrificed every better
- feeling to worldly advantage. Charlotte the wife of Mr. Collins
- was a most humiliating picture! And to the pang of a friend
- disgracing herself and sunk in her esteem, was added the
- distressing conviction that it was impossible for that friend to
- be tolerably happy in the lot she had chosen.
-
-
-
-
- Chapter 23
-
- Elizabeth was sitting with her mother and sisters, reflecting on
- what she had heard, and doubting whether she was authorised to
- mention it, when Sir William Lucas himself appeared, sent by his
- daughter, to announce her engagement to the family. With many
- compliments to them, and much self-gratulation on the prospect of
- a connection between the houses, he unfolded the matter—to an
- audience not merely wondering, but incredulous; for Mrs. Bennet,
- with more perseverance than politeness, protested he must be
- entirely mistaken; and Lydia, always unguarded and often uncivil,
- boisterously exclaimed:
-
- “Good Lord! Sir William, how can you tell such a story? Do not
- you know that Mr. Collins wants to marry Lizzy?”
-
- Nothing less than the complaisance of a courtier could have borne
- without anger such treatment; but Sir William’s good breeding
- carried him through it all; and though he begged leave to be
- positive as to the truth of his information, he listened to all
- their impertinence with the most forbearing courtesy.
-
- Elizabeth, feeling it incumbent on her to relieve him from so
- unpleasant a situation, now put herself forward to confirm his
- account, by mentioning her prior knowledge of it from Charlotte
- herself; and endeavoured to put a stop to the exclamations of her
- mother and sisters by the earnestness of her congratulations to
- Sir William, in which she was readily joined by Jane, and by
- making a variety of remarks on the happiness that might be
- expected from the match, the excellent character of Mr. Collins,
- and the convenient distance of Hunsford from London.
-
- Mrs. Bennet was in fact too much overpowered to say a great deal
- while Sir William remained; but no sooner had he left them than
- her feelings found a rapid vent. In the first place, she
- persisted in disbelieving the whole of the matter; secondly, she
- was very sure that Mr. Collins had been taken in; thirdly, she
- trusted that they would never be happy together; and fourthly,
- that the match might be broken off. Two inferences, however, were
- plainly deduced from the whole: one, that Elizabeth was the real
- cause of the mischief; and the other that she herself had been
- barbarously misused by them all; and on these two points she
- principally dwelt during the rest of the day. Nothing could
- console and nothing could appease her. Nor did that day wear out
- her resentment. A week elapsed before she could see Elizabeth
- without scolding her, a month passed away before she could speak
- to Sir William or Lady Lucas without being rude, and many months
- were gone before she could at all forgive their daughter.
-
- Mr. Bennet’s emotions were much more tranquil on the occasion,
- and such as he did experience he pronounced to be of a most
- agreeable sort; for it gratified him, he said, to discover that
- Charlotte Lucas, whom he had been used to think tolerably
- sensible, was as foolish as his wife, and more foolish than his
- daughter!
-
- Jane confessed herself a little surprised at the match; but she
- said less of her astonishment than of her earnest desire for
- their happiness; nor could Elizabeth persuade her to consider it
- as improbable. Kitty and Lydia were far from envying Miss Lucas,
- for Mr. Collins was only a clergyman; and it affected them in no
- other way than as a piece of news to spread at Meryton.
-
- Lady Lucas could not be insensible of triumph on being able to
- retort on Mrs. Bennet the comfort of having a daughter well
- married; and she called at Longbourn rather oftener than usual to
- say how happy she was, though Mrs. Bennet’s sour looks and
- ill-natured remarks might have been enough to drive happiness
- away.
-
- Between Elizabeth and Charlotte there was a restraint which kept
- them mutually silent on the subject; and Elizabeth felt persuaded
- that no real confidence could ever subsist between them again.
- Her disappointment in Charlotte made her turn with fonder regard
- to her sister, of whose rectitude and delicacy she was sure her
- opinion could never be shaken, and for whose happiness she grew
- daily more anxious, as Bingley had now been gone a week and
- nothing more was heard of his return.
-
- Jane had sent Caroline an early answer to her letter, and was
- counting the days till she might reasonably hope to hear again.
- The promised letter of thanks from Mr. Collins arrived on
- Tuesday, addressed to their father, and written with all the
- solemnity of gratitude which a twelvemonth’s abode in the family
- might have prompted. After discharging his conscience on that
- head, he proceeded to inform them, with many rapturous
- expressions, of his happiness in having obtained the affection of
- their amiable neighbour, Miss Lucas, and then explained that it
- was merely with the view of enjoying her society that he had been
- so ready to close with their kind wish of seeing him again at
- Longbourn, whither he hoped to be able to return on Monday
- fortnight; for Lady Catherine, he added, so heartily approved his
- marriage, that she wished it to take place as soon as possible,
- which he trusted would be an unanswerable argument with his
- amiable Charlotte to name an early day for making him the
- happiest of men.
-
- Mr. Collins’s return into Hertfordshire was no longer a matter of
- pleasure to Mrs. Bennet. On the contrary, she was as much
- disposed to complain of it as her husband. It was very strange
- that he should come to Longbourn instead of to Lucas Lodge; it
- was also very inconvenient and exceedingly troublesome. She hated
- having visitors in the house while her health was so indifferent,
- and lovers were of all people the most disagreeable. Such were
- the gentle murmurs of Mrs. Bennet, and they gave way only to the
- greater distress of Mr. Bingley’s continued absence.
-
- Neither Jane nor Elizabeth were comfortable on this subject. Day
- after day passed away without bringing any other tidings of him
- than the report which shortly prevailed in Meryton of his coming
- no more to Netherfield the whole winter; a report which highly
- incensed Mrs. Bennet, and which she never failed to contradict as
- a most scandalous falsehood.
-
- Even Elizabeth began to fear—not that Bingley was indifferent—but
- that his sisters would be successful in keeping him away.
- Unwilling as she was to admit an idea so destructive of Jane’s
- happiness, and so dishonorable to the stability of her lover, she
- could not prevent its frequently occurring. The united efforts of
- his two unfeeling sisters and of his overpowering friend,
- assisted by the attractions of Miss Darcy and the amusements of
- London might be too much, she feared, for the strength of his
- attachment.
-
- As for Jane, _her_ anxiety under this suspense was, of course,
- more painful than Elizabeth’s, but whatever she felt she was
- desirous of concealing, and between herself and Elizabeth,
- therefore, the subject was never alluded to. But as no such
- delicacy restrained her mother, an hour seldom passed in which
- she did not talk of Bingley, express her impatience for his
- arrival, or even require Jane to confess that if he did not come
- back she would think herself very ill used. It needed all Jane’s
- steady mildness to bear these attacks with tolerable
- tranquillity.
-
- Mr. Collins returned most punctually on Monday fortnight, but his
- reception at Longbourn was not quite so gracious as it had been
- on his first introduction. He was too happy, however, to need
- much attention; and luckily for the others, the business of
- love-making relieved them from a great deal of his company. The
- chief of every day was spent by him at Lucas Lodge, and he
- sometimes returned to Longbourn only in time to make an apology
- for his absence before the family went to bed.
-
- Mrs. Bennet was really in a most pitiable state. The very mention
- of anything concerning the match threw her into an agony of
- ill-humour, and wherever she went she was sure of hearing it
- talked of. The sight of Miss Lucas was odious to her. As her
- successor in that house, she regarded her with jealous
- abhorrence. Whenever Charlotte came to see them, she concluded
- her to be anticipating the hour of possession; and whenever she
- spoke in a low voice to Mr. Collins, was convinced that they were
- talking of the Longbourn estate, and resolving to turn herself
- and her daughters out of the house, as soon as Mr. Bennet were
- dead. She complained bitterly of all this to her husband.
-
- “Indeed, Mr. Bennet,” said she, “it is very hard to think that
- Charlotte Lucas should ever be mistress of this house, that _I_
- should be forced to make way for _her_, and live to see her take
- her place in it!”
-
- “My dear, do not give way to such gloomy thoughts. Let us hope
- for better things. Let us flatter ourselves that _I_ may be the
- survivor.”
-
- This was not very consoling to Mrs. Bennet, and therefore,
- instead of making any answer, she went on as before.
-
- “I cannot bear to think that they should have all this estate. If
- it was not for the entail, I should not mind it.”
-
- “What should not you mind?”
-
- “I should not mind anything at all.”
-
- “Let us be thankful that you are preserved from a state of such
- insensibility.”
-
- “I never can be thankful, Mr. Bennet, for anything about the
- entail. How anyone could have the conscience to entail away an
- estate from one’s own daughters, I cannot understand; and all for
- the sake of Mr. Collins too! Why should _he_ have it more than
- anybody else?”
-
- “I leave it to yourself to determine,” said Mr. Bennet.
-
-
-
-
- Chapter 24
-
- Miss Bingley’s letter arrived, and put an end to doubt. The very
- first sentence conveyed the assurance of their being all settled
- in London for the winter, and concluded with her brother’s regret
- at not having had time to pay his respects to his friends in
- Hertfordshire before he left the country.
-
- Hope was over, entirely over; and when Jane could attend to the
- rest of the letter, she found little, except the professed
- affection of the writer, that could give her any comfort. Miss
- Darcy’s praise occupied the chief of it. Her many attractions
- were again dwelt on, and Caroline boasted joyfully of their
- increasing intimacy, and ventured to predict the accomplishment
- of the wishes which had been unfolded in her former letter. She
- wrote also with great pleasure of her brother’s being an inmate
- of Mr. Darcy’s house, and mentioned with raptures some plans of
- the latter with regard to new furniture.
-
- Elizabeth, to whom Jane very soon communicated the chief of all
- this, heard it in silent indignation. Her heart was divided
- between concern for her sister, and resentment against all
- others. To Caroline’s assertion of her brother’s being partial to
- Miss Darcy she paid no credit. That he was really fond of Jane,
- she doubted no more than she had ever done; and much as she had
- always been disposed to like him, she could not think without
- anger, hardly without contempt, on that easiness of temper, that
- want of proper resolution, which now made him the slave of his
- designing friends, and led him to sacrifice of his own happiness
- to the caprice of their inclination. Had his own happiness,
- however, been the only sacrifice, he might have been allowed to
- sport with it in whatever manner he thought best, but her
- sister’s was involved in it, as she thought he must be sensible
- himself. It was a subject, in short, on which reflection would be
- long indulged, and must be unavailing. She could think of nothing
- else; and yet whether Bingley’s regard had really died away, or
- were suppressed by his friends’ interference; whether he had been
- aware of Jane’s attachment, or whether it had escaped his
- observation; whatever were the case, though her opinion of him
- must be materially affected by the difference, her sister’s
- situation remained the same, her peace equally wounded.
-
- A day or two passed before Jane had courage to speak of her
- feelings to Elizabeth; but at last, on Mrs. Bennet’s leaving them
- together, after a longer irritation than usual about Netherfield
- and its master, she could not help saying:
-
- “Oh, that my dear mother had more command over herself! She can
- have no idea of the pain she gives me by her continual
- reflections on him. But I will not repine. It cannot last long.
- He will be forgot, and we shall all be as we were before.”
-
- Elizabeth looked at her sister with incredulous solicitude, but
- said nothing.
-
- “You doubt me,” cried Jane, slightly colouring; “indeed, you have
- no reason. He may live in my memory as the most amiable man of my
- acquaintance, but that is all. I have nothing either to hope or
- fear, and nothing to reproach him with. Thank God! I have not
- _that_ pain. A little time, therefore—I shall certainly try to
- get the better.”
-
- With a stronger voice she soon added, “I have this comfort
- immediately, that it has not been more than an error of fancy on
- my side, and that it has done no harm to anyone but myself.”
-
- “My dear Jane!” exclaimed Elizabeth, “you are too good. Your
- sweetness and disinterestedness are really angelic; I do not know
- what to say to you. I feel as if I had never done you justice, or
- loved you as you deserve.”
-
- Miss Bennet eagerly disclaimed all extraordinary merit, and threw
- back the praise on her sister’s warm affection.
-
- “Nay,” said Elizabeth, “this is not fair. _You_ wish to think all
- the world respectable, and are hurt if I speak ill of anybody.
- _I_ only want to think _you_ perfect, and you set yourself
- against it. Do not be afraid of my running into any excess, of my
- encroaching on your privilege of universal good-will. You need
- not. There are few people whom I really love, and still fewer of
- whom I think well. The more I see of the world, the more am I
- dissatisfied with it; and every day confirms my belief of the
- inconsistency of all human characters, and of the little
- dependence that can be placed on the appearance of merit or
- sense. I have met with two instances lately, one I will not
- mention; the other is Charlotte’s marriage. It is unaccountable!
- In every view it is unaccountable!”
-
- “My dear Lizzy, do not give way to such feelings as these. They
- will ruin your happiness. You do not make allowance enough for
- difference of situation and temper. Consider Mr. Collins’s
- respectability, and Charlotte’s steady, prudent character.
- Remember that she is one of a large family; that as to fortune,
- it is a most eligible match; and be ready to believe, for
- everybody’s sake, that she may feel something like regard and
- esteem for our cousin.”
-
- “To oblige you, I would try to believe almost anything, but no
- one else could be benefited by such a belief as this; for were I
- persuaded that Charlotte had any regard for him, I should only
- think worse of her understanding than I now do of her heart. My
- dear Jane, Mr. Collins is a conceited, pompous, narrow-minded,
- silly man; you know he is, as well as I do; and you must feel, as
- well as I do, that the woman who married him cannot have a proper
- way of thinking. You shall not defend her, though it is Charlotte
- Lucas. You shall not, for the sake of one individual, change the
- meaning of principle and integrity, nor endeavour to persuade
- yourself or me, that selfishness is prudence, and insensibility
- of danger security for happiness.”
-
- “I must think your language too strong in speaking of both,”
- replied Jane; “and I hope you will be convinced of it by seeing
- them happy together. But enough of this. You alluded to something
- else. You mentioned _two_ instances. I cannot misunderstand you,
- but I entreat you, dear Lizzy, not to pain me by thinking _that
- person_ to blame, and saying your opinion of him is sunk. We must
- not be so ready to fancy ourselves intentionally injured. We must
- not expect a lively young man to be always so guarded and
- circumspect. It is very often nothing but our own vanity that
- deceives us. Women fancy admiration means more than it does.”
-
- “And men take care that they should.”
-
- “If it is designedly done, they cannot be justified; but I have
- no idea of there being so much design in the world as some
- persons imagine.”
-
- “I am far from attributing any part of Mr. Bingley’s conduct to
- design,” said Elizabeth; “but without scheming to do wrong, or to
- make others unhappy, there may be error, and there may be misery.
- Thoughtlessness, want of attention to other people’s feelings,
- and want of resolution, will do the business.”
-
- “And do you impute it to either of those?”
-
- “Yes; to the last. But if I go on, I shall displease you by
- saying what I think of persons you esteem. Stop me whilst you
- can.”
-
- “You persist, then, in supposing his sisters influence him?”
-
- “Yes, in conjunction with his friend.”
-
- “I cannot believe it. Why should they try to influence him? They
- can only wish his happiness; and if he is attached to me, no
- other woman can secure it.”
-
- “Your first position is false. They may wish many things besides
- his happiness; they may wish his increase of wealth and
- consequence; they may wish him to marry a girl who has all the
- importance of money, great connections, and pride.”
-
- “Beyond a doubt, they do wish him to choose Miss Darcy,” replied
- Jane; “but this may be from better feelings than you are
- supposing. They have known her much longer than they have known
- me; no wonder if they love her better. But, whatever may be their
- own wishes, it is very unlikely they should have opposed their
- brother’s. What sister would think herself at liberty to do it,
- unless there were something very objectionable? If they believed
- him attached to me, they would not try to part us; if he were so,
- they could not succeed. By supposing such an affection, you make
- everybody acting unnaturally and wrong, and me most unhappy. Do
- not distress me by the idea. I am not ashamed of having been
- mistaken—or, at least, it is light, it is nothing in comparison
- of what I should feel in thinking ill of him or his sisters. Let
- me take it in the best light, in the light in which it may be
- understood.”
-
- Elizabeth could not oppose such a wish; and from this time Mr.
- Bingley’s name was scarcely ever mentioned between them.
-
- Mrs. Bennet still continued to wonder and repine at his returning
- no more, and though a day seldom passed in which Elizabeth did
- not account for it clearly, there was little chance of her ever
- considering it with less perplexity. Her daughter endeavoured to
- convince her of what she did not believe herself, that his
- attentions to Jane had been merely the effect of a common and
- transient liking, which ceased when he saw her no more; but
- though the probability of the statement was admitted at the time,
- she had the same story to repeat every day. Mrs. Bennet’s best
- comfort was that Mr. Bingley must be down again in the summer.
-
- Mr. Bennet treated the matter differently. “So, Lizzy,” said he
- one day, “your sister is crossed in love, I find. I congratulate
- her. Next to being married, a girl likes to be crossed a little
- in love now and then. It is something to think of, and it gives
- her a sort of distinction among her companions. When is your turn
- to come? You will hardly bear to be long outdone by Jane. Now is
- your time. Here are officers enough in Meryton to disappoint all
- the young ladies in the country. Let Wickham be your man. He is a
- pleasant fellow, and would jilt you creditably.”
-
- “Thank you, sir, but a less agreeable man would satisfy me. We
- must not all expect Jane’s good fortune.”
-
- “True,” said Mr. Bennet, “but it is a comfort to think that
- whatever of that kind may befall you, you have an affectionate
- mother who will make the most of it.”
-
- Mr. Wickham’s society was of material service in dispelling the
- gloom which the late perverse occurrences had thrown on many of
- the Longbourn family. They saw him often, and to his other
- recommendations was now added that of general unreserve. The
- whole of what Elizabeth had already heard, his claims on Mr.
- Darcy, and all that he had suffered from him, was now openly
- acknowledged and publicly canvassed; and everybody was pleased to
- know how much they had always disliked Mr. Darcy before they had
- known anything of the matter.
-
- Miss Bennet was the only creature who could suppose there might
- be any extenuating circumstances in the case, unknown to the
- society of Hertfordshire; her mild and steady candour always
- pleaded for allowances, and urged the possibility of mistakes—but
- by everybody else Mr. Darcy was condemned as the worst of men.
-
-
-
-
- Chapter 25
-
- After a week spent in professions of love and schemes of
- felicity, Mr. Collins was called from his amiable Charlotte by
- the arrival of Saturday. The pain of separation, however, might
- be alleviated on his side, by preparations for the reception of
- his bride; as he had reason to hope, that shortly after his
- return into Hertfordshire, the day would be fixed that was to
- make him the happiest of men. He took leave of his relations at
- Longbourn with as much solemnity as before; wished his fair
- cousins health and happiness again, and promised their father
- another letter of thanks.
-
- On the following Monday, Mrs. Bennet had the pleasure of
- receiving her brother and his wife, who came as usual to spend
- the Christmas at Longbourn. Mr. Gardiner was a sensible,
- gentlemanlike man, greatly superior to his sister, as well by
- nature as education. The Netherfield ladies would have had
- difficulty in believing that a man who lived by trade, and within
- view of his own warehouses, could have been so well-bred and
- agreeable. Mrs. Gardiner, who was several years younger than Mrs.
- Bennet and Mrs. Phillips, was an amiable, intelligent, elegant
- woman, and a great favourite with all her Longbourn nieces.
- Between the two eldest and herself especially, there subsisted a
- particular regard. They had frequently been staying with her in
- town.
-
- The first part of Mrs. Gardiner’s business on her arrival was to
- distribute her presents and describe the newest fashions. When
- this was done she had a less active part to play. It became her
- turn to listen. Mrs. Bennet had many grievances to relate, and
- much to complain of. They had all been very ill-used since she
- last saw her sister. Two of her girls had been upon the point of
- marriage, and after all there was nothing in it.
-
- “I do not blame Jane,” she continued, “for Jane would have got
- Mr. Bingley if she could. But Lizzy! Oh, sister! It is very hard
- to think that she might have been Mr. Collins’s wife by this
- time, had it not been for her own perverseness. He made her an
- offer in this very room, and she refused him. The consequence of
- it is, that Lady Lucas will have a daughter married before I
- have, and that the Longbourn estate is just as much entailed as
- ever. The Lucases are very artful people indeed, sister. They are
- all for what they can get. I am sorry to say it of them, but so
- it is. It makes me very nervous and poorly, to be thwarted so in
- my own family, and to have neighbours who think of themselves
- before anybody else. However, your coming just at this time is
- the greatest of comforts, and I am very glad to hear what you
- tell us, of long sleeves.”
-
- Mrs. Gardiner, to whom the chief of this news had been given
- before, in the course of Jane and Elizabeth’s correspondence with
- her, made her sister a slight answer, and, in compassion to her
- nieces, turned the conversation.
-
- When alone with Elizabeth afterwards, she spoke more on the
- subject. “It seems likely to have been a desirable match for
- Jane,” said she. “I am sorry it went off. But these things happen
- so often! A young man, such as you describe Mr. Bingley, so
- easily falls in love with a pretty girl for a few weeks, and when
- accident separates them, so easily forgets her, that these sort
- of inconsistencies are very frequent.”
-
- “An excellent consolation in its way,” said Elizabeth, “but it
- will not do for _us_. We do not suffer by accident. It does not
- often happen that the interference of friends will persuade a
- young man of independent fortune to think no more of a girl whom
- he was violently in love with only a few days before.”
-
- “But that expression of ‘violently in love’ is so hackneyed, so
- doubtful, so indefinite, that it gives me very little idea. It is
- as often applied to feelings which arise from a half-hour’s
- acquaintance, as to a real, strong attachment. Pray, how _violent
- was_ Mr. Bingley’s love?”
-
- “I never saw a more promising inclination; he was growing quite
- inattentive to other people, and wholly engrossed by her. Every
- time they met, it was more decided and remarkable. At his own
- ball he offended two or three young ladies, by not asking them to
- dance; and I spoke to him twice myself, without receiving an
- answer. Could there be finer symptoms? Is not general incivility
- the very essence of love?”
-
- “Oh, yes!—of that kind of love which I suppose him to have felt.
- Poor Jane! I am sorry for her, because, with her disposition, she
- may not get over it immediately. It had better have happened to
- _you_, Lizzy; you would have laughed yourself out of it sooner.
- But do you think she would be prevailed upon to go back with us?
- Change of scene might be of service—and perhaps a little relief
- from home may be as useful as anything.”
-
- Elizabeth was exceedingly pleased with this proposal, and felt
- persuaded of her sister’s ready acquiescence.
-
- “I hope,” added Mrs. Gardiner, “that no consideration with regard
- to this young man will influence her. We live in so different a
- part of town, all our connections are so different, and, as you
- well know, we go out so little, that it is very improbable that
- they should meet at all, unless he really comes to see her.”
-
- “And _that_ is quite impossible; for he is now in the custody of
- his friend, and Mr. Darcy would no more suffer him to call on
- Jane in such a part of London! My dear aunt, how could you think
- of it? Mr. Darcy may perhaps have _heard_ of such a place as
- Gracechurch Street, but he would hardly think a month’s ablution
- enough to cleanse him from its impurities, were he once to enter
- it; and depend upon it, Mr. Bingley never stirs without him.”
-
- “So much the better. I hope they will not meet at all. But does
- not Jane correspond with his sister? _She_ will not be able to
- help calling.”
-
- “She will drop the acquaintance entirely.”
-
- But in spite of the certainty in which Elizabeth affected to
- place this point, as well as the still more interesting one of
- Bingley’s being withheld from seeing Jane, she felt a solicitude
- on the subject which convinced her, on examination, that she did
- not consider it entirely hopeless. It was possible, and sometimes
- she thought it probable, that his affection might be reanimated,
- and the influence of his friends successfully combated by the
- more natural influence of Jane’s attractions.
-
- Miss Bennet accepted her aunt’s invitation with pleasure; and the
- Bingleys were no otherwise in her thoughts at the same time, than
- as she hoped by Caroline’s not living in the same house with her
- brother, she might occasionally spend a morning with her, without
- any danger of seeing him.
-
- The Gardiners stayed a week at Longbourn; and what with the
- Phillipses, the Lucases, and the officers, there was not a day
- without its engagement. Mrs. Bennet had so carefully provided for
- the entertainment of her brother and sister, that they did not
- once sit down to a family dinner. When the engagement was for
- home, some of the officers always made part of it—of which
- officers Mr. Wickham was sure to be one; and on these occasions,
- Mrs. Gardiner, rendered suspicious by Elizabeth’s warm
- commendation, narrowly observed them both. Without supposing
- them, from what she saw, to be very seriously in love, their
- preference of each other was plain enough to make her a little
- uneasy; and she resolved to speak to Elizabeth on the subject
- before she left Hertfordshire, and represent to her the
- imprudence of encouraging such an attachment.
-
- To Mrs. Gardiner, Wickham had one means of affording pleasure,
- unconnected with his general powers. About ten or a dozen years
- ago, before her marriage, she had spent a considerable time in
- that very part of Derbyshire to which he belonged. They had,
- therefore, many acquaintances in common; and though Wickham had
- been little there since the death of Darcy’s father, it was yet
- in his power to give her fresher intelligence of her former
- friends than she had been in the way of procuring.
-
- Mrs. Gardiner had seen Pemberley, and known the late Mr. Darcy by
- character perfectly well. Here consequently was an inexhaustible
- subject of discourse. In comparing her recollection of Pemberley
- with the minute description which Wickham could give, and in
- bestowing her tribute of praise on the character of its late
- possessor, she was delighting both him and herself. On being made
- acquainted with the present Mr. Darcy’s treatment of him, she
- tried to remember some of that gentleman’s reputed disposition
- when quite a lad which might agree with it, and was confident at
- last that she recollected having heard Mr. Fitzwilliam Darcy
- formerly spoken of as a very proud, ill-natured boy.
-
-
-
-
- Chapter 26
-
- Mrs. Gardiner’s caution to Elizabeth was punctually and kindly
- given on the first favourable opportunity of speaking to her
- alone; after honestly telling her what she thought, she thus went
- on:
-
- “You are too sensible a girl, Lizzy, to fall in love merely
- because you are warned against it; and, therefore, I am not
- afraid of speaking openly. Seriously, I would have you be on your
- guard. Do not involve yourself or endeavour to involve him in an
- affection which the want of fortune would make so very imprudent.
- I have nothing to say against _him_; he is a most interesting
- young man; and if he had the fortune he ought to have, I should
- think you could not do better. But as it is, you must not let
- your fancy run away with you. You have sense, and we all expect
- you to use it. Your father would depend on _your_ resolution and
- good conduct, I am sure. You must not disappoint your father.”
-
- “My dear aunt, this is being serious indeed.”
-
- “Yes, and I hope to engage you to be serious likewise.”
-
- “Well, then, you need not be under any alarm. I will take care of
- myself, and of Mr. Wickham too. He shall not be in love with me,
- if I can prevent it.”
-
- “Elizabeth, you are not serious now.”
-
- “I beg your pardon, I will try again. At present I am not in love
- with Mr. Wickham; no, I certainly am not. But he is, beyond all
- comparison, the most agreeable man I ever saw—and if he becomes
- really attached to me—I believe it will be better that he should
- not. I see the imprudence of it. Oh! _that_ abominable Mr. Darcy!
- My father’s opinion of me does me the greatest honour, and I
- should be miserable to forfeit it. My father, however, is partial
- to Mr. Wickham. In short, my dear aunt, I should be very sorry to
- be the means of making any of you unhappy; but since we see every
- day that where there is affection, young people are seldom
- withheld by immediate want of fortune from entering into
- engagements with each other, how can I promise to be wiser than
- so many of my fellow-creatures if I am tempted, or how am I even
- to know that it would be wisdom to resist? All that I can promise
- you, therefore, is not to be in a hurry. I will not be in a hurry
- to believe myself his first object. When I am in company with
- him, I will not be wishing. In short, I will do my best.”
-
- “Perhaps it will be as well if you discourage his coming here so
- very often. At least, you should not _remind_ your mother of
- inviting him.”
-
- “As I did the other day,” said Elizabeth with a conscious smile:
- “very true, it will be wise in me to refrain from _that_. But do
- not imagine that he is always here so often. It is on your
- account that he has been so frequently invited this week. You
- know my mother’s ideas as to the necessity of constant company
- for her friends. But really, and upon my honour, I will try to do
- what I think to be the wisest; and now I hope you are satisfied.”
-
- Her aunt assured her that she was, and Elizabeth having thanked
- her for the kindness of her hints, they parted; a wonderful
- instance of advice being given on such a point, without being
- resented.
-
- Mr. Collins returned into Hertfordshire soon after it had been
- quitted by the Gardiners and Jane; but as he took up his abode
- with the Lucases, his arrival was no great inconvenience to Mrs.
- Bennet. His marriage was now fast approaching, and she was at
- length so far resigned as to think it inevitable, and even
- repeatedly to say, in an ill-natured tone, that she “_wished_
- they might be happy.” Thursday was to be the wedding day, and on
- Wednesday Miss Lucas paid her farewell visit; and when she rose
- to take leave, Elizabeth, ashamed of her mother’s ungracious and
- reluctant good wishes, and sincerely affected herself,
- accompanied her out of the room. As they went downstairs
- together, Charlotte said:
-
- “I shall depend on hearing from you very often, Eliza.”
-
- “_That_ you certainly shall.”
-
- “And I have another favour to ask you. Will you come and see me?”
-
- “We shall often meet, I hope, in Hertfordshire.”
-
- “I am not likely to leave Kent for some time. Promise me,
- therefore, to come to Hunsford.”
-
- Elizabeth could not refuse, though she foresaw little pleasure in
- the visit.
-
- “My father and Maria are coming to me in March,” added Charlotte,
- “and I hope you will consent to be of the party. Indeed, Eliza,
- you will be as welcome as either of them.”
-
- The wedding took place; the bride and bridegroom set off for Kent
- from the church door, and everybody had as much to say, or to
- hear, on the subject as usual. Elizabeth soon heard from her
- friend; and their correspondence was as regular and frequent as
- it had ever been; that it should be equally unreserved was
- impossible. Elizabeth could never address her without feeling
- that all the comfort of intimacy was over, and though determined
- not to slacken as a correspondent, it was for the sake of what
- had been, rather than what was. Charlotte’s first letters were
- received with a good deal of eagerness; there could not but be
- curiosity to know how she would speak of her new home, how she
- would like Lady Catherine, and how happy she would dare pronounce
- herself to be; though, when the letters were read, Elizabeth felt
- that Charlotte expressed herself on every point exactly as she
- might have foreseen. She wrote cheerfully, seemed surrounded with
- comforts, and mentioned nothing which she could not praise. The
- house, furniture, neighbourhood, and roads, were all to her
- taste, and Lady Catherine’s behaviour was most friendly and
- obliging. It was Mr. Collins’s picture of Hunsford and Rosings
- rationally softened; and Elizabeth perceived that she must wait
- for her own visit there to know the rest.
-
- Jane had already written a few lines to her sister to announce
- their safe arrival in London; and when she wrote again, Elizabeth
- hoped it would be in her power to say something of the Bingleys.
-
- Her impatience for this second letter was as well rewarded as
- impatience generally is. Jane had been a week in town without
- either seeing or hearing from Caroline. She accounted for it,
- however, by supposing that her last letter to her friend from
- Longbourn had by some accident been lost.
-
- “My aunt,” she continued, “is going to-morrow into that part of
- the town, and I shall take the opportunity of calling in
- Grosvenor Street.”
-
- She wrote again when the visit was paid, and she had seen Miss
- Bingley. “I did not think Caroline in spirits,” were her words,
- “but she was very glad to see me, and reproached me for giving
- her no notice of my coming to London. I was right, therefore, my
- last letter had never reached her. I inquired after their
- brother, of course. He was well, but so much engaged with Mr.
- Darcy that they scarcely ever saw him. I found that Miss Darcy
- was expected to dinner. I wish I could see her. My visit was not
- long, as Caroline and Mrs. Hurst were going out. I dare say I
- shall see them soon here.”
-
- Elizabeth shook her head over this letter. It convinced her that
- accident only could discover to Mr. Bingley her sister’s being in
- town.
-
- Four weeks passed away, and Jane saw nothing of him. She
- endeavoured to persuade herself that she did not regret it; but
- she could no longer be blind to Miss Bingley’s inattention. After
- waiting at home every morning for a fortnight, and inventing
- every evening a fresh excuse for her, the visitor did at last
- appear; but the shortness of her stay, and yet more, the
- alteration of her manner would allow Jane to deceive herself no
- longer. The letter which she wrote on this occasion to her sister
- will prove what she felt.
-
- “My dearest Lizzy will, I am sure, be incapable of triumphing in
- her better judgement, at my expense, when I confess myself to
- have been entirely deceived in Miss Bingley’s regard for me. But,
- my dear sister, though the event has proved you right, do not
- think me obstinate if I still assert that, considering what her
- behaviour was, my confidence was as natural as your suspicion. I
- do not at all comprehend her reason for wishing to be intimate
- with me; but if the same circumstances were to happen again, I am
- sure I should be deceived again. Caroline did not return my visit
- till yesterday; and not a note, not a line, did I receive in the
- meantime. When she did come, it was very evident that she had no
- pleasure in it; she made a slight, formal apology, for not
- calling before, said not a word of wishing to see me again, and
- was in every respect so altered a creature, that when she went
- away I was perfectly resolved to continue the acquaintance no
- longer. I pity, though I cannot help blaming her. She was very
- wrong in singling me out as she did; I can safely say that every
- advance to intimacy began on her side. But I pity her, because
- she must feel that she has been acting wrong, and because I am
- very sure that anxiety for her brother is the cause of it. I need
- not explain myself farther; and though _we_ know this anxiety to
- be quite needless, yet if she feels it, it will easily account
- for her behaviour to me; and so deservedly dear as he is to his
- sister, whatever anxiety she must feel on his behalf is natural
- and amiable. I cannot but wonder, however, at her having any such
- fears now, because, if he had at all cared about me, we must have
- met, long ago. He knows of my being in town, I am certain, from
- something she said herself; and yet it would seem, by her manner
- of talking, as if she wanted to persuade herself that he is
- really partial to Miss Darcy. I cannot understand it. If I were
- not afraid of judging harshly, I should be almost tempted to say
- that there is a strong appearance of duplicity in all this. But I
- will endeavour to banish every painful thought, and think only of
- what will make me happy—your affection, and the invariable
- kindness of my dear uncle and aunt. Let me hear from you very
- soon. Miss Bingley said something of his never returning to
- Netherfield again, of giving up the house, but not with any
- certainty. We had better not mention it. I am extremely glad that
- you have such pleasant accounts from our friends at Hunsford.
- Pray go to see them, with Sir William and Maria. I am sure you
- will be very comfortable there.—Yours, etc.”
-
- This letter gave Elizabeth some pain; but her spirits returned as
- she considered that Jane would no longer be duped, by the sister
- at least. All expectation from the brother was now absolutely
- over. She would not even wish for a renewal of his attentions.
- His character sunk on every review of it; and as a punishment for
- him, as well as a possible advantage to Jane, she seriously hoped
- he might really soon marry Mr. Darcy’s sister, as by Wickham’s
- account, she would make him abundantly regret what he had thrown
- away.
-
- Mrs. Gardiner about this time reminded Elizabeth of her promise
- concerning that gentleman, and required information; and
- Elizabeth had such to send as might rather give contentment to
- her aunt than to herself. His apparent partiality had subsided,
- his attentions were over, he was the admirer of some one else.
- Elizabeth was watchful enough to see it all, but she could see it
- and write of it without material pain. Her heart had been but
- slightly touched, and her vanity was satisfied with believing
- that _she_ would have been his only choice, had fortune permitted
- it. The sudden acquisition of ten thousand pounds was the most
- remarkable charm of the young lady to whom he was now rendering
- himself agreeable; but Elizabeth, less clear-sighted perhaps in
- this case than in Charlotte’s, did not quarrel with him for his
- wish of independence. Nothing, on the contrary, could be more
- natural; and while able to suppose that it cost him a few
- struggles to relinquish her, she was ready to allow it a wise and
- desirable measure for both, and could very sincerely wish him
- happy.
-
- All this was acknowledged to Mrs. Gardiner; and after relating
- the circumstances, she thus went on: “I am now convinced, my dear
- aunt, that I have never been much in love; for had I really
- experienced that pure and elevating passion, I should at present
- detest his very name, and wish him all manner of evil. But my
- feelings are not only cordial towards _him_; they are even
- impartial towards Miss King. I cannot find out that I hate her at
- all, or that I am in the least unwilling to think her a very good
- sort of girl. There can be no love in all this. My watchfulness
- has been effectual; and though I certainly should be a more
- interesting object to all my acquaintances were I distractedly in
- love with him, I cannot say that I regret my comparative
- insignificance. Importance may sometimes be purchased too dearly.
- Kitty and Lydia take his defection much more to heart than I do.
- They are young in the ways of the world, and not yet open to the
- mortifying conviction that handsome young men must have something
- to live on as well as the plain.”
-
-
-
-
- Chapter 27
-
- With no greater events than these in the Longbourn family, and
- otherwise diversified by little beyond the walks to Meryton,
- sometimes dirty and sometimes cold, did January and February pass
- away. March was to take Elizabeth to Hunsford. She had not at
- first thought very seriously of going thither; but Charlotte, she
- soon found, was depending on the plan and she gradually learned
- to consider it herself with greater pleasure as well as greater
- certainty. Absence had increased her desire of seeing Charlotte
- again, and weakened her disgust of Mr. Collins. There was novelty
- in the scheme, and as, with such a mother and such
- uncompanionable sisters, home could not be faultless, a little
- change was not unwelcome for its own sake. The journey would
- moreover give her a peep at Jane; and, in short, as the time drew
- near, she would have been very sorry for any delay. Everything,
- however, went on smoothly, and was finally settled according to
- Charlotte’s first sketch. She was to accompany Sir William and
- his second daughter. The improvement of spending a night in
- London was added in time, and the plan became perfect as plan
- could be.
-
- The only pain was in leaving her father, who would certainly miss
- her, and who, when it came to the point, so little liked her
- going, that he told her to write to him, and almost promised to
- answer her letter.
-
- The farewell between herself and Mr. Wickham was perfectly
- friendly; on his side even more. His present pursuit could not
- make him forget that Elizabeth had been the first to excite and
- to deserve his attention, the first to listen and to pity, the
- first to be admired; and in his manner of bidding her adieu,
- wishing her every enjoyment, reminding her of what she was to
- expect in Lady Catherine de Bourgh, and trusting their opinion of
- her—their opinion of everybody—would always coincide, there was a
- solicitude, an interest which she felt must ever attach her to
- him with a most sincere regard; and she parted from him convinced
- that, whether married or single, he must always be her model of
- the amiable and pleasing.
-
- Her fellow-travellers the next day were not of a kind to make her
- think him less agreeable. Sir William Lucas, and his daughter
- Maria, a good-humoured girl, but as empty-headed as himself, had
- nothing to say that could be worth hearing, and were listened to
- with about as much delight as the rattle of the chaise. Elizabeth
- loved absurdities, but she had known Sir William’s too long. He
- could tell her nothing new of the wonders of his presentation and
- knighthood; and his civilities were worn out, like his
- information.
-
- It was a journey of only twenty-four miles, and they began it so
- early as to be in Gracechurch Street by noon. As they drove to
- Mr. Gardiner’s door, Jane was at a drawing-room window watching
- their arrival; when they entered the passage she was there to
- welcome them, and Elizabeth, looking earnestly in her face, was
- pleased to see it healthful and lovely as ever. On the stairs
- were a troop of little boys and girls, whose eagerness for their
- cousin’s appearance would not allow them to wait in the
- drawing-room, and whose shyness, as they had not seen her for a
- twelvemonth, prevented their coming lower. All was joy and
- kindness. The day passed most pleasantly away; the morning in
- bustle and shopping, and the evening at one of the theatres.
-
- Elizabeth then contrived to sit by her aunt. Their first object
- was her sister; and she was more grieved than astonished to hear,
- in reply to her minute inquiries, that though Jane always
- struggled to support her spirits, there were periods of
- dejection. It was reasonable, however, to hope that they would
- not continue long. Mrs. Gardiner gave her the particulars also of
- Miss Bingley’s visit in Gracechurch Street, and repeated
- conversations occurring at different times between Jane and
- herself, which proved that the former had, from her heart, given
- up the acquaintance.
-
- Mrs. Gardiner then rallied her niece on Wickham’s desertion, and
- complimented her on bearing it so well.
-
- “But my dear Elizabeth,” she added, “what sort of girl is Miss
- King? I should be sorry to think our friend mercenary.”
-
- “Pray, my dear aunt, what is the difference in matrimonial
- affairs, between the mercenary and the prudent motive? Where does
- discretion end, and avarice begin? Last Christmas you were afraid
- of his marrying me, because it would be imprudent; and now,
- because he is trying to get a girl with only ten thousand pounds,
- you want to find out that he is mercenary.”
-
- “If you will only tell me what sort of girl Miss King is, I shall
- know what to think.”
-
- “She is a very good kind of girl, I believe. I know no harm of
- her.”
-
- “But he paid her not the smallest attention till her
- grandfather’s death made her mistress of this fortune.”
-
- “No—why should he? If it were not allowable for him to gain _my_
- affections because I had no money, what occasion could there be
- for making love to a girl whom he did not care about, and who was
- equally poor?”
-
- “But there seems an indelicacy in directing his attentions
- towards her so soon after this event.”
-
- “A man in distressed circumstances has not time for all those
- elegant decorums which other people may observe. If _she_ does
- not object to it, why should _we_?”
-
- “_Her_ not objecting does not justify _him_. It only shows her
- being deficient in something herself—sense or feeling.”
-
- “Well,” cried Elizabeth, “have it as you choose. _He_ shall be
- mercenary, and _she_ shall be foolish.”
-
- “No, Lizzy, that is what I do _not_ choose. I should be sorry,
- you know, to think ill of a young man who has lived so long in
- Derbyshire.”
-
- “Oh! if that is all, I have a very poor opinion of young men who
- live in Derbyshire; and their intimate friends who live in
- Hertfordshire are not much better. I am sick of them all. Thank
- Heaven! I am going to-morrow where I shall find a man who has not
- one agreeable quality, who has neither manner nor sense to
- recommend him. Stupid men are the only ones worth knowing, after
- all.”
-
- “Take care, Lizzy; that speech savours strongly of
- disappointment.”
-
- Before they were separated by the conclusion of the play, she had
- the unexpected happiness of an invitation to accompany her uncle
- and aunt in a tour of pleasure which they proposed taking in the
- summer.
-
- “We have not determined how far it shall carry us,” said Mrs.
- Gardiner, “but, perhaps, to the Lakes.”
-
- No scheme could have been more agreeable to Elizabeth, and her
- acceptance of the invitation was most ready and grateful. “Oh, my
- dear, dear aunt,” she rapturously cried, “what delight! what
- felicity! You give me fresh life and vigour. Adieu to
- disappointment and spleen. What are young men to rocks and
- mountains? Oh! what hours of transport we shall spend! And when
- we _do_ return, it shall not be like other travellers, without
- being able to give one accurate idea of anything. We _will_ know
- where we have gone—we _will_ recollect what we have seen. Lakes,
- mountains, and rivers shall not be jumbled together in our
- imaginations; nor when we attempt to describe any particular
- scene, will we begin quarreling about its relative situation. Let
- _our_ first effusions be less insupportable than those of the
- generality of travellers.”
-
-
-
-
- Chapter 28
-
- Every object in the next day’s journey was new and interesting to
- Elizabeth; and her spirits were in a state of enjoyment; for she
- had seen her sister looking so well as to banish all fear for her
- health, and the prospect of her northern tour was a constant
- source of delight.
-
- When they left the high road for the lane to Hunsford, every eye
- was in search of the Parsonage, and every turning expected to
- bring it in view. The palings of Rosings Park was their boundary
- on one side. Elizabeth smiled at the recollection of all that she
- had heard of its inhabitants.
-
- At length the Parsonage was discernible. The garden sloping to
- the road, the house standing in it, the green pales, and the
- laurel hedge, everything declared they were arriving. Mr. Collins
- and Charlotte appeared at the door, and the carriage stopped at
- the small gate which led by a short gravel walk to the house,
- amidst the nods and smiles of the whole party. In a moment they
- were all out of the chaise, rejoicing at the sight of each other.
- Mrs. Collins welcomed her friend with the liveliest pleasure, and
- Elizabeth was more and more satisfied with coming when she found
- herself so affectionately received. She saw instantly that her
- cousin’s manners were not altered by his marriage; his formal
- civility was just what it had been, and he detained her some
- minutes at the gate to hear and satisfy his inquiries after all
- her family. They were then, with no other delay than his pointing
- out the neatness of the entrance, taken into the house; and as
- soon as they were in the parlour, he welcomed them a second time,
- with ostentatious formality to his humble abode, and punctually
- repeated all his wife’s offers of refreshment.
-
- Elizabeth was prepared to see him in his glory; and she could not
- help in fancying that in displaying the good proportion of the
- room, its aspect and its furniture, he addressed himself
- particularly to her, as if wishing to make her feel what she had
- lost in refusing him. But though everything seemed neat and
- comfortable, she was not able to gratify him by any sigh of
- repentance, and rather looked with wonder at her friend that she
- could have so cheerful an air with such a companion. When Mr.
- Collins said anything of which his wife might reasonably be
- ashamed, which certainly was not unseldom, she involuntarily
- turned her eye on Charlotte. Once or twice she could discern a
- faint blush; but in general Charlotte wisely did not hear. After
- sitting long enough to admire every article of furniture in the
- room, from the sideboard to the fender, to give an account of
- their journey, and of all that had happened in London, Mr.
- Collins invited them to take a stroll in the garden, which was
- large and well laid out, and to the cultivation of which he
- attended himself. To work in this garden was one of his most
- respectable pleasures; and Elizabeth admired the command of
- countenance with which Charlotte talked of the healthfulness of
- the exercise, and owned she encouraged it as much as possible.
- Here, leading the way through every walk and cross walk, and
- scarcely allowing them an interval to utter the praises he asked
- for, every view was pointed out with a minuteness which left
- beauty entirely behind. He could number the fields in every
- direction, and could tell how many trees there were in the most
- distant clump. But of all the views which his garden, or which
- the country or kingdom could boast, none were to be compared with
- the prospect of Rosings, afforded by an opening in the trees that
- bordered the park nearly opposite the front of his house. It was
- a handsome modern building, well situated on rising ground.
-
- From his garden, Mr. Collins would have led them round his two
- meadows; but the ladies, not having shoes to encounter the
- remains of a white frost, turned back; and while Sir William
- accompanied him, Charlotte took her sister and friend over the
- house, extremely well pleased, probably, to have the opportunity
- of showing it without her husband’s help. It was rather small,
- but well built and convenient; and everything was fitted up and
- arranged with a neatness and consistency of which Elizabeth gave
- Charlotte all the credit. When Mr. Collins could be forgotten,
- there was really an air of great comfort throughout, and by
- Charlotte’s evident enjoyment of it, Elizabeth supposed he must
- be often forgotten.
-
- She had already learnt that Lady Catherine was still in the
- country. It was spoken of again while they were at dinner, when
- Mr. Collins joining in, observed:
-
- “Yes, Miss Elizabeth, you will have the honour of seeing Lady
- Catherine de Bourgh on the ensuing Sunday at church, and I need
- not say you will be delighted with her. She is all affability and
- condescension, and I doubt not but you will be honoured with some
- portion of her notice when service is over. I have scarcely any
- hesitation in saying she will include you and my sister Maria in
- every invitation with which she honours us during your stay here.
- Her behaviour to my dear Charlotte is charming. We dine at
- Rosings twice every week, and are never allowed to walk home. Her
- ladyship’s carriage is regularly ordered for us. I _should_ say,
- one of her ladyship’s carriages, for she has several.”
-
- “Lady Catherine is a very respectable, sensible woman indeed,”
- added Charlotte, “and a most attentive neighbour.”
-
- “Very true, my dear, that is exactly what I say. She is the sort
- of woman whom one cannot regard with too much deference.”
-
- The evening was spent chiefly in talking over Hertfordshire news,
- and telling again what had already been written; and when it
- closed, Elizabeth, in the solitude of her chamber, had to
- meditate upon Charlotte’s degree of contentment, to understand
- her address in guiding, and composure in bearing with, her
- husband, and to acknowledge that it was all done very well. She
- had also to anticipate how her visit would pass, the quiet tenor
- of their usual employments, the vexatious interruptions of Mr.
- Collins, and the gaieties of their intercourse with Rosings. A
- lively imagination soon settled it all.
-
- About the middle of the next day, as she was in her room getting
- ready for a walk, a sudden noise below seemed to speak the whole
- house in confusion; and, after listening a moment, she heard
- somebody running up stairs in a violent hurry, and calling loudly
- after her. She opened the door and met Maria in the landing
- place, who, breathless with agitation, cried out—
-
- “Oh, my dear Eliza! pray make haste and come into the
- dining-room, for there is such a sight to be seen! I will not
- tell you what it is. Make haste, and come down this moment.”
-
- Elizabeth asked questions in vain; Maria would tell her nothing
- more, and down they ran into the dining-room, which fronted the
- lane, in quest of this wonder; It was two ladies stopping in a
- low phaeton at the garden gate.
-
- “And is this all?” cried Elizabeth. “I expected at least that the
- pigs were got into the garden, and here is nothing but Lady
- Catherine and her daughter.”
-
- “La! my dear,” said Maria, quite shocked at the mistake, “it is
- not Lady Catherine. The old lady is Mrs. Jenkinson, who lives
- with them; the other is Miss de Bourgh. Only look at her. She is
- quite a little creature. Who would have thought that she could be
- so thin and small?”
-
- “She is abominably rude to keep Charlotte out of doors in all
- this wind. Why does she not come in?”
-
- “Oh, Charlotte says she hardly ever does. It is the greatest of
- favours when Miss de Bourgh comes in.”
-
- “I like her appearance,” said Elizabeth, struck with other ideas.
- “She looks sickly and cross. Yes, she will do for him very well.
- She will make him a very proper wife.”
-
- Mr. Collins and Charlotte were both standing at the gate in
- conversation with the ladies; and Sir William, to Elizabeth’s
- high diversion, was stationed in the doorway, in earnest
- contemplation of the greatness before him, and constantly bowing
- whenever Miss de Bourgh looked that way.
-
- At length there was nothing more to be said; the ladies drove on,
- and the others returned into the house. Mr. Collins no sooner saw
- the two girls than he began to congratulate them on their good
- fortune, which Charlotte explained by letting them know that the
- whole party was asked to dine at Rosings the next day.
-
-
-
-
- Chapter 29
-
- Mr. Collins’s triumph, in consequence of this invitation, was
- complete. The power of displaying the grandeur of his patroness
- to his wondering visitors, and of letting them see her civility
- towards himself and his wife, was exactly what he had wished for;
- and that an opportunity of doing it should be given so soon, was
- such an instance of Lady Catherine’s condescension, as he knew
- not how to admire enough.
-
- “I confess,” said he, “that I should not have been at all
- surprised by her ladyship’s asking us on Sunday to drink tea and
- spend the evening at Rosings. I rather expected, from my
- knowledge of her affability, that it would happen. But who could
- have foreseen such an attention as this? Who could have imagined
- that we should receive an invitation to dine there (an
- invitation, moreover, including the whole party) so immediately
- after your arrival!”
-
- “I am the less surprised at what has happened,” replied Sir
- William, “from that knowledge of what the manners of the great
- really are, which my situation in life has allowed me to acquire.
- About the court, such instances of elegant breeding are not
- uncommon.”
-
- Scarcely anything was talked of the whole day or next morning but
- their visit to Rosings. Mr. Collins was carefully instructing
- them in what they were to expect, that the sight of such rooms,
- so many servants, and so splendid a dinner, might not wholly
- overpower them.
-
- When the ladies were separating for the toilette, he said to
- Elizabeth—
-
- “Do not make yourself uneasy, my dear cousin, about your apparel.
- Lady Catherine is far from requiring that elegance of dress in us
- which becomes herself and her daughter. I would advise you merely
- to put on whatever of your clothes is superior to the rest—there
- is no occasion for anything more. Lady Catherine will not think
- the worse of you for being simply dressed. She likes to have the
- distinction of rank preserved.”
-
- While they were dressing, he came two or three times to their
- different doors, to recommend their being quick, as Lady
- Catherine very much objected to be kept waiting for her dinner.
- Such formidable accounts of her ladyship, and her manner of
- living, quite frightened Maria Lucas who had been little used to
- company, and she looked forward to her introduction at Rosings
- with as much apprehension as her father had done to his
- presentation at St. James’s.
-
- As the weather was fine, they had a pleasant walk of about half a
- mile across the park. Every park has its beauty and its
- prospects; and Elizabeth saw much to be pleased with, though she
- could not be in such raptures as Mr. Collins expected the scene
- to inspire, and was but slightly affected by his enumeration of
- the windows in front of the house, and his relation of what the
- glazing altogether had originally cost Sir Lewis de Bourgh.
-
- When they ascended the steps to the hall, Maria’s alarm was every
- moment increasing, and even Sir William did not look perfectly
- calm. Elizabeth’s courage did not fail her. She had heard nothing
- of Lady Catherine that spoke her awful from any extraordinary
- talents or miraculous virtue, and the mere stateliness of money
- or rank she thought she could witness without trepidation.
-
- From the entrance-hall, of which Mr. Collins pointed out, with a
- rapturous air, the fine proportion and the finished ornaments,
- they followed the servants through an ante-chamber, to the room
- where Lady Catherine, her daughter, and Mrs. Jenkinson were
- sitting. Her ladyship, with great condescension, arose to receive
- them; and as Mrs. Collins had settled it with her husband that
- the office of introduction should be hers, it was performed in a
- proper manner, without any of those apologies and thanks which he
- would have thought necessary.
-
- In spite of having been at St. James’s, Sir William was so
- completely awed by the grandeur surrounding him, that he had but
- just courage enough to make a very low bow, and take his seat
- without saying a word; and his daughter, frightened almost out of
- her senses, sat on the edge of her chair, not knowing which way
- to look. Elizabeth found herself quite equal to the scene, and
- could observe the three ladies before her composedly. Lady
- Catherine was a tall, large woman, with strongly-marked features,
- which might once have been handsome. Her air was not
- conciliating, nor was her manner of receiving them such as to
- make her visitors forget their inferior rank. She was not
- rendered formidable by silence; but whatever she said was spoken
- in so authoritative a tone, as marked her self-importance, and
- brought Mr. Wickham immediately to Elizabeth’s mind; and from the
- observation of the day altogether, she believed Lady Catherine to
- be exactly what he represented.
-
- When, after examining the mother, in whose countenance and
- deportment she soon found some resemblance of Mr. Darcy, she
- turned her eyes on the daughter, she could almost have joined in
- Maria’s astonishment at her being so thin and so small. There was
- neither in figure nor face any likeness between the ladies. Miss
- de Bourgh was pale and sickly; her features, though not plain,
- were insignificant; and she spoke very little, except in a low
- voice, to Mrs. Jenkinson, in whose appearance there was nothing
- remarkable, and who was entirely engaged in listening to what she
- said, and placing a screen in the proper direction before her
- eyes.
-
- After sitting a few minutes, they were all sent to one of the
- windows to admire the view, Mr. Collins attending them to point
- out its beauties, and Lady Catherine kindly informing them that
- it was much better worth looking at in the summer.
-
- The dinner was exceedingly handsome, and there were all the
- servants and all the articles of plate which Mr. Collins had
- promised; and, as he had likewise foretold, he took his seat at
- the bottom of the table, by her ladyship’s desire, and looked as
- if he felt that life could furnish nothing greater. He carved,
- and ate, and praised with delighted alacrity; and every dish was
- commended, first by him and then by Sir William, who was now
- enough recovered to echo whatever his son-in-law said, in a
- manner which Elizabeth wondered Lady Catherine could bear. But
- Lady Catherine seemed gratified by their excessive admiration,
- and gave most gracious smiles, especially when any dish on the
- table proved a novelty to them. The party did not supply much
- conversation. Elizabeth was ready to speak whenever there was an
- opening, but she was seated between Charlotte and Miss de
- Bourgh—the former of whom was engaged in listening to Lady
- Catherine, and the latter said not a word to her all dinner-time.
- Mrs. Jenkinson was chiefly employed in watching how little Miss
- de Bourgh ate, pressing her to try some other dish, and fearing
- she was indisposed. Maria thought speaking out of the question,
- and the gentlemen did nothing but eat and admire.
-
- When the ladies returned to the drawing-room, there was little to
- be done but to hear Lady Catherine talk, which she did without
- any intermission till coffee came in, delivering her opinion on
- every subject in so decisive a manner, as proved that she was not
- used to have her judgement controverted. She inquired into
- Charlotte’s domestic concerns familiarly and minutely, gave her a
- great deal of advice as to the management of them all; told her
- how everything ought to be regulated in so small a family as
- hers, and instructed her as to the care of her cows and her
- poultry. Elizabeth found that nothing was beneath this great
- lady’s attention, which could furnish her with an occasion of
- dictating to others. In the intervals of her discourse with Mrs.
- Collins, she addressed a variety of questions to Maria and
- Elizabeth, but especially to the latter, of whose connections she
- knew the least, and who she observed to Mrs. Collins was a very
- genteel, pretty kind of girl. She asked her, at different times,
- how many sisters she had, whether they were older or younger than
- herself, whether any of them were likely to be married, whether
- they were handsome, where they had been educated, what carriage
- her father kept, and what had been her mother’s maiden name?
- Elizabeth felt all the impertinence of her questions but answered
- them very composedly. Lady Catherine then observed,
-
- “Your father’s estate is entailed on Mr. Collins, I think. For
- your sake,” turning to Charlotte, “I am glad of it; but otherwise
- I see no occasion for entailing estates from the female line. It
- was not thought necessary in Sir Lewis de Bourgh’s family. Do you
- play and sing, Miss Bennet?”
-
- “A little.”
-
- “Oh! then—some time or other we shall be happy to hear you. Our
- instrument is a capital one, probably superior to——You shall try
- it some day. Do your sisters play and sing?”
-
- “One of them does.”
-
- “Why did not you all learn? You ought all to have learned. The
- Miss Webbs all play, and their father has not so good an income
- as yours. Do you draw?”
-
- “No, not at all.”
-
- “What, none of you?”
-
- “Not one.”
-
- “That is very strange. But I suppose you had no opportunity. Your
- mother should have taken you to town every spring for the benefit
- of masters.”
-
- “My mother would have had no objection, but my father hates
- London.”
-
- “Has your governess left you?”
-
- “We never had any governess.”
-
- “No governess! How was that possible? Five daughters brought up
- at home without a governess! I never heard of such a thing. Your
- mother must have been quite a slave to your education.”
-
- Elizabeth could hardly help smiling as she assured her that had
- not been the case.
-
- “Then, who taught you? who attended to you? Without a governess,
- you must have been neglected.”
-
- “Compared with some families, I believe we were; but such of us
- as wished to learn never wanted the means. We were always
- encouraged to read, and had all the masters that were necessary.
- Those who chose to be idle, certainly might.”
-
- “Aye, no doubt; but that is what a governess will prevent, and if
- I had known your mother, I should have advised her most
- strenuously to engage one. I always say that nothing is to be
- done in education without steady and regular instruction, and
- nobody but a governess can give it. It is wonderful how many
- families I have been the means of supplying in that way. I am
- always glad to get a young person well placed out. Four nieces of
- Mrs. Jenkinson are most delightfully situated through my means;
- and it was but the other day that I recommended another young
- person, who was merely accidentally mentioned to me, and the
- family are quite delighted with her. Mrs. Collins, did I tell you
- of Lady Metcalf’s calling yesterday to thank me? She finds Miss
- Pope a treasure. ‘Lady Catherine,’ said she, ‘you have given me a
- treasure.’ Are any of your younger sisters out, Miss Bennet?”
-
- “Yes, ma’am, all.”
-
- “All! What, all five out at once? Very odd! And you only the
- second. The younger ones out before the elder ones are married!
- Your younger sisters must be very young?”
-
- “Yes, my youngest is not sixteen. Perhaps _she_ is full young to
- be much in company. But really, ma’am, I think it would be very
- hard upon younger sisters, that they should not have their share
- of society and amusement, because the elder may not have the
- means or inclination to marry early. The last-born has as good a
- right to the pleasures of youth as the first. And to be kept back
- on _such_ a motive! I think it would not be very likely to
- promote sisterly affection or delicacy of mind.”
-
- “Upon my word,” said her ladyship, “you give your opinion very
- decidedly for so young a person. Pray, what is your age?”
-
- “With three younger sisters grown up,” replied Elizabeth,
- smiling, “your ladyship can hardly expect me to own it.”
-
- Lady Catherine seemed quite astonished at not receiving a direct
- answer; and Elizabeth suspected herself to be the first creature
- who had ever dared to trifle with so much dignified impertinence.
-
- “You cannot be more than twenty, I am sure, therefore you need
- not conceal your age.”
-
- “I am not one-and-twenty.”
-
- When the gentlemen had joined them, and tea was over, the
- card-tables were placed. Lady Catherine, Sir William, and Mr. and
- Mrs. Collins sat down to quadrille; and as Miss de Bourgh chose
- to play at cassino, the two girls had the honour of assisting
- Mrs. Jenkinson to make up her party. Their table was
- superlatively stupid. Scarcely a syllable was uttered that did
- not relate to the game, except when Mrs. Jenkinson expressed her
- fears of Miss de Bourgh’s being too hot or too cold, or having
- too much or too little light. A great deal more passed at the
- other table. Lady Catherine was generally speaking—stating the
- mistakes of the three others, or relating some anecdote of
- herself. Mr. Collins was employed in agreeing to everything her
- ladyship said, thanking her for every fish he won, and
- apologising if he thought he won too many. Sir William did not
- say much. He was storing his memory with anecdotes and noble
- names.
-
- When Lady Catherine and her daughter had played as long as they
- chose, the tables were broken up, the carriage was offered to
- Mrs. Collins, gratefully accepted and immediately ordered. The
- party then gathered round the fire to hear Lady Catherine
- determine what weather they were to have on the morrow. From
- these instructions they were summoned by the arrival of the
- coach; and with many speeches of thankfulness on Mr. Collins’s
- side and as many bows on Sir William’s they departed. As soon as
- they had driven from the door, Elizabeth was called on by her
- cousin to give her opinion of all that she had seen at Rosings,
- which, for Charlotte’s sake, she made more favourable than it
- really was. But her commendation, though costing her some
- trouble, could by no means satisfy Mr. Collins, and he was very
- soon obliged to take her ladyship’s praise into his own hands.
-
-
-
-
- Chapter 30
-
- Sir William stayed only a week at Hunsford, but his visit was
- long enough to convince him of his daughter’s being most
- comfortably settled, and of her possessing such a husband and
- such a neighbour as were not often met with. While Sir William
- was with them, Mr. Collins devoted his morning to driving him out
- in his gig, and showing him the country; but when he went away,
- the whole family returned to their usual employments, and
- Elizabeth was thankful to find that they did not see more of her
- cousin by the alteration, for the chief of the time between
- breakfast and dinner was now passed by him either at work in the
- garden or in reading and writing, and looking out of the window
- in his own book-room, which fronted the road. The room in which
- the ladies sat was backwards. Elizabeth had at first rather
- wondered that Charlotte should not prefer the dining-parlour for
- common use; it was a better sized room, and had a more pleasant
- aspect; but she soon saw that her friend had an excellent reason
- for what she did, for Mr. Collins would undoubtedly have been
- much less in his own apartment, had they sat in one equally
- lively; and she gave Charlotte credit for the arrangement.
-
- From the drawing-room they could distinguish nothing in the lane,
- and were indebted to Mr. Collins for the knowledge of what
- carriages went along, and how often especially Miss de Bourgh
- drove by in her phaeton, which he never failed coming to inform
- them of, though it happened almost every day. She not
- unfrequently stopped at the Parsonage, and had a few minutes’
- conversation with Charlotte, but was scarcely ever prevailed upon
- to get out.
-
- Very few days passed in which Mr. Collins did not walk to
- Rosings, and not many in which his wife did not think it
- necessary to go likewise; and till Elizabeth recollected that
- there might be other family livings to be disposed of, she could
- not understand the sacrifice of so many hours. Now and then they
- were honoured with a call from her ladyship, and nothing escaped
- her observation that was passing in the room during these visits.
- She examined into their employments, looked at their work, and
- advised them to do it differently; found fault with the
- arrangement of the furniture; or detected the housemaid in
- negligence; and if she accepted any refreshment, seemed to do it
- only for the sake of finding out that Mrs. Collins’s joints of
- meat were too large for her family.
-
- Elizabeth soon perceived, that though this great lady was not in
- commission of the peace of the county, she was a most active
- magistrate in her own parish, the minutest concerns of which were
- carried to her by Mr. Collins; and whenever any of the cottagers
- were disposed to be quarrelsome, discontented, or too poor, she
- sallied forth into the village to settle their differences,
- silence their complaints, and scold them into harmony and plenty.
-
- The entertainment of dining at Rosings was repeated about twice a
- week; and, allowing for the loss of Sir William, and there being
- only one card-table in the evening, every such entertainment was
- the counterpart of the first. Their other engagements were few,
- as the style of living in the neighbourhood in general was beyond
- Mr. Collins’s reach. This, however, was no evil to Elizabeth, and
- upon the whole she spent her time comfortably enough; there were
- half-hours of pleasant conversation with Charlotte, and the
- weather was so fine for the time of year that she had often great
- enjoyment out of doors. Her favourite walk, and where she
- frequently went while the others were calling on Lady Catherine,
- was along the open grove which edged that side of the park, where
- there was a nice sheltered path, which no one seemed to value but
- herself, and where she felt beyond the reach of Lady Catherine’s
- curiosity.
-
- In this quiet way, the first fortnight of her visit soon passed
- away. Easter was approaching, and the week preceding it was to
- bring an addition to the family at Rosings, which in so small a
- circle must be important. Elizabeth had heard soon after her
- arrival that Mr. Darcy was expected there in the course of a few
- weeks, and though there were not many of her acquaintances whom
- she did not prefer, his coming would furnish one comparatively
- new to look at in their Rosings parties, and she might be amused
- in seeing how hopeless Miss Bingley’s designs on him were, by his
- behaviour to his cousin, for whom he was evidently destined by
- Lady Catherine, who talked of his coming with the greatest
- satisfaction, spoke of him in terms of the highest admiration,
- and seemed almost angry to find that he had already been
- frequently seen by Miss Lucas and herself.
-
- His arrival was soon known at the Parsonage; for Mr. Collins was
- walking the whole morning within view of the lodges opening into
- Hunsford Lane, in order to have the earliest assurance of it, and
- after making his bow as the carriage turned into the Park,
- hurried home with the great intelligence. On the following
- morning he hastened to Rosings to pay his respects. There were
- two nephews of Lady Catherine to require them, for Mr. Darcy had
- brought with him a Colonel Fitzwilliam, the younger son of his
- uncle Lord ——, and, to the great surprise of all the party, when
- Mr. Collins returned, the gentlemen accompanied him. Charlotte
- had seen them from her husband’s room, crossing the road, and
- immediately running into the other, told the girls what an honour
- they might expect, adding:
-
- “I may thank you, Eliza, for this piece of civility. Mr. Darcy
- would never have come so soon to wait upon me.”
-
- Elizabeth had scarcely time to disclaim all right to the
- compliment, before their approach was announced by the door-bell,
- and shortly afterwards the three gentlemen entered the room.
- Colonel Fitzwilliam, who led the way, was about thirty, not
- handsome, but in person and address most truly the gentleman. Mr.
- Darcy looked just as he had been used to look in
- Hertfordshire—paid his compliments, with his usual reserve, to
- Mrs. Collins, and whatever might be his feelings toward her
- friend, met her with every appearance of composure. Elizabeth
- merely curtseyed to him without saying a word.
-
- Colonel Fitzwilliam entered into conversation directly with the
- readiness and ease of a well-bred man, and talked very
- pleasantly; but his cousin, after having addressed a slight
- observation on the house and garden to Mrs. Collins, sat for some
- time without speaking to anybody. At length, however, his
- civility was so far awakened as to inquire of Elizabeth after the
- health of her family. She answered him in the usual way, and
- after a moment’s pause, added:
-
- “My eldest sister has been in town these three months. Have you
- never happened to see her there?”
-
- She was perfectly sensible that he never had; but she wished to
- see whether he would betray any consciousness of what had passed
- between the Bingleys and Jane, and she thought he looked a little
- confused as he answered that he had never been so fortunate as to
- meet Miss Bennet. The subject was pursued no farther, and the
- gentlemen soon afterwards went away.
-
-
-
-
- Chapter 31
-
- Colonel Fitzwilliam’s manners were very much admired at the
- Parsonage, and the ladies all felt that he must add considerably
- to the pleasures of their engagements at Rosings. It was some
- days, however, before they received any invitation thither—for
- while there were visitors in the house, they could not be
- necessary; and it was not till Easter-day, almost a week after
- the gentlemen’s arrival, that they were honoured by such an
- attention, and then they were merely asked on leaving church to
- come there in the evening. For the last week they had seen very
- little of Lady Catherine or her daughter. Colonel Fitzwilliam had
- called at the Parsonage more than once during the time, but Mr.
- Darcy they had seen only at church.
-
- The invitation was accepted of course, and at a proper hour they
- joined the party in Lady Catherine’s drawing-room. Her ladyship
- received them civilly, but it was plain that their company was by
- no means so acceptable as when she could get nobody else; and she
- was, in fact, almost engrossed by her nephews, speaking to them,
- especially to Darcy, much more than to any other person in the
- room.
-
- Colonel Fitzwilliam seemed really glad to see them; anything was
- a welcome relief to him at Rosings; and Mrs. Collins’s pretty
- friend had moreover caught his fancy very much. He now seated
- himself by her, and talked so agreeably of Kent and
- Hertfordshire, of travelling and staying at home, of new books
- and music, that Elizabeth had never been half so well entertained
- in that room before; and they conversed with so much spirit and
- flow, as to draw the attention of Lady Catherine herself, as well
- as of Mr. Darcy. _His_ eyes had been soon and repeatedly turned
- towards them with a look of curiosity; and that her ladyship,
- after a while, shared the feeling, was more openly acknowledged,
- for she did not scruple to call out:
-
- “What is that you are saying, Fitzwilliam? What is it you are
- talking of? What are you telling Miss Bennet? Let me hear what it
- is.”
-
- “We are speaking of music, madam,” said he, when no longer able
- to avoid a reply.
-
- “Of music! Then pray speak aloud. It is of all subjects my
- delight. I must have my share in the conversation if you are
- speaking of music. There are few people in England, I suppose,
- who have more true enjoyment of music than myself, or a better
- natural taste. If I had ever learnt, I should have been a great
- proficient. And so would Anne, if her health had allowed her to
- apply. I am confident that she would have performed delightfully.
- How does Georgiana get on, Darcy?”
-
- Mr. Darcy spoke with affectionate praise of his sister’s
- proficiency.
-
- “I am very glad to hear such a good account of her,” said Lady
- Catherine; “and pray tell her from me, that she cannot expect to
- excel if she does not practice a good deal.”
-
- “I assure you, madam,” he replied, “that she does not need such
- advice. She practises very constantly.”
-
- “So much the better. It cannot be done too much; and when I next
- write to her, I shall charge her not to neglect it on any
- account. I often tell young ladies that no excellence in music is
- to be acquired without constant practice. I have told Miss Bennet
- several times, that she will never play really well unless she
- practises more; and though Mrs. Collins has no instrument, she is
- very welcome, as I have often told her, to come to Rosings every
- day, and play on the pianoforte in Mrs. Jenkinson’s room. She
- would be in nobody’s way, you know, in that part of the house.”
-
- Mr. Darcy looked a little ashamed of his aunt’s ill-breeding, and
- made no answer.
-
- When coffee was over, Colonel Fitzwilliam reminded Elizabeth of
- having promised to play to him; and she sat down directly to the
- instrument. He drew a chair near her. Lady Catherine listened to
- half a song, and then talked, as before, to her other nephew;
- till the latter walked away from her, and making with his usual
- deliberation towards the pianoforte stationed himself so as to
- command a full view of the fair performer’s countenance.
- Elizabeth saw what he was doing, and at the first convenient
- pause, turned to him with an arch smile, and said:
-
- “You mean to frighten me, Mr. Darcy, by coming in all this state
- to hear me? I will not be alarmed though your sister _does_ play
- so well. There is a stubbornness about me that never can bear to
- be frightened at the will of others. My courage always rises at
- every attempt to intimidate me.”
-
- “I shall not say you are mistaken,” he replied, “because you
- could not really believe me to entertain any design of alarming
- you; and I have had the pleasure of your acquaintance long enough
- to know that you find great enjoyment in occasionally professing
- opinions which in fact are not your own.”
-
- Elizabeth laughed heartily at this picture of herself, and said
- to Colonel Fitzwilliam, “Your cousin will give you a very pretty
- notion of me, and teach you not to believe a word I say. I am
- particularly unlucky in meeting with a person so able to expose
- my real character, in a part of the world where I had hoped to
- pass myself off with some degree of credit. Indeed, Mr. Darcy, it
- is very ungenerous in you to mention all that you knew to my
- disadvantage in Hertfordshire—and, give me leave to say, very
- impolitic too—for it is provoking me to retaliate, and such
- things may come out as will shock your relations to hear.”
-
- “I am not afraid of you,” said he, smilingly.
-
- “Pray let me hear what you have to accuse him of,” cried Colonel
- Fitzwilliam. “I should like to know how he behaves among
- strangers.”
-
- “You shall hear then—but prepare yourself for something very
- dreadful. The first time of my ever seeing him in Hertfordshire,
- you must know, was at a ball—and at this ball, what do you think
- he did? He danced only four dances, though gentlemen were scarce;
- and, to my certain knowledge, more than one young lady was
- sitting down in want of a partner. Mr. Darcy, you cannot deny the
- fact.”
-
- “I had not at that time the honour of knowing any lady in the
- assembly beyond my own party.”
-
- “True; and nobody can ever be introduced in a ball-room. Well,
- Colonel Fitzwilliam, what do I play next? My fingers wait your
- orders.”
-
- “Perhaps,” said Darcy, “I should have judged better, had I sought
- an introduction; but I am ill-qualified to recommend myself to
- strangers.”
-
- “Shall we ask your cousin the reason of this?” said Elizabeth,
- still addressing Colonel Fitzwilliam. “Shall we ask him why a man
- of sense and education, and who has lived in the world, is ill
- qualified to recommend himself to strangers?”
-
- “I can answer your question,” said Fitzwilliam, “without applying
- to him. It is because he will not give himself the trouble.”
-
- “I certainly have not the talent which some people possess,” said
- Darcy, “of conversing easily with those I have never seen before.
- I cannot catch their tone of conversation, or appear interested
- in their concerns, as I often see done.”
-
- “My fingers,” said Elizabeth, “do not move over this instrument
- in the masterly manner which I see so many women’s do. They have
- not the same force or rapidity, and do not produce the same
- expression. But then I have always supposed it to be my own
- fault—because I will not take the trouble of practising. It is
- not that I do not believe _my_ fingers as capable as any other
- woman’s of superior execution.”
-
- Darcy smiled and said, “You are perfectly right. You have
- employed your time much better. No one admitted to the privilege
- of hearing you can think anything wanting. We neither of us
- perform to strangers.”
-
- Here they were interrupted by Lady Catherine, who called out to
- know what they were talking of. Elizabeth immediately began
- playing again. Lady Catherine approached, and, after listening
- for a few minutes, said to Darcy:
-
- “Miss Bennet would not play at all amiss if she practised more,
- and could have the advantage of a London master. She has a very
- good notion of fingering, though her taste is not equal to
- Anne’s. Anne would have been a delightful performer, had her
- health allowed her to learn.”
-
- Elizabeth looked at Darcy to see how cordially he assented to his
- cousin’s praise; but neither at that moment nor at any other
- could she discern any symptom of love; and from the whole of his
- behaviour to Miss de Bourgh she derived this comfort for Miss
- Bingley, that he might have been just as likely to marry _her_,
- had she been his relation.
-
- Lady Catherine continued her remarks on Elizabeth’s performance,
- mixing with them many instructions on execution and taste.
- Elizabeth received them with all the forbearance of civility,
- and, at the request of the gentlemen, remained at the instrument
- till her ladyship’s carriage was ready to take them all home.
-
-
-
-
- Chapter 32
-
- Elizabeth was sitting by herself the next morning, and writing to
- Jane while Mrs. Collins and Maria were gone on business into the
- village, when she was startled by a ring at the door, the certain
- signal of a visitor. As she had heard no carriage, she thought it
- not unlikely to be Lady Catherine, and under that apprehension
- was putting away her half-finished letter that she might escape
- all impertinent questions, when the door opened, and, to her very
- great surprise, Mr. Darcy, and Mr. Darcy only, entered the room.
-
- He seemed astonished too on finding her alone, and apologised for
- his intrusion by letting her know that he had understood all the
- ladies were to be within.
-
- They then sat down, and when her inquiries after Rosings were
- made, seemed in danger of sinking into total silence. It was
- absolutely necessary, therefore, to think of something, and in
- this emergence recollecting _when_ she had seen him last in
- Hertfordshire, and feeling curious to know what he would say on
- the subject of their hasty departure, she observed:
-
- “How very suddenly you all quitted Netherfield last November, Mr.
- Darcy! It must have been a most agreeable surprise to Mr. Bingley
- to see you all after him so soon; for, if I recollect right, he
- went but the day before. He and his sisters were well, I hope,
- when you left London?”
-
- “Perfectly so, I thank you.”
-
- She found that she was to receive no other answer, and, after a
- short pause added:
-
- “I think I have understood that Mr. Bingley has not much idea of
- ever returning to Netherfield again?”
-
- “I have never heard him say so; but it is probable that he may
- spend very little of his time there in the future. He has many
- friends, and is at a time of life when friends and engagements
- are continually increasing.”
-
- “If he means to be but little at Netherfield, it would be better
- for the neighbourhood that he should give up the place entirely,
- for then we might possibly get a settled family there. But,
- perhaps, Mr. Bingley did not take the house so much for the
- convenience of the neighbourhood as for his own, and we must
- expect him to keep it or quit it on the same principle.”
-
- “I should not be surprised,” said Darcy, “if he were to give it
- up as soon as any eligible purchase offers.”
-
- Elizabeth made no answer. She was afraid of talking longer of his
- friend; and, having nothing else to say, was now determined to
- leave the trouble of finding a subject to him.
-
- He took the hint, and soon began with, “This seems a very
- comfortable house. Lady Catherine, I believe, did a great deal to
- it when Mr. Collins first came to Hunsford.”
-
- “I believe she did—and I am sure she could not have bestowed her
- kindness on a more grateful object.”
-
- “Mr. Collins appears to be very fortunate in his choice of a
- wife.”
-
- “Yes, indeed, his friends may well rejoice in his having met with
- one of the very few sensible women who would have accepted him,
- or have made him happy if they had. My friend has an excellent
- understanding—though I am not certain that I consider her
- marrying Mr. Collins as the wisest thing she ever did. She seems
- perfectly happy, however, and in a prudential light it is
- certainly a very good match for her.”
-
- “It must be very agreeable for her to be settled within so easy a
- distance of her own family and friends.”
-
- “An easy distance, do you call it? It is nearly fifty miles.”
-
- “And what is fifty miles of good road? Little more than half a
- day’s journey. Yes, I call it a very easy distance.”
-
- “I should never have considered the distance as one of the
- _advantages_ of the match,” cried Elizabeth. “I should never have
- said Mrs. Collins was settled _near_ her family.”
-
- “It is a proof of your own attachment to Hertfordshire. Anything
- beyond the very neighbourhood of Longbourn, I suppose, would
- appear far.”
-
- As he spoke there was a sort of smile which Elizabeth fancied she
- understood; he must be supposing her to be thinking of Jane and
- Netherfield, and she blushed as she answered:
-
- “I do not mean to say that a woman may not be settled too near
- her family. The far and the near must be relative, and depend on
- many varying circumstances. Where there is fortune to make the
- expenses of travelling unimportant, distance becomes no evil. But
- that is not the case _here_. Mr. and Mrs. Collins have a
- comfortable income, but not such a one as will allow of frequent
- journeys—and I am persuaded my friend would not call herself
- _near_ her family under less than _half_ the present distance.”
-
- Mr. Darcy drew his chair a little towards her, and said, “_You_
- cannot have a right to such very strong local attachment. _You_
- cannot have been always at Longbourn.”
-
- Elizabeth looked surprised. The gentleman experienced some change
- of feeling; he drew back his chair, took a newspaper from the
- table, and glancing over it, said, in a colder voice:
-
- “Are you pleased with Kent?”
-
- A short dialogue on the subject of the country ensued, on either
- side calm and concise—and soon put an end to by the entrance of
- Charlotte and her sister, just returned from her walk. The
- _tête-à-tête_ surprised them. Mr. Darcy related the mistake which
- had occasioned his intruding on Miss Bennet, and after sitting a
- few minutes longer without saying much to anybody, went away.
-
- “What can be the meaning of this?” said Charlotte, as soon as he
- was gone. “My dear, Eliza, he must be in love with you, or he
- would never have called us in this familiar way.”
-
- But when Elizabeth told of his silence, it did not seem very
- likely, even to Charlotte’s wishes, to be the case; and after
- various conjectures, they could at last only suppose his visit to
- proceed from the difficulty of finding anything to do, which was
- the more probable from the time of year. All field sports were
- over. Within doors there was Lady Catherine, books, and a
- billiard-table, but gentlemen cannot always be within doors; and
- in the nearness of the Parsonage, or the pleasantness of the walk
- to it, or of the people who lived in it, the two cousins found a
- temptation from this period of walking thither almost every day.
- They called at various times of the morning, sometimes
- separately, sometimes together, and now and then accompanied by
- their aunt. It was plain to them all that Colonel Fitzwilliam
- came because he had pleasure in their society, a persuasion which
- of course recommended him still more; and Elizabeth was reminded
- by her own satisfaction in being with him, as well as by his
- evident admiration of her, of her former favourite George
- Wickham; and though, in comparing them, she saw there was less
- captivating softness in Colonel Fitzwilliam’s manners, she
- believed he might have the best informed mind.
-
- But why Mr. Darcy came so often to the Parsonage, it was more
- difficult to understand. It could not be for society, as he
- frequently sat there ten minutes together without opening his
- lips; and when he did speak, it seemed the effect of necessity
- rather than of choice—a sacrifice to propriety, not a pleasure to
- himself. He seldom appeared really animated. Mrs. Collins knew
- not what to make of him. Colonel Fitzwilliam’s occasionally
- laughing at his stupidity, proved that he was generally
- different, which her own knowledge of him could not have told
- her; and as she would liked to have believed this change the
- effect of love, and the object of that love her friend Eliza, she
- set herself seriously to work to find it out. She watched him
- whenever they were at Rosings, and whenever he came to Hunsford;
- but without much success. He certainly looked at her friend a
- great deal, but the expression of that look was disputable. It
- was an earnest, steadfast gaze, but she often doubted whether
- there were much admiration in it, and sometimes it seemed nothing
- but absence of mind.
-
- She had once or twice suggested to Elizabeth the possibility of
- his being partial to her, but Elizabeth always laughed at the
- idea; and Mrs. Collins did not think it right to press the
- subject, from the danger of raising expectations which might only
- end in disappointment; for in her opinion it admitted not of a
- doubt, that all her friend’s dislike would vanish, if she could
- suppose him to be in her power.
-
- In her kind schemes for Elizabeth, she sometimes planned her
- marrying Colonel Fitzwilliam. He was beyond comparison the most
- pleasant man; he certainly admired her, and his situation in life
- was most eligible; but, to counterbalance these advantages, Mr.
- Darcy had considerable patronage in the church, and his cousin
- could have none at all.
-
-
-
-
- Chapter 33
-
- More than once did Elizabeth, in her ramble within the park,
- unexpectedly meet Mr. Darcy. She felt all the perverseness of the
- mischance that should bring him where no one else was brought,
- and, to prevent its ever happening again, took care to inform him
- at first that it was a favourite haunt of hers. How it could
- occur a second time, therefore, was very odd! Yet it did, and
- even a third. It seemed like wilful ill-nature, or a voluntary
- penance, for on these occasions it was not merely a few formal
- inquiries and an awkward pause and then away, but he actually
- thought it necessary to turn back and walk with her. He never
- said a great deal, nor did she give herself the trouble of
- talking or of listening much; but it struck her in the course of
- their third rencontre that he was asking some odd unconnected
- questions—about her pleasure in being at Hunsford, her love of
- solitary walks, and her opinion of Mr. and Mrs. Collins’s
- happiness; and that in speaking of Rosings and her not perfectly
- understanding the house, he seemed to expect that whenever she
- came into Kent again she would be staying _there_ too. His words
- seemed to imply it. Could he have Colonel Fitzwilliam in his
- thoughts? She supposed, if he meant anything, he must mean an
- allusion to what might arise in that quarter. It distressed her a
- little, and she was quite glad to find herself at the gate in the
- pales opposite the Parsonage.
-
- She was engaged one day as she walked, in perusing Jane’s last
- letter, and dwelling on some passages which proved that Jane had
- not written in spirits, when, instead of being again surprised by
- Mr. Darcy, she saw on looking up that Colonel Fitzwilliam was
- meeting her. Putting away the letter immediately and forcing a
- smile, she said:
-
- “I did not know before that you ever walked this way.”
-
- “I have been making the tour of the park,” he replied, “as I
- generally do every year, and intend to close it with a call at
- the Parsonage. Are you going much farther?”
-
- “No, I should have turned in a moment.”
-
- And accordingly she did turn, and they walked towards the
- Parsonage together.
-
- “Do you certainly leave Kent on Saturday?” said she.
-
- “Yes—if Darcy does not put it off again. But I am at his
- disposal. He arranges the business just as he pleases.”
-
- “And if not able to please himself in the arrangement, he has at
- least pleasure in the great power of choice. I do not know
- anybody who seems more to enjoy the power of doing what he likes
- than Mr. Darcy.”
-
- “He likes to have his own way very well,” replied Colonel
- Fitzwilliam. “But so we all do. It is only that he has better
- means of having it than many others, because he is rich, and many
- others are poor. I speak feelingly. A younger son, you know, must
- be inured to self-denial and dependence.”
-
- “In my opinion, the younger son of an earl can know very little
- of either. Now seriously, what have you ever known of self-denial
- and dependence? When have you been prevented by want of money
- from going wherever you chose, or procuring anything you had a
- fancy for?”
-
- “These are home questions—and perhaps I cannot say that I have
- experienced many hardships of that nature. But in matters of
- greater weight, I may suffer from want of money. Younger sons
- cannot marry where they like.”
-
- “Unless where they like women of fortune, which I think they very
- often do.”
-
- “Our habits of expense make us too dependent, and there are not
- many in my rank of life who can afford to marry without some
- attention to money.”
-
- “Is this,” thought Elizabeth, “meant for me?” and she coloured at
- the idea; but, recovering herself, said in a lively tone, “And
- pray, what is the usual price of an earl’s younger son? Unless
- the elder brother is very sickly, I suppose you would not ask
- above fifty thousand pounds.”
-
- He answered her in the same style, and the subject dropped. To
- interrupt a silence which might make him fancy her affected with
- what had passed, she soon afterwards said:
-
- “I imagine your cousin brought you down with him chiefly for the
- sake of having someone at his disposal. I wonder he does not
- marry, to secure a lasting convenience of that kind. But,
- perhaps, his sister does as well for the present, and, as she is
- under his sole care, he may do what he likes with her.”
-
- “No,” said Colonel Fitzwilliam, “that is an advantage which he
- must divide with me. I am joined with him in the guardianship of
- Miss Darcy.”
-
- “Are you indeed? And pray what sort of guardians do you make?
- Does your charge give you much trouble? Young ladies of her age
- are sometimes a little difficult to manage, and if she has the
- true Darcy spirit, she may like to have her own way.”
-
- As she spoke she observed him looking at her earnestly; and the
- manner in which he immediately asked her why she supposed Miss
- Darcy likely to give them any uneasiness, convinced her that she
- had somehow or other got pretty near the truth. She directly
- replied:
-
- “You need not be frightened. I never heard any harm of her; and I
- dare say she is one of the most tractable creatures in the world.
- She is a very great favourite with some ladies of my
- acquaintance, Mrs. Hurst and Miss Bingley. I think I have heard
- you say that you know them.”
-
- “I know them a little. Their brother is a pleasant gentlemanlike
- man—he is a great friend of Darcy’s.”
-
- “Oh! yes,” said Elizabeth drily; “Mr. Darcy is uncommonly kind to
- Mr. Bingley, and takes a prodigious deal of care of him.”
-
- “Care of him! Yes, I really believe Darcy _does_ take care of him
- in those points where he most wants care. From something that he
- told me in our journey hither, I have reason to think Bingley
- very much indebted to him. But I ought to beg his pardon, for I
- have no right to suppose that Bingley was the person meant. It
- was all conjecture.”
-
- “What is it you mean?”
-
- “It is a circumstance which Darcy could not wish to be generally
- known, because if it were to get round to the lady’s family, it
- would be an unpleasant thing.”
-
- “You may depend upon my not mentioning it.”
-
- “And remember that I have not much reason for supposing it to be
- Bingley. What he told me was merely this: that he congratulated
- himself on having lately saved a friend from the inconveniences
- of a most imprudent marriage, but without mentioning names or any
- other particulars, and I only suspected it to be Bingley from
- believing him the kind of young man to get into a scrape of that
- sort, and from knowing them to have been together the whole of
- last summer.”
-
- “Did Mr. Darcy give you reasons for this interference?”
-
- “I understood that there were some very strong objections against
- the lady.”
-
- “And what arts did he use to separate them?”
-
- “He did not talk to me of his own arts,” said Fitzwilliam,
- smiling. “He only told me what I have now told you.”
-
- Elizabeth made no answer, and walked on, her heart swelling with
- indignation. After watching her a little, Fitzwilliam asked her
- why she was so thoughtful.
-
- “I am thinking of what you have been telling me,” said she. “Your
- cousin’s conduct does not suit my feelings. Why was he to be the
- judge?”
-
- “You are rather disposed to call his interference officious?”
-
- “I do not see what right Mr. Darcy had to decide on the propriety
- of his friend’s inclination, or why, upon his own judgement
- alone, he was to determine and direct in what manner his friend
- was to be happy. But,” she continued, recollecting herself, “as
- we know none of the particulars, it is not fair to condemn him.
- It is not to be supposed that there was much affection in the
- case.”
-
- “That is not an unnatural surmise,” said Fitzwilliam, “but it is
- a lessening of the honour of my cousin’s triumph very sadly.”
-
- This was spoken jestingly; but it appeared to her so just a
- picture of Mr. Darcy, that she would not trust herself with an
- answer, and therefore, abruptly changing the conversation talked
- on indifferent matters until they reached the Parsonage. There,
- shut into her own room, as soon as their visitor left them, she
- could think without interruption of all that she had heard. It
- was not to be supposed that any other people could be meant than
- those with whom she was connected. There could not exist in the
- world _two_ men over whom Mr. Darcy could have such boundless
- influence. That he had been concerned in the measures taken to
- separate Bingley and Jane she had never doubted; but she had
- always attributed to Miss Bingley the principal design and
- arrangement of them. If his own vanity, however, did not mislead
- him, _he_ was the cause, his pride and caprice were the cause, of
- all that Jane had suffered, and still continued to suffer. He had
- ruined for a while every hope of happiness for the most
- affectionate, generous heart in the world; and no one could say
- how lasting an evil he might have inflicted.
-
- “There were some very strong objections against the lady,” were
- Colonel Fitzwilliam’s words; and those strong objections probably
- were, her having one uncle who was a country attorney, and
- another who was in business in London.
-
- “To Jane herself,” she exclaimed, “there could be no possibility
- of objection; all loveliness and goodness as she is!—her
- understanding excellent, her mind improved, and her manners
- captivating. Neither could anything be urged against my father,
- who, though with some peculiarities, has abilities Mr. Darcy
- himself need not disdain, and respectability which he will
- probably never reach.” When she thought of her mother, her
- confidence gave way a little; but she would not allow that any
- objections _there_ had material weight with Mr. Darcy, whose
- pride, she was convinced, would receive a deeper wound from the
- want of importance in his friend’s connections, than from their
- want of sense; and she was quite decided, at last, that he had
- been partly governed by this worst kind of pride, and partly by
- the wish of retaining Mr. Bingley for his sister.
-
- The agitation and tears which the subject occasioned, brought on
- a headache; and it grew so much worse towards the evening, that,
- added to her unwillingness to see Mr. Darcy, it determined her
- not to attend her cousins to Rosings, where they were engaged to
- drink tea. Mrs. Collins, seeing that she was really unwell, did
- not press her to go and as much as possible prevented her husband
- from pressing her; but Mr. Collins could not conceal his
- apprehension of Lady Catherine’s being rather displeased by her
- staying at home.
-
-
-
-
- Chapter 34
-
- When they were gone, Elizabeth, as if intending to exasperate
- herself as much as possible against Mr. Darcy, chose for her
- employment the examination of all the letters which Jane had
- written to her since her being in Kent. They contained no actual
- complaint, nor was there any revival of past occurrences, or any
- communication of present suffering. But in all, and in almost
- every line of each, there was a want of that cheerfulness which
- had been used to characterise her style, and which, proceeding
- from the serenity of a mind at ease with itself and kindly
- disposed towards everyone, had been scarcely ever clouded.
- Elizabeth noticed every sentence conveying the idea of
- uneasiness, with an attention which it had hardly received on the
- first perusal. Mr. Darcy’s shameful boast of what misery he had
- been able to inflict, gave her a keener sense of her sister’s
- sufferings. It was some consolation to think that his visit to
- Rosings was to end on the day after the next—and, a still
- greater, that in less than a fortnight she should herself be with
- Jane again, and enabled to contribute to the recovery of her
- spirits, by all that affection could do.
-
- She could not think of Darcy’s leaving Kent without remembering
- that his cousin was to go with him; but Colonel Fitzwilliam had
- made it clear that he had no intentions at all, and agreeable as
- he was, she did not mean to be unhappy about him.
-
- While settling this point, she was suddenly roused by the sound
- of the door-bell, and her spirits were a little fluttered by the
- idea of its being Colonel Fitzwilliam himself, who had once
- before called late in the evening, and might now come to inquire
- particularly after her. But this idea was soon banished, and her
- spirits were very differently affected, when, to her utter
- amazement, she saw Mr. Darcy walk into the room. In an hurried
- manner he immediately began an inquiry after her health, imputing
- his visit to a wish of hearing that she were better. She answered
- him with cold civility. He sat down for a few moments, and then
- getting up, walked about the room. Elizabeth was surprised, but
- said not a word. After a silence of several minutes, he came
- towards her in an agitated manner, and thus began:
-
- “In vain I have struggled. It will not do. My feelings will not
- be repressed. You must allow me to tell you how ardently I admire
- and love you.”
-
- Elizabeth’s astonishment was beyond expression. She stared,
- coloured, doubted, and was silent. This he considered sufficient
- encouragement; and the avowal of all that he felt, and had long
- felt for her, immediately followed. He spoke well; but there were
- feelings besides those of the heart to be detailed; and he was
- not more eloquent on the subject of tenderness than of pride. His
- sense of her inferiority—of its being a degradation—of the family
- obstacles which had always opposed to inclination, were dwelt on
- with a warmth which seemed due to the consequence he was
- wounding, but was very unlikely to recommend his suit.
-
- In spite of her deeply-rooted dislike, she could not be
- insensible to the compliment of such a man’s affection, and
- though her intentions did not vary for an instant, she was at
- first sorry for the pain he was to receive; till, roused to
- resentment by his subsequent language, she lost all compassion in
- anger. She tried, however, to compose herself to answer him with
- patience, when he should have done. He concluded with
- representing to her the strength of that attachment which, in
- spite of all his endeavours, he had found impossible to conquer;
- and with expressing his hope that it would now be rewarded by her
- acceptance of his hand. As he said this, she could easily see
- that he had no doubt of a favourable answer. He _spoke_ of
- apprehension and anxiety, but his countenance expressed real
- security. Such a circumstance could only exasperate farther, and,
- when he ceased, the colour rose into her cheeks, and she said:
-
- “In such cases as this, it is, I believe, the established mode to
- express a sense of obligation for the sentiments avowed, however
- unequally they may be returned. It is natural that obligation
- should be felt, and if I could _feel_ gratitude, I would now
- thank you. But I cannot—I have never desired your good opinion,
- and you have certainly bestowed it most unwillingly. I am sorry
- to have occasioned pain to anyone. It has been most unconsciously
- done, however, and I hope will be of short duration. The feelings
- which, you tell me, have long prevented the acknowledgment of
- your regard, can have little difficulty in overcoming it after
- this explanation.”
-
- Mr. Darcy, who was leaning against the mantelpiece with his eyes
- fixed on her face, seemed to catch her words with no less
- resentment than surprise. His complexion became pale with anger,
- and the disturbance of his mind was visible in every feature. He
- was struggling for the appearance of composure, and would not
- open his lips till he believed himself to have attained it. The
- pause was to Elizabeth’s feelings dreadful. At length, with a
- voice of forced calmness, he said:
-
- “And this is all the reply which I am to have the honour of
- expecting! I might, perhaps, wish to be informed why, with so
- little _endeavour_ at civility, I am thus rejected. But it is of
- small importance.”
-
- “I might as well inquire,” replied she, “why with so evident a
- desire of offending and insulting me, you chose to tell me that
- you liked me against your will, against your reason, and even
- against your character? Was not this some excuse for incivility,
- if I _was_ uncivil? But I have other provocations. You know I
- have. Had not my feelings decided against you—had they been
- indifferent, or had they even been favourable, do you think that
- any consideration would tempt me to accept the man who has been
- the means of ruining, perhaps for ever, the happiness of a most
- beloved sister?”
-
- As she pronounced these words, Mr. Darcy changed colour; but the
- emotion was short, and he listened without attempting to
- interrupt her while she continued:
-
- “I have every reason in the world to think ill of you. No motive
- can excuse the unjust and ungenerous part you acted _there_. You
- dare not, you cannot deny, that you have been the principal, if
- not the only means of dividing them from each other—of exposing
- one to the censure of the world for caprice and instability, and
- the other to its derision for disappointed hopes, and involving
- them both in misery of the acutest kind.”
-
- She paused, and saw with no slight indignation that he was
- listening with an air which proved him wholly unmoved by any
- feeling of remorse. He even looked at her with a smile of
- affected incredulity.
-
- “Can you deny that you have done it?” she repeated.
-
- With assumed tranquillity he then replied: “I have no wish of
- denying that I did everything in my power to separate my friend
- from your sister, or that I rejoice in my success. Towards _him_
- I have been kinder than towards myself.”
-
- Elizabeth disdained the appearance of noticing this civil
- reflection, but its meaning did not escape, nor was it likely to
- conciliate her.
-
- “But it is not merely this affair,” she continued, “on which my
- dislike is founded. Long before it had taken place my opinion of
- you was decided. Your character was unfolded in the recital which
- I received many months ago from Mr. Wickham. On this subject,
- what can you have to say? In what imaginary act of friendship can
- you here defend yourself? or under what misrepresentation can you
- here impose upon others?”
-
- “You take an eager interest in that gentleman’s concerns,” said
- Darcy, in a less tranquil tone, and with a heightened colour.
-
- “Who that knows what his misfortunes have been, can help feeling
- an interest in him?”
-
- “His misfortunes!” repeated Darcy contemptuously; “yes, his
- misfortunes have been great indeed.”
-
- “And of your infliction,” cried Elizabeth with energy. “You have
- reduced him to his present state of poverty—comparative poverty.
- You have withheld the advantages which you must know to have been
- designed for him. You have deprived the best years of his life of
- that independence which was no less his due than his desert. You
- have done all this! and yet you can treat the mention of his
- misfortune with contempt and ridicule.”
-
- “And this,” cried Darcy, as he walked with quick steps across the
- room, “is your opinion of me! This is the estimation in which you
- hold me! I thank you for explaining it so fully. My faults,
- according to this calculation, are heavy indeed! But perhaps,”
- added he, stopping in his walk, and turning towards her, “these
- offenses might have been overlooked, had not your pride been hurt
- by my honest confession of the scruples that had long prevented
- my forming any serious design. These bitter accusations might
- have been suppressed, had I, with greater policy, concealed my
- struggles, and flattered you into the belief of my being impelled
- by unqualified, unalloyed inclination; by reason, by reflection,
- by everything. But disguise of every sort is my abhorrence. Nor
- am I ashamed of the feelings I related. They were natural and
- just. Could you expect me to rejoice in the inferiority of your
- connections?—to congratulate myself on the hope of relations,
- whose condition in life is so decidedly beneath my own?”
-
- Elizabeth felt herself growing more angry every moment; yet she
- tried to the utmost to speak with composure when she said:
-
- “You are mistaken, Mr. Darcy, if you suppose that the mode of
- your declaration affected me in any other way, than as it spared
- me the concern which I might have felt in refusing you, had you
- behaved in a more gentlemanlike manner.”
-
- She saw him start at this, but he said nothing, and she
- continued:
-
- “You could not have made the offer of your hand in any possible
- way that would have tempted me to accept it.”
-
- Again his astonishment was obvious; and he looked at her with an
- expression of mingled incredulity and mortification. She went on:
-
- “From the very beginning—from the first moment, I may almost
- say—of my acquaintance with you, your manners, impressing me with
- the fullest belief of your arrogance, your conceit, and your
- selfish disdain of the feelings of others, were such as to form
- the groundwork of disapprobation on which succeeding events have
- built so immovable a dislike; and I had not known you a month
- before I felt that you were the last man in the world whom I
- could ever be prevailed on to marry.”
-
- “You have said quite enough, madam. I perfectly comprehend your
- feelings, and have now only to be ashamed of what my own have
- been. Forgive me for having taken up so much of your time, and
- accept my best wishes for your health and happiness.”
-
- And with these words he hastily left the room, and Elizabeth
- heard him the next moment open the front door and quit the house.
-
- The tumult of her mind, was now painfully great. She knew not how
- to support herself, and from actual weakness sat down and cried
- for half-an-hour. Her astonishment, as she reflected on what had
- passed, was increased by every review of it. That she should
- receive an offer of marriage from Mr. Darcy! That he should have
- been in love with her for so many months! So much in love as to
- wish to marry her in spite of all the objections which had made
- him prevent his friend’s marrying her sister, and which must
- appear at least with equal force in his own case—was almost
- incredible! It was gratifying to have inspired unconsciously so
- strong an affection. But his pride, his abominable pride—his
- shameless avowal of what he had done with respect to Jane—his
- unpardonable assurance in acknowledging, though he could not
- justify it, and the unfeeling manner in which he had mentioned
- Mr. Wickham, his cruelty towards whom he had not attempted to
- deny, soon overcame the pity which the consideration of his
- attachment had for a moment excited. She continued in very
- agitated reflections till the sound of Lady Catherine’s carriage
- made her feel how unequal she was to encounter Charlotte’s
- observation, and hurried her away to her room.
-
-
-
-
- Chapter 35
-
- Elizabeth awoke the next morning to the same thoughts and
- meditations which had at length closed her eyes. She could not
- yet recover from the surprise of what had happened; it was
- impossible to think of anything else; and, totally indisposed for
- employment, she resolved, soon after breakfast, to indulge
- herself in air and exercise. She was proceeding directly to her
- favourite walk, when the recollection of Mr. Darcy’s sometimes
- coming there stopped her, and instead of entering the park, she
- turned up the lane, which led farther from the turnpike-road. The
- park paling was still the boundary on one side, and she soon
- passed one of the gates into the ground.
-
- After walking two or three times along that part of the lane, she
- was tempted, by the pleasantness of the morning, to stop at the
- gates and look into the park. The five weeks which she had now
- passed in Kent had made a great difference in the country, and
- every day was adding to the verdure of the early trees. She was
- on the point of continuing her walk, when she caught a glimpse of
- a gentleman within the sort of grove which edged the park; he was
- moving that way; and, fearful of its being Mr. Darcy, she was
- directly retreating. But the person who advanced was now near
- enough to see her, and stepping forward with eagerness,
- pronounced her name. She had turned away; but on hearing herself
- called, though in a voice which proved it to be Mr. Darcy, she
- moved again towards the gate. He had by that time reached it
- also, and, holding out a letter, which she instinctively took,
- said, with a look of haughty composure, “I have been walking in
- the grove some time in the hope of meeting you. Will you do me
- the honour of reading that letter?” And then, with a slight bow,
- turned again into the plantation, and was soon out of sight.
-
- With no expectation of pleasure, but with the strongest
- curiosity, Elizabeth opened the letter, and, to her still
- increasing wonder, perceived an envelope containing two sheets of
- letter-paper, written quite through, in a very close hand. The
- envelope itself was likewise full. Pursuing her way along the
- lane, she then began it. It was dated from Rosings, at eight
- o’clock in the morning, and was as follows:—
-
- “Be not alarmed, madam, on receiving this letter, by the
- apprehension of its containing any repetition of those sentiments
- or renewal of those offers which were last night so disgusting to
- you. I write without any intention of paining you, or humbling
- myself, by dwelling on wishes which, for the happiness of both,
- cannot be too soon forgotten; and the effort which the formation
- and the perusal of this letter must occasion, should have been
- spared, had not my character required it to be written and read.
- You must, therefore, pardon the freedom with which I demand your
- attention; your feelings, I know, will bestow it unwillingly, but
- I demand it of your justice.
-
- “Two offenses of a very different nature, and by no means of
- equal magnitude, you last night laid to my charge. The first
- mentioned was, that, regardless of the sentiments of either, I
- had detached Mr. Bingley from your sister, and the other, that I
- had, in defiance of various claims, in defiance of honour and
- humanity, ruined the immediate prosperity and blasted the
- prospects of Mr. Wickham. Wilfully and wantonly to have thrown
- off the companion of my youth, the acknowledged favourite of my
- father, a young man who had scarcely any other dependence than on
- our patronage, and who had been brought up to expect its
- exertion, would be a depravity, to which the separation of two
- young persons, whose affection could be the growth of only a few
- weeks, could bear no comparison. But from the severity of that
- blame which was last night so liberally bestowed, respecting each
- circumstance, I shall hope to be in the future secured, when the
- following account of my actions and their motives has been read.
- If, in the explanation of them, which is due to myself, I am
- under the necessity of relating feelings which may be offensive
- to yours, I can only say that I am sorry. The necessity must be
- obeyed, and further apology would be absurd.
-
- “I had not been long in Hertfordshire, before I saw, in common
- with others, that Bingley preferred your elder sister to any
- other young woman in the country. But it was not till the evening
- of the dance at Netherfield that I had any apprehension of his
- feeling a serious attachment. I had often seen him in love
- before. At that ball, while I had the honour of dancing with you,
- I was first made acquainted, by Sir William Lucas’s accidental
- information, that Bingley’s attentions to your sister had given
- rise to a general expectation of their marriage. He spoke of it
- as a certain event, of which the time alone could be undecided.
- From that moment I observed my friend’s behaviour attentively;
- and I could then perceive that his partiality for Miss Bennet was
- beyond what I had ever witnessed in him. Your sister I also
- watched. Her look and manners were open, cheerful, and engaging
- as ever, but without any symptom of peculiar regard, and I
- remained convinced from the evening’s scrutiny, that though she
- received his attentions with pleasure, she did not invite them by
- any participation of sentiment. If _you_ have not been mistaken
- here, _I_ must have been in error. Your superior knowledge of
- your sister must make the latter probable. If it be so, if I have
- been misled by such error to inflict pain on her, your resentment
- has not been unreasonable. But I shall not scruple to assert,
- that the serenity of your sister’s countenance and air was such
- as might have given the most acute observer a conviction that,
- however amiable her temper, her heart was not likely to be easily
- touched. That I was desirous of believing her indifferent is
- certain—but I will venture to say that my investigation and
- decisions are not usually influenced by my hopes or fears. I did
- not believe her to be indifferent because I wished it; I believed
- it on impartial conviction, as truly as I wished it in reason. My
- objections to the marriage were not merely those which I last
- night acknowledged to have the utmost force of passion to put
- aside, in my own case; the want of connection could not be so
- great an evil to my friend as to me. But there were other causes
- of repugnance; causes which, though still existing, and existing
- to an equal degree in both instances, I had myself endeavoured to
- forget, because they were not immediately before me. These causes
- must be stated, though briefly. The situation of your mother’s
- family, though objectionable, was nothing in comparison to that
- total want of propriety so frequently, so almost uniformly
- betrayed by herself, by your three younger sisters, and
- occasionally even by your father. Pardon me. It pains me to
- offend you. But amidst your concern for the defects of your
- nearest relations, and your displeasure at this representation of
- them, let it give you consolation to consider that, to have
- conducted yourselves so as to avoid any share of the like
- censure, is praise no less generally bestowed on you and your
- elder sister, than it is honourable to the sense and disposition
- of both. I will only say farther that from what passed that
- evening, my opinion of all parties was confirmed, and every
- inducement heightened which could have led me before, to preserve
- my friend from what I esteemed a most unhappy connection. He left
- Netherfield for London, on the day following, as you, I am
- certain, remember, with the design of soon returning.
-
- “The part which I acted is now to be explained. His sisters’
- uneasiness had been equally excited with my own; our coincidence
- of feeling was soon discovered, and, alike sensible that no time
- was to be lost in detaching their brother, we shortly resolved on
- joining him directly in London. We accordingly went—and there I
- readily engaged in the office of pointing out to my friend the
- certain evils of such a choice. I described, and enforced them
- earnestly. But, however this remonstrance might have staggered or
- delayed his determination, I do not suppose that it would
- ultimately have prevented the marriage, had it not been seconded
- by the assurance that I hesitated not in giving, of your sister’s
- indifference. He had before believed her to return his affection
- with sincere, if not with equal regard. But Bingley has great
- natural modesty, with a stronger dependence on my judgement than
- on his own. To convince him, therefore, that he had deceived
- himself, was no very difficult point. To persuade him against
- returning into Hertfordshire, when that conviction had been
- given, was scarcely the work of a moment. I cannot blame myself
- for having done thus much. There is but one part of my conduct in
- the whole affair on which I do not reflect with satisfaction; it
- is that I condescended to adopt the measures of art so far as to
- conceal from him your sister’s being in town. I knew it myself,
- as it was known to Miss Bingley; but her brother is even yet
- ignorant of it. That they might have met without ill consequence
- is perhaps probable; but his regard did not appear to me enough
- extinguished for him to see her without some danger. Perhaps this
- concealment, this disguise was beneath me; it is done, however,
- and it was done for the best. On this subject I have nothing more
- to say, no other apology to offer. If I have wounded your
- sister’s feelings, it was unknowingly done and though the motives
- which governed me may to you very naturally appear insufficient,
- I have not yet learnt to condemn them.
-
- “With respect to that other, more weighty accusation, of having
- injured Mr. Wickham, I can only refute it by laying before you
- the whole of his connection with my family. Of what he has
- _particularly_ accused me I am ignorant; but of the truth of what
- I shall relate, I can summon more than one witness of undoubted
- veracity.
-
- “Mr. Wickham is the son of a very respectable man, who had for
- many years the management of all the Pemberley estates, and whose
- good conduct in the discharge of his trust naturally inclined my
- father to be of service to him; and on George Wickham, who was
- his godson, his kindness was therefore liberally bestowed. My
- father supported him at school, and afterwards at Cambridge—most
- important assistance, as his own father, always poor from the
- extravagance of his wife, would have been unable to give him a
- gentleman’s education. My father was not only fond of this young
- man’s society, whose manners were always engaging; he had also
- the highest opinion of him, and hoping the church would be his
- profession, intended to provide for him in it. As for myself, it
- is many, many years since I first began to think of him in a very
- different manner. The vicious propensities—the want of principle,
- which he was careful to guard from the knowledge of his best
- friend, could not escape the observation of a young man of nearly
- the same age with himself, and who had opportunities of seeing
- him in unguarded moments, which Mr. Darcy could not have. Here
- again I shall give you pain—to what degree you only can tell. But
- whatever may be the sentiments which Mr. Wickham has created, a
- suspicion of their nature shall not prevent me from unfolding his
- real character—it adds even another motive.
-
- “My excellent father died about five years ago; and his
- attachment to Mr. Wickham was to the last so steady, that in his
- will he particularly recommended it to me, to promote his
- advancement in the best manner that his profession might
- allow—and if he took orders, desired that a valuable family
- living might be his as soon as it became vacant. There was also a
- legacy of one thousand pounds. His own father did not long
- survive mine, and within half a year from these events, Mr.
- Wickham wrote to inform me that, having finally resolved against
- taking orders, he hoped I should not think it unreasonable for
- him to expect some more immediate pecuniary advantage, in lieu of
- the preferment, by which he could not be benefited. He had some
- intention, he added, of studying law, and I must be aware that
- the interest of one thousand pounds would be a very insufficient
- support therein. I rather wished, than believed him to be
- sincere; but, at any rate, was perfectly ready to accede to his
- proposal. I knew that Mr. Wickham ought not to be a clergyman;
- the business was therefore soon settled—he resigned all claim to
- assistance in the church, were it possible that he could ever be
- in a situation to receive it, and accepted in return three
- thousand pounds. All connection between us seemed now dissolved.
- I thought too ill of him to invite him to Pemberley, or admit his
- society in town. In town I believe he chiefly lived, but his
- studying the law was a mere pretence, and being now free from all
- restraint, his life was a life of idleness and dissipation. For
- about three years I heard little of him; but on the decease of
- the incumbent of the living which had been designed for him, he
- applied to me again by letter for the presentation. His
- circumstances, he assured me, and I had no difficulty in
- believing it, were exceedingly bad. He had found the law a most
- unprofitable study, and was now absolutely resolved on being
- ordained, if I would present him to the living in question—of
- which he trusted there could be little doubt, as he was well
- assured that I had no other person to provide for, and I could
- not have forgotten my revered father’s intentions. You will
- hardly blame me for refusing to comply with this entreaty, or for
- resisting every repetition to it. His resentment was in
- proportion to the distress of his circumstances—and he was
- doubtless as violent in his abuse of me to others as in his
- reproaches to myself. After this period every appearance of
- acquaintance was dropped. How he lived I know not. But last
- summer he was again most painfully obtruded on my notice.
-
- “I must now mention a circumstance which I would wish to forget
- myself, and which no obligation less than the present should
- induce me to unfold to any human being. Having said thus much, I
- feel no doubt of your secrecy. My sister, who is more than ten
- years my junior, was left to the guardianship of my mother’s
- nephew, Colonel Fitzwilliam, and myself. About a year ago, she
- was taken from school, and an establishment formed for her in
- London; and last summer she went with the lady who presided over
- it, to Ramsgate; and thither also went Mr. Wickham, undoubtedly
- by design; for there proved to have been a prior acquaintance
- between him and Mrs. Younge, in whose character we were most
- unhappily deceived; and by her connivance and aid, he so far
- recommended himself to Georgiana, whose affectionate heart
- retained a strong impression of his kindness to her as a child,
- that she was persuaded to believe herself in love, and to consent
- to an elopement. She was then but fifteen, which must be her
- excuse; and after stating her imprudence, I am happy to add, that
- I owed the knowledge of it to herself. I joined them unexpectedly
- a day or two before the intended elopement, and then Georgiana,
- unable to support the idea of grieving and offending a brother
- whom she almost looked up to as a father, acknowledged the whole
- to me. You may imagine what I felt and how I acted. Regard for my
- sister’s credit and feelings prevented any public exposure; but I
- wrote to Mr. Wickham, who left the place immediately, and Mrs.
- Younge was of course removed from her charge. Mr. Wickham’s chief
- object was unquestionably my sister’s fortune, which is thirty
- thousand pounds; but I cannot help supposing that the hope of
- revenging himself on me was a strong inducement. His revenge
- would have been complete indeed.
-
- “This, madam, is a faithful narrative of every event in which we
- have been concerned together; and if you do not absolutely reject
- it as false, you will, I hope, acquit me henceforth of cruelty
- towards Mr. Wickham. I know not in what manner, under what form
- of falsehood he had imposed on you; but his success is not
- perhaps to be wondered at. Ignorant as you previously were of
- everything concerning either, detection could not be in your
- power, and suspicion certainly not in your inclination.
-
- “You may possibly wonder why all this was not told you last
- night; but I was not then master enough of myself to know what
- could or ought to be revealed. For the truth of everything here
- related, I can appeal more particularly to the testimony of
- Colonel Fitzwilliam, who, from our near relationship and constant
- intimacy, and, still more, as one of the executors of my father’s
- will, has been unavoidably acquainted with every particular of
- these transactions. If your abhorrence of _me_ should make _my_
- assertions valueless, you cannot be prevented by the same cause
- from confiding in my cousin; and that there may be the
- possibility of consulting him, I shall endeavour to find some
- opportunity of putting this letter in your hands in the course of
- the morning. I will only add, God bless you.
-
- “FITZWILLIAM DARCY”
-
-
-
-
- Chapter 36
-
- If Elizabeth, when Mr. Darcy gave her the letter, did not expect
- it to contain a renewal of his offers, she had formed no
- expectation at all of its contents. But such as they were, it may
- well be supposed how eagerly she went through them, and what a
- contrariety of emotion they excited. Her feelings as she read
- were scarcely to be defined. With amazement did she first
- understand that he believed any apology to be in his power; and
- steadfastly was she persuaded, that he could have no explanation
- to give, which a just sense of shame would not conceal. With a
- strong prejudice against everything he might say, she began his
- account of what had happened at Netherfield. She read with an
- eagerness which hardly left her power of comprehension, and from
- impatience of knowing what the next sentence might bring, was
- incapable of attending to the sense of the one before her eyes.
- His belief of her sister’s insensibility she instantly resolved
- to be false; and his account of the real, the worst objections to
- the match, made her too angry to have any wish of doing him
- justice. He expressed no regret for what he had done which
- satisfied her; his style was not penitent, but haughty. It was
- all pride and insolence.
-
- But when this subject was succeeded by his account of Mr.
- Wickham—when she read with somewhat clearer attention a relation
- of events which, if true, must overthrow every cherished opinion
- of his worth, and which bore so alarming an affinity to his own
- history of himself—her feelings were yet more acutely painful and
- more difficult of definition. Astonishment, apprehension, and
- even horror, oppressed her. She wished to discredit it entirely,
- repeatedly exclaiming, “This must be false! This cannot be! This
- must be the grossest falsehood!”—and when she had gone through
- the whole letter, though scarcely knowing anything of the last
- page or two, put it hastily away, protesting that she would not
- regard it, that she would never look in it again.
-
- In this perturbed state of mind, with thoughts that could rest on
- nothing, she walked on; but it would not do; in half a minute the
- letter was unfolded again, and collecting herself as well as she
- could, she again began the mortifying perusal of all that related
- to Wickham, and commanded herself so far as to examine the
- meaning of every sentence. The account of his connection with the
- Pemberley family was exactly what he had related himself; and the
- kindness of the late Mr. Darcy, though she had not before known
- its extent, agreed equally well with his own words. So far each
- recital confirmed the other; but when she came to the will, the
- difference was great. What Wickham had said of the living was
- fresh in her memory, and as she recalled his very words, it was
- impossible not to feel that there was gross duplicity on one side
- or the other; and, for a few moments, she flattered herself that
- her wishes did not err. But when she read and re-read with the
- closest attention, the particulars immediately following of
- Wickham’s resigning all pretensions to the living, of his
- receiving in lieu so considerable a sum as three thousand pounds,
- again was she forced to hesitate. She put down the letter,
- weighed every circumstance with what she meant to be
- impartiality—deliberated on the probability of each statement—but
- with little success. On both sides it was only assertion. Again
- she read on; but every line proved more clearly that the affair,
- which she had believed it impossible that any contrivance could
- so represent as to render Mr. Darcy’s conduct in it less than
- infamous, was capable of a turn which must make him entirely
- blameless throughout the whole.
-
- The extravagance and general profligacy which he scrupled not to
- lay at Mr. Wickham’s charge, exceedingly shocked her; the more
- so, as she could bring no proof of its injustice. She had never
- heard of him before his entrance into the ——shire Militia, in
- which he had engaged at the persuasion of the young man who, on
- meeting him accidentally in town, had there renewed a slight
- acquaintance. Of his former way of life nothing had been known in
- Hertfordshire but what he told himself. As to his real character,
- had information been in her power, she had never felt a wish of
- inquiring. His countenance, voice, and manner had established him
- at once in the possession of every virtue. She tried to recollect
- some instance of goodness, some distinguished trait of integrity
- or benevolence, that might rescue him from the attacks of Mr.
- Darcy; or at least, by the predominance of virtue, atone for
- those casual errors under which she would endeavour to class what
- Mr. Darcy had described as the idleness and vice of many years’
- continuance. But no such recollection befriended her. She could
- see him instantly before her, in every charm of air and address;
- but she could remember no more substantial good than the general
- approbation of the neighbourhood, and the regard which his social
- powers had gained him in the mess. After pausing on this point a
- considerable while, she once more continued to read. But, alas!
- the story which followed, of his designs on Miss Darcy, received
- some confirmation from what had passed between Colonel
- Fitzwilliam and herself only the morning before; and at last she
- was referred for the truth of every particular to Colonel
- Fitzwilliam himself—from whom she had previously received the
- information of his near concern in all his cousin’s affairs, and
- whose character she had no reason to question. At one time she
- had almost resolved on applying to him, but the idea was checked
- by the awkwardness of the application, and at length wholly
- banished by the conviction that Mr. Darcy would never have
- hazarded such a proposal, if he had not been well assured of his
- cousin’s corroboration.
-
- She perfectly remembered everything that had passed in
- conversation between Wickham and herself, in their first evening
- at Mr. Phillips’s. Many of his expressions were still fresh in
- her memory. She was _now_ struck with the impropriety of such
- communications to a stranger, and wondered it had escaped her
- before. She saw the indelicacy of putting himself forward as he
- had done, and the inconsistency of his professions with his
- conduct. She remembered that he had boasted of having no fear of
- seeing Mr. Darcy—that Mr. Darcy might leave the country, but that
- _he_ should stand his ground; yet he had avoided the Netherfield
- ball the very next week. She remembered also that, till the
- Netherfield family had quitted the country, he had told his story
- to no one but herself; but that after their removal it had been
- everywhere discussed; that he had then no reserves, no scruples
- in sinking Mr. Darcy’s character, though he had assured her that
- respect for the father would always prevent his exposing the son.
-
- How differently did everything now appear in which he was
- concerned! His attentions to Miss King were now the consequence
- of views solely and hatefully mercenary; and the mediocrity of
- her fortune proved no longer the moderation of his wishes, but
- his eagerness to grasp at anything. His behaviour to herself
- could now have had no tolerable motive; he had either been
- deceived with regard to her fortune, or had been gratifying his
- vanity by encouraging the preference which she believed she had
- most incautiously shown. Every lingering struggle in his favour
- grew fainter and fainter; and in farther justification of Mr.
- Darcy, she could not but allow that Mr. Bingley, when questioned
- by Jane, had long ago asserted his blamelessness in the affair;
- that proud and repulsive as were his manners, she had never, in
- the whole course of their acquaintance—an acquaintance which had
- latterly brought them much together, and given her a sort of
- intimacy with his ways—seen anything that betrayed him to be
- unprincipled or unjust—anything that spoke him of irreligious or
- immoral habits; that among his own connections he was esteemed
- and valued—that even Wickham had allowed him merit as a brother,
- and that she had often heard him speak so affectionately of his
- sister as to prove him capable of some amiable feeling; that had
- his actions been what Mr. Wickham represented them, so gross a
- violation of everything right could hardly have been concealed
- from the world; and that friendship between a person capable of
- it, and such an amiable man as Mr. Bingley, was incomprehensible.
-
- She grew absolutely ashamed of herself. Of neither Darcy nor
- Wickham could she think without feeling she had been blind,
- partial, prejudiced, absurd.
-
- “How despicably I have acted!” she cried; “I, who have prided
- myself on my discernment! I, who have valued myself on my
- abilities! who have often disdained the generous candour of my
- sister, and gratified my vanity in useless or blameable mistrust!
- How humiliating is this discovery! Yet, how just a humiliation!
- Had I been in love, I could not have been more wretchedly blind!
- But vanity, not love, has been my folly. Pleased with the
- preference of one, and offended by the neglect of the other, on
- the very beginning of our acquaintance, I have courted
- prepossession and ignorance, and driven reason away, where either
- were concerned. Till this moment I never knew myself.”
-
- From herself to Jane—from Jane to Bingley, her thoughts were in a
- line which soon brought to her recollection that Mr. Darcy’s
- explanation _there_ had appeared very insufficient, and she read
- it again. Widely different was the effect of a second perusal.
- How could she deny that credit to his assertions in one instance,
- which she had been obliged to give in the other? He declared
- himself to be totally unsuspicious of her sister’s attachment;
- and she could not help remembering what Charlotte’s opinion had
- always been. Neither could she deny the justice of his
- description of Jane. She felt that Jane’s feelings, though
- fervent, were little displayed, and that there was a constant
- complacency in her air and manner not often united with great
- sensibility.
-
- When she came to that part of the letter in which her family were
- mentioned in terms of such mortifying, yet merited reproach, her
- sense of shame was severe. The justice of the charge struck her
- too forcibly for denial, and the circumstances to which he
- particularly alluded as having passed at the Netherfield ball,
- and as confirming all his first disapprobation, could not have
- made a stronger impression on his mind than on hers.
-
- The compliment to herself and her sister was not unfelt. It
- soothed, but it could not console her for the contempt which had
- thus been self-attracted by the rest of her family; and as she
- considered that Jane’s disappointment had in fact been the work
- of her nearest relations, and reflected how materially the credit
- of both must be hurt by such impropriety of conduct, she felt
- depressed beyond anything she had ever known before.
-
- After wandering along the lane for two hours, giving way to every
- variety of thought—re-considering events, determining
- probabilities, and reconciling herself, as well as she could, to
- a change so sudden and so important, fatigue, and a recollection
- of her long absence, made her at length return home; and she
- entered the house with the wish of appearing cheerful as usual,
- and the resolution of repressing such reflections as must make
- her unfit for conversation.
-
- She was immediately told that the two gentlemen from Rosings had
- each called during her absence; Mr. Darcy, only for a few
- minutes, to take leave—but that Colonel Fitzwilliam had been
- sitting with them at least an hour, hoping for her return, and
- almost resolving to walk after her till she could be found.
- Elizabeth could but just _affect_ concern in missing him; she
- really rejoiced at it. Colonel Fitzwilliam was no longer an
- object; she could think only of her letter.
-
-
-
-
- Chapter 37
-
- The two gentlemen left Rosings the next morning, and Mr. Collins
- having been in waiting near the lodges, to make them his parting
- obeisance, was able to bring home the pleasing intelligence, of
- their appearing in very good health, and in as tolerable spirits
- as could be expected, after the melancholy scene so lately gone
- through at Rosings. To Rosings he then hastened, to console Lady
- Catherine and her daughter; and on his return brought back, with
- great satisfaction, a message from her ladyship, importing that
- she felt herself so dull as to make her very desirous of having
- them all to dine with her.
-
- Elizabeth could not see Lady Catherine without recollecting that,
- had she chosen it, she might by this time have been presented to
- her as her future niece; nor could she think, without a smile, of
- what her ladyship’s indignation would have been. “What would she
- have said? how would she have behaved?” were questions with which
- she amused herself.
-
- Their first subject was the diminution of the Rosings party. “I
- assure you, I feel it exceedingly,” said Lady Catherine; “I
- believe no one feels the loss of friends so much as I do. But I
- am particularly attached to these young men, and know them to be
- so much attached to me! They were excessively sorry to go! But so
- they always are. The dear Colonel rallied his spirits tolerably
- till just at last; but Darcy seemed to feel it most acutely,
- more, I think, than last year. His attachment to Rosings
- certainly increases.”
-
- Mr. Collins had a compliment, and an allusion to throw in here,
- which were kindly smiled on by the mother and daughter.
-
- Lady Catherine observed, after dinner, that Miss Bennet seemed
- out of spirits, and immediately accounting for it by herself, by
- supposing that she did not like to go home again so soon, she
- added:
-
- “But if that is the case, you must write to your mother and beg
- that you may stay a little longer. Mrs. Collins will be very glad
- of your company, I am sure.”
-
- “I am much obliged to your ladyship for your kind invitation,”
- replied Elizabeth, “but it is not in my power to accept it. I
- must be in town next Saturday.”
-
- “Why, at that rate, you will have been here only six weeks. I
- expected you to stay two months. I told Mrs. Collins so before
- you came. There can be no occasion for your going so soon. Mrs.
- Bennet could certainly spare you for another fortnight.”
-
- “But my father cannot. He wrote last week to hurry my return.”
-
- “Oh! your father of course may spare you, if your mother can.
- Daughters are never of so much consequence to a father. And if
- you will stay another _month_ complete, it will be in my power to
- take one of you as far as London, for I am going there early in
- June, for a week; and as Dawson does not object to the
- barouche-box, there will be very good room for one of you—and
- indeed, if the weather should happen to be cool, I should not
- object to taking you both, as you are neither of you large.”
-
- “You are all kindness, madam; but I believe we must abide by our
- original plan.”
-
- Lady Catherine seemed resigned. “Mrs. Collins, you must send a
- servant with them. You know I always speak my mind, and I cannot
- bear the idea of two young women travelling post by themselves.
- It is highly improper. You must contrive to send somebody. I have
- the greatest dislike in the world to that sort of thing. Young
- women should always be properly guarded and attended, according
- to their situation in life. When my niece Georgiana went to
- Ramsgate last summer, I made a point of her having two
- men-servants go with her. Miss Darcy, the daughter of Mr. Darcy,
- of Pemberley, and Lady Anne, could not have appeared with
- propriety in a different manner. I am excessively attentive to
- all those things. You must send John with the young ladies, Mrs.
- Collins. I am glad it occurred to me to mention it; for it would
- really be discreditable to _you_ to let them go alone.”
-
- “My uncle is to send a servant for us.”
-
- “Oh! Your uncle! He keeps a man-servant, does he? I am very glad
- you have somebody who thinks of these things. Where shall you
- change horses? Oh! Bromley, of course. If you mention my name at
- the Bell, you will be attended to.”
-
- Lady Catherine had many other questions to ask respecting their
- journey, and as she did not answer them all herself, attention
- was necessary, which Elizabeth believed to be lucky for her; or,
- with a mind so occupied, she might have forgotten where she was.
- Reflection must be reserved for solitary hours; whenever she was
- alone, she gave way to it as the greatest relief; and not a day
- went by without a solitary walk, in which she might indulge in
- all the delight of unpleasant recollections.
-
- Mr. Darcy’s letter she was in a fair way of soon knowing by
- heart. She studied every sentence; and her feelings towards its
- writer were at times widely different. When she remembered the
- style of his address, she was still full of indignation; but when
- she considered how unjustly she had condemned and upbraided him,
- her anger was turned against herself; and his disappointed
- feelings became the object of compassion. His attachment excited
- gratitude, his general character respect; but she could not
- approve him; nor could she for a moment repent her refusal, or
- feel the slightest inclination ever to see him again. In her own
- past behaviour, there was a constant source of vexation and
- regret; and in the unhappy defects of her family, a subject of
- yet heavier chagrin. They were hopeless of remedy. Her father,
- contented with laughing at them, would never exert himself to
- restrain the wild giddiness of his youngest daughters; and her
- mother, with manners so far from right herself, was entirely
- insensible of the evil. Elizabeth had frequently united with Jane
- in an endeavour to check the imprudence of Catherine and Lydia;
- but while they were supported by their mother’s indulgence, what
- chance could there be of improvement? Catherine, weak-spirited,
- irritable, and completely under Lydia’s guidance, had been always
- affronted by their advice; and Lydia, self-willed and careless,
- would scarcely give them a hearing. They were ignorant, idle, and
- vain. While there was an officer in Meryton, they would flirt
- with him; and while Meryton was within a walk of Longbourn, they
- would be going there forever.
-
- Anxiety on Jane’s behalf was another prevailing concern; and Mr.
- Darcy’s explanation, by restoring Bingley to all her former good
- opinion, heightened the sense of what Jane had lost. His
- affection was proved to have been sincere, and his conduct
- cleared of all blame, unless any could attach to the implicitness
- of his confidence in his friend. How grievous then was the
- thought that, of a situation so desirable in every respect, so
- replete with advantage, so promising for happiness, Jane had been
- deprived, by the folly and indecorum of her own family!
-
- When to these recollections was added the development of
- Wickham’s character, it may be easily believed that the happy
- spirits which had seldom been depressed before, were now so much
- affected as to make it almost impossible for her to appear
- tolerably cheerful.
-
- Their engagements at Rosings were as frequent during the last
- week of her stay as they had been at first. The very last evening
- was spent there; and her ladyship again inquired minutely into
- the particulars of their journey, gave them directions as to the
- best method of packing, and was so urgent on the necessity of
- placing gowns in the only right way, that Maria thought herself
- obliged, on her return, to undo all the work of the morning, and
- pack her trunk afresh.
-
- When they parted, Lady Catherine, with great condescension,
- wished them a good journey, and invited them to come to Hunsford
- again next year; and Miss de Bourgh exerted herself so far as to
- curtsey and hold out her hand to both.
-
-
-
-
- Chapter 38
-
- On Saturday morning Elizabeth and Mr. Collins met for breakfast a
- few minutes before the others appeared; and he took the
- opportunity of paying the parting civilities which he deemed
- indispensably necessary.
-
- “I know not, Miss Elizabeth,” said he, “whether Mrs. Collins has
- yet expressed her sense of your kindness in coming to us; but I
- am very certain you will not leave the house without receiving
- her thanks for it. The favour of your company has been much felt,
- I assure you. We know how little there is to tempt anyone to our
- humble abode. Our plain manner of living, our small rooms and few
- domestics, and the little we see of the world, must make Hunsford
- extremely dull to a young lady like yourself; but I hope you will
- believe us grateful for the condescension, and that we have done
- everything in our power to prevent your spending your time
- unpleasantly.”
-
- Elizabeth was eager with her thanks and assurances of happiness.
- She had spent six weeks with great enjoyment; and the pleasure of
- being with Charlotte, and the kind attentions she had received,
- must make _her_ feel the obliged. Mr. Collins was gratified, and
- with a more smiling solemnity replied:
-
- “It gives me great pleasure to hear that you have passed your
- time not disagreeably. We have certainly done our best; and most
- fortunately having it in our power to introduce you to very
- superior society, and, from our connection with Rosings, the
- frequent means of varying the humble home scene, I think we may
- flatter ourselves that your Hunsford visit cannot have been
- entirely irksome. Our situation with regard to Lady Catherine’s
- family is indeed the sort of extraordinary advantage and blessing
- which few can boast. You see on what a footing we are. You see
- how continually we are engaged there. In truth I must acknowledge
- that, with all the disadvantages of this humble parsonage, I
- should not think anyone abiding in it an object of compassion,
- while they are sharers of our intimacy at Rosings.”
-
- Words were insufficient for the elevation of his feelings; and he
- was obliged to walk about the room, while Elizabeth tried to
- unite civility and truth in a few short sentences.
-
- “You may, in fact, carry a very favourable report of us into
- Hertfordshire, my dear cousin. I flatter myself at least that you
- will be able to do so. Lady Catherine’s great attentions to Mrs.
- Collins you have been a daily witness of; and altogether I trust
- it does not appear that your friend has drawn an unfortunate—but
- on this point it will be as well to be silent. Only let me assure
- you, my dear Miss Elizabeth, that I can from my heart most
- cordially wish you equal felicity in marriage. My dear Charlotte
- and I have but one mind and one way of thinking. There is in
- everything a most remarkable resemblance of character and ideas
- between us. We seem to have been designed for each other.”
-
- Elizabeth could safely say that it was a great happiness where
- that was the case, and with equal sincerity could add, that she
- firmly believed and rejoiced in his domestic comforts. She was
- not sorry, however, to have the recital of them interrupted by
- the lady from whom they sprang. Poor Charlotte! it was melancholy
- to leave her to such society! But she had chosen it with her eyes
- open; and though evidently regretting that her visitors were to
- go, she did not seem to ask for compassion. Her home and her
- housekeeping, her parish and her poultry, and all their dependent
- concerns, had not yet lost their charms.
-
- At length the chaise arrived, the trunks were fastened on, the
- parcels placed within, and it was pronounced to be ready. After
- an affectionate parting between the friends, Elizabeth was
- attended to the carriage by Mr. Collins, and as they walked down
- the garden he was commissioning her with his best respects to all
- her family, not forgetting his thanks for the kindness he had
- received at Longbourn in the winter, and his compliments to Mr.
- and Mrs. Gardiner, though unknown. He then handed her in, Maria
- followed, and the door was on the point of being closed, when he
- suddenly reminded them, with some consternation, that they had
- hitherto forgotten to leave any message for the ladies at
- Rosings.
-
- “But,” he added, “you will of course wish to have your humble
- respects delivered to them, with your grateful thanks for their
- kindness to you while you have been here.”
-
- Elizabeth made no objection; the door was then allowed to be
- shut, and the carriage drove off.
-
- “Good gracious!” cried Maria, after a few minutes’ silence, “it
- seems but a day or two since we first came! and yet how many
- things have happened!”
-
- “A great many indeed,” said her companion with a sigh.
-
- “We have dined nine times at Rosings, besides drinking tea there
- twice! How much I shall have to tell!”
-
- Elizabeth added privately, “And how much I shall have to
- conceal!”
-
- Their journey was performed without much conversation, or any
- alarm; and within four hours of their leaving Hunsford they
- reached Mr. Gardiner’s house, where they were to remain a few
- days.
-
- Jane looked well, and Elizabeth had little opportunity of
- studying her spirits, amidst the various engagements which the
- kindness of her aunt had reserved for them. But Jane was to go
- home with her, and at Longbourn there would be leisure enough for
- observation.
-
- It was not without an effort, meanwhile, that she could wait even
- for Longbourn, before she told her sister of Mr. Darcy’s
- proposals. To know that she had the power of revealing what would
- so exceedingly astonish Jane, and must, at the same time, so
- highly gratify whatever of her own vanity she had not yet been
- able to reason away, was such a temptation to openness as nothing
- could have conquered but the state of indecision in which she
- remained as to the extent of what she should communicate; and her
- fear, if she once entered on the subject, of being hurried into
- repeating something of Bingley which might only grieve her sister
- further.
-
-
-
-
- Chapter 39
-
- It was the second week in May, in which the three young ladies
- set out together from Gracechurch Street for the town of ——, in
- Hertfordshire; and, as they drew near the appointed inn where Mr.
- Bennet’s carriage was to meet them, they quickly perceived, in
- token of the coachman’s punctuality, both Kitty and Lydia looking
- out of a dining-room up stairs. These two girls had been above an
- hour in the place, happily employed in visiting an opposite
- milliner, watching the sentinel on guard, and dressing a salad
- and cucumber.
-
- After welcoming their sisters, they triumphantly displayed a
- table set out with such cold meat as an inn larder usually
- affords, exclaiming, “Is not this nice? Is not this an agreeable
- surprise?”
-
- “And we mean to treat you all,” added Lydia, “but you must lend
- us the money, for we have just spent ours at the shop out there.”
- Then, showing her purchases—“Look here, I have bought this
- bonnet. I do not think it is very pretty; but I thought I might
- as well buy it as not. I shall pull it to pieces as soon as I get
- home, and see if I can make it up any better.”
-
- And when her sisters abused it as ugly, she added, with perfect
- unconcern, “Oh! but there were two or three much uglier in the
- shop; and when I have bought some prettier-coloured satin to trim
- it with fresh, I think it will be very tolerable. Besides, it
- will not much signify what one wears this summer, after the
- ——shire have left Meryton, and they are going in a fortnight.”
-
- “Are they indeed!” cried Elizabeth, with the greatest
- satisfaction.
-
- “They are going to be encamped near Brighton; and I do so want
- papa to take us all there for the summer! It would be such a
- delicious scheme; and I dare say would hardly cost anything at
- all. Mamma would like to go too of all things! Only think what a
- miserable summer else we shall have!”
-
- “Yes,” thought Elizabeth, “_that_ would be a delightful scheme
- indeed, and completely do for us at once. Good Heaven! Brighton,
- and a whole campful of soldiers, to us, who have been overset
- already by one poor regiment of militia, and the monthly balls of
- Meryton!”
-
- “Now I have got some news for you,” said Lydia, as they sat down
- at table. “What do you think? It is excellent news—capital
- news—and about a certain person we all like!”
-
- Jane and Elizabeth looked at each other, and the waiter was told
- he need not stay. Lydia laughed, and said:
-
- “Aye, that is just like your formality and discretion. You
- thought the waiter must not hear, as if he cared! I dare say he
- often hears worse things said than I am going to say. But he is
- an ugly fellow! I am glad he is gone. I never saw such a long
- chin in my life. Well, but now for my news; it is about dear
- Wickham; too good for the waiter, is it not? There is no danger
- of Wickham’s marrying Mary King. There’s for you! She is gone
- down to her uncle at Liverpool: gone to stay. Wickham is safe.”
-
- “And Mary King is safe!” added Elizabeth; “safe from a connection
- imprudent as to fortune.”
-
- “She is a great fool for going away, if she liked him.”
-
- “But I hope there is no strong attachment on either side,” said
- Jane.
-
- “I am sure there is not on _his_. I will answer for it, he never
- cared three straws about her—who _could_ about such a nasty
- little freckled thing?”
-
- Elizabeth was shocked to think that, however incapable of such
- coarseness of _expression_ herself, the coarseness of the
- _sentiment_ was little other than her own breast had harboured
- and fancied liberal!
-
- As soon as all had ate, and the elder ones paid, the carriage was
- ordered; and after some contrivance, the whole party, with all
- their boxes, work-bags, and parcels, and the unwelcome addition
- of Kitty’s and Lydia’s purchases, were seated in it.
-
- “How nicely we are all crammed in,” cried Lydia. “I am glad I
- bought my bonnet, if it is only for the fun of having another
- bandbox! Well, now let us be quite comfortable and snug, and talk
- and laugh all the way home. And in the first place, let us hear
- what has happened to you all since you went away. Have you seen
- any pleasant men? Have you had any flirting? I was in great hopes
- that one of you would have got a husband before you came back.
- Jane will be quite an old maid soon, I declare. She is almost
- three-and-twenty! Lord, how ashamed I should be of not being
- married before three-and-twenty! My aunt Phillips wants you so to
- get husbands, you can’t think. She says Lizzy had better have
- taken Mr. Collins; but _I_ do not think there would have been any
- fun in it. Lord! how I should like to be married before any of
- you; and then I would _chaperon_ you about to all the balls. Dear
- me! we had such a good piece of fun the other day at Colonel
- Forster’s. Kitty and me were to spend the day there, and Mrs.
- Forster promised to have a little dance in the evening; (by the
- bye, Mrs. Forster and me are _such_ friends!) and so she asked
- the two Harringtons to come, but Harriet was ill, and so Pen was
- forced to come by herself; and then, what do you think we did? We
- dressed up Chamberlayne in woman’s clothes on purpose to pass for
- a lady, only think what fun! Not a soul knew of it, but Colonel
- and Mrs. Forster, and Kitty and me, except my aunt, for we were
- forced to borrow one of her gowns; and you cannot imagine how
- well he looked! When Denny, and Wickham, and Pratt, and two or
- three more of the men came in, they did not know him in the
- least. Lord! how I laughed! and so did Mrs. Forster. I thought I
- should have died. And _that_ made the men suspect something, and
- then they soon found out what was the matter.”
-
- With such kinds of histories of their parties and good jokes, did
- Lydia, assisted by Kitty’s hints and additions, endeavour to
- amuse her companions all the way to Longbourn. Elizabeth listened
- as little as she could, but there was no escaping the frequent
- mention of Wickham’s name.
-
- Their reception at home was most kind. Mrs. Bennet rejoiced to
- see Jane in undiminished beauty; and more than once during dinner
- did Mr. Bennet say voluntarily to Elizabeth:
-
- “I am glad you are come back, Lizzy.”
-
- Their party in the dining-room was large, for almost all the
- Lucases came to meet Maria and hear the news; and various were
- the subjects that occupied them: Lady Lucas was inquiring of
- Maria, after the welfare and poultry of her eldest daughter; Mrs.
- Bennet was doubly engaged, on one hand collecting an account of
- the present fashions from Jane, who sat some way below her, and,
- on the other, retailing them all to the younger Lucases; and
- Lydia, in a voice rather louder than any other person’s, was
- enumerating the various pleasures of the morning to anybody who
- would hear her.
-
- “Oh! Mary,” said she, “I wish you had gone with us, for we had
- such fun! As we went along, Kitty and I drew up the blinds, and
- pretended there was nobody in the coach; and I should have gone
- so all the way, if Kitty had not been sick; and when we got to
- the George, I do think we behaved very handsomely, for we treated
- the other three with the nicest cold luncheon in the world, and
- if you would have gone, we would have treated you too. And then
- when we came away it was such fun! I thought we never should have
- got into the coach. I was ready to die of laughter. And then we
- were so merry all the way home! we talked and laughed so loud,
- that anybody might have heard us ten miles off!”
-
- To this Mary very gravely replied, “Far be it from me, my dear
- sister, to depreciate such pleasures! They would doubtless be
- congenial with the generality of female minds. But I confess they
- would have no charms for _me_—I should infinitely prefer a book.”
-
- But of this answer Lydia heard not a word. She seldom listened to
- anybody for more than half a minute, and never attended to Mary
- at all.
-
- In the afternoon Lydia was urgent with the rest of the girls to
- walk to Meryton, and to see how everybody went on; but Elizabeth
- steadily opposed the scheme. It should not be said that the Miss
- Bennets could not be at home half a day before they were in
- pursuit of the officers. There was another reason too for her
- opposition. She dreaded seeing Mr. Wickham again, and was
- resolved to avoid it as long as possible. The comfort to _her_ of
- the regiment’s approaching removal was indeed beyond expression.
- In a fortnight they were to go—and once gone, she hoped there
- could be nothing more to plague her on his account.
-
- She had not been many hours at home before she found that the
- Brighton scheme, of which Lydia had given them a hint at the inn,
- was under frequent discussion between her parents. Elizabeth saw
- directly that her father had not the smallest intention of
- yielding; but his answers were at the same time so vague and
- equivocal, that her mother, though often disheartened, had never
- yet despaired of succeeding at last.
-
-
-
-
- Chapter 40
-
- Elizabeth’s impatience to acquaint Jane with what had happened
- could no longer be overcome; and at length, resolving to suppress
- every particular in which her sister was concerned, and preparing
- her to be surprised, she related to her the next morning the
- chief of the scene between Mr. Darcy and herself.
-
- Miss Bennet’s astonishment was soon lessened by the strong
- sisterly partiality which made any admiration of Elizabeth appear
- perfectly natural; and all surprise was shortly lost in other
- feelings. She was sorry that Mr. Darcy should have delivered his
- sentiments in a manner so little suited to recommend them; but
- still more was she grieved for the unhappiness which her sister’s
- refusal must have given him.
-
- “His being so sure of succeeding was wrong,” said she, “and
- certainly ought not to have appeared; but consider how much it
- must increase his disappointment!”
-
- “Indeed,” replied Elizabeth, “I am heartily sorry for him; but he
- has other feelings, which will probably soon drive away his
- regard for me. You do not blame me, however, for refusing him?”
-
- “Blame you! Oh, no.”
-
- “But you blame me for having spoken so warmly of Wickham?”
-
- “No—I do not know that you were wrong in saying what you did.”
-
- “But you _will_ know it, when I tell you what happened the very
- next day.”
-
- She then spoke of the letter, repeating the whole of its contents
- as far as they concerned George Wickham. What a stroke was this
- for poor Jane! who would willingly have gone through the world
- without believing that so much wickedness existed in the whole
- race of mankind, as was here collected in one individual. Nor was
- Darcy’s vindication, though grateful to her feelings, capable of
- consoling her for such discovery. Most earnestly did she labour
- to prove the probability of error, and seek to clear the one
- without involving the other.
-
- “This will not do,” said Elizabeth; “you never will be able to
- make both of them good for anything. Take your choice, but you
- must be satisfied with only one. There is but such a quantity of
- merit between them; just enough to make one good sort of man; and
- of late it has been shifting about pretty much. For my part, I am
- inclined to believe it all Darcy’s; but you shall do as you
- choose.”
-
- It was some time, however, before a smile could be extorted from
- Jane.
-
- “I do not know when I have been more shocked,” said she. “Wickham
- so very bad! It is almost past belief. And poor Mr. Darcy! Dear
- Lizzy, only consider what he must have suffered. Such a
- disappointment! and with the knowledge of your ill opinion, too!
- and having to relate such a thing of his sister! It is really too
- distressing. I am sure you must feel it so.”
-
- “Oh! no, my regret and compassion are all done away by seeing you
- so full of both. I know you will do him such ample justice, that
- I am growing every moment more unconcerned and indifferent. Your
- profusion makes me saving; and if you lament over him much
- longer, my heart will be as light as a feather.”
-
- “Poor Wickham! there is such an expression of goodness in his
- countenance! such an openness and gentleness in his manner!”
-
- “There certainly was some great mismanagement in the education of
- those two young men. One has got all the goodness, and the other
- all the appearance of it.”
-
- “I never thought Mr. Darcy so deficient in the _appearance_ of it
- as you used to do.”
-
- “And yet I meant to be uncommonly clever in taking so decided a
- dislike to him, without any reason. It is such a spur to one’s
- genius, such an opening for wit, to have a dislike of that kind.
- One may be continually abusive without saying anything just; but
- one cannot always be laughing at a man without now and then
- stumbling on something witty.”
-
- “Lizzy, when you first read that letter, I am sure you could not
- treat the matter as you do now.”
-
- “Indeed, I could not. I was uncomfortable enough, I may say
- unhappy. And with no one to speak to about what I felt, no Jane
- to comfort me and say that I had not been so very weak and vain
- and nonsensical as I knew I had! Oh! how I wanted you!”
-
- “How unfortunate that you should have used such very strong
- expressions in speaking of Wickham to Mr. Darcy, for now they
- _do_ appear wholly undeserved.”
-
- “Certainly. But the misfortune of speaking with bitterness is a
- most natural consequence of the prejudices I had been
- encouraging. There is one point on which I want your advice. I
- want to be told whether I ought, or ought not, to make our
- acquaintances in general understand Wickham’s character.”
-
- Miss Bennet paused a little, and then replied, “Surely there can
- be no occasion for exposing him so dreadfully. What is your
- opinion?”
-
- “That it ought not to be attempted. Mr. Darcy has not authorised
- me to make his communication public. On the contrary, every
- particular relative to his sister was meant to be kept as much as
- possible to myself; and if I endeavour to undeceive people as to
- the rest of his conduct, who will believe me? The general
- prejudice against Mr. Darcy is so violent, that it would be the
- death of half the good people in Meryton to attempt to place him
- in an amiable light. I am not equal to it. Wickham will soon be
- gone; and therefore it will not signify to anyone here what he
- really is. Some time hence it will be all found out, and then we
- may laugh at their stupidity in not knowing it before. At present
- I will say nothing about it.”
-
- “You are quite right. To have his errors made public might ruin
- him for ever. He is now, perhaps, sorry for what he has done, and
- anxious to re-establish a character. We must not make him
- desperate.”
-
- The tumult of Elizabeth’s mind was allayed by this conversation.
- She had got rid of two of the secrets which had weighed on her
- for a fortnight, and was certain of a willing listener in Jane,
- whenever she might wish to talk again of either. But there was
- still something lurking behind, of which prudence forbade the
- disclosure. She dared not relate the other half of Mr. Darcy’s
- letter, nor explain to her sister how sincerely she had been
- valued by her friend. Here was knowledge in which no one could
- partake; and she was sensible that nothing less than a perfect
- understanding between the parties could justify her in throwing
- off this last encumbrance of mystery. “And then,” said she, “if
- that very improbable event should ever take place, I shall merely
- be able to tell what Bingley may tell in a much more agreeable
- manner himself. The liberty of communication cannot be mine till
- it has lost all its value!”
-
- She was now, on being settled at home, at leisure to observe the
- real state of her sister’s spirits. Jane was not happy. She still
- cherished a very tender affection for Bingley. Having never even
- fancied herself in love before, her regard had all the warmth of
- first attachment, and, from her age and disposition, greater
- steadiness than most first attachments often boast; and so
- fervently did she value his remembrance, and prefer him to every
- other man, that all her good sense, and all her attention to the
- feelings of her friends, were requisite to check the indulgence
- of those regrets which must have been injurious to her own health
- and their tranquillity.
-
- “Well, Lizzy,” said Mrs. Bennet one day, “what is your opinion
- _now_ of this sad business of Jane’s? For my part, I am
- determined never to speak of it again to anybody. I told my
- sister Phillips so the other day. But I cannot find out that Jane
- saw anything of him in London. Well, he is a very undeserving
- young man—and I do not suppose there’s the least chance in the
- world of her ever getting him now. There is no talk of his coming
- to Netherfield again in the summer; and I have inquired of
- everybody, too, who is likely to know.”
-
- “I do not believe he will ever live at Netherfield any more.”
-
- “Oh well! it is just as he chooses. Nobody wants him to come.
- Though I shall always say he used my daughter extremely ill; and
- if I was her, I would not have put up with it. Well, my comfort
- is, I am sure Jane will die of a broken heart; and then he will
- be sorry for what he has done.”
-
- But as Elizabeth could not receive comfort from any such
- expectation, she made no answer.
-
- “Well, Lizzy,” continued her mother, soon afterwards, “and so the
- Collinses live very comfortable, do they? Well, well, I only hope
- it will last. And what sort of table do they keep? Charlotte is
- an excellent manager, I dare say. If she is half as sharp as her
- mother, she is saving enough. There is nothing extravagant in
- _their_ housekeeping, I dare say.”
-
- “No, nothing at all.”
-
- “A great deal of good management, depend upon it. Yes, yes.
- _They_ will take care not to outrun their income. _They_ will
- never be distressed for money. Well, much good may it do them!
- And so, I suppose, they often talk of having Longbourn when your
- father is dead. They look upon it as quite their own, I dare say,
- whenever that happens.”
-
- “It was a subject which they could not mention before me.”
-
- “No; it would have been strange if they had; but I make no doubt
- they often talk of it between themselves. Well, if they can be
- easy with an estate that is not lawfully their own, so much the
- better. _I_ should be ashamed of having one that was only
- entailed on me.”
-
-
-
-
- Chapter 41
-
- The first week of their return was soon gone. The second began.
- It was the last of the regiment’s stay in Meryton, and all the
- young ladies in the neighbourhood were drooping apace. The
- dejection was almost universal. The elder Miss Bennets alone were
- still able to eat, drink, and sleep, and pursue the usual course
- of their employments. Very frequently were they reproached for
- this insensibility by Kitty and Lydia, whose own misery was
- extreme, and who could not comprehend such hard-heartedness in
- any of the family.
-
- “Good Heaven! what is to become of us? What are we to do?” would
- they often exclaim in the bitterness of woe. “How can you be
- smiling so, Lizzy?”
-
- Their affectionate mother shared all their grief; she remembered
- what she had herself endured on a similar occasion,
- five-and-twenty years ago.
-
- “I am sure,” said she, “I cried for two days together when
- Colonel Miller’s regiment went away. I thought I should have
- broken my heart.”
-
- “I am sure I shall break _mine_,” said Lydia.
-
- “If one could but go to Brighton!” observed Mrs. Bennet.
-
- “Oh, yes!—if one could but go to Brighton! But papa is so
- disagreeable.”
-
- “A little sea-bathing would set me up forever.”
-
- “And my aunt Phillips is sure it would do _me_ a great deal of
- good,” added Kitty.
-
- Such were the kind of lamentations resounding perpetually through
- Longbourn House. Elizabeth tried to be diverted by them; but all
- sense of pleasure was lost in shame. She felt anew the justice of
- Mr. Darcy’s objections; and never had she been so much disposed
- to pardon his interference in the views of his friend.
-
- But the gloom of Lydia’s prospect was shortly cleared away; for
- she received an invitation from Mrs. Forster, the wife of the
- colonel of the regiment, to accompany her to Brighton. This
- invaluable friend was a very young woman, and very lately
- married. A resemblance in good humour and good spirits had
- recommended her and Lydia to each other, and out of their _three_
- months’ acquaintance they had been intimate _two_.
-
- The rapture of Lydia on this occasion, her adoration of Mrs.
- Forster, the delight of Mrs. Bennet, and the mortification of
- Kitty, are scarcely to be described. Wholly inattentive to her
- sister’s feelings, Lydia flew about the house in restless
- ecstasy, calling for everyone’s congratulations, and laughing and
- talking with more violence than ever; whilst the luckless Kitty
- continued in the parlour repined at her fate in terms as
- unreasonable as her accent was peevish.
-
- “I cannot see why Mrs. Forster should not ask _me_ as well as
- Lydia,” said she, “Though I am _not_ her particular friend. I
- have just as much right to be asked as she has, and more too, for
- I am two years older.”
-
- In vain did Elizabeth attempt to make her reasonable, and Jane to
- make her resigned. As for Elizabeth herself, this invitation was
- so far from exciting in her the same feelings as in her mother
- and Lydia, that she considered it as the death warrant of all
- possibility of common sense for the latter; and detestable as
- such a step must make her were it known, she could not help
- secretly advising her father not to let her go. She represented
- to him all the improprieties of Lydia’s general behaviour, the
- little advantage she could derive from the friendship of such a
- woman as Mrs. Forster, and the probability of her being yet more
- imprudent with such a companion at Brighton, where the
- temptations must be greater than at home. He heard her
- attentively, and then said:
-
- “Lydia will never be easy until she has exposed herself in some
- public place or other, and we can never expect her to do it with
- so little expense or inconvenience to her family as under the
- present circumstances.”
-
- “If you were aware,” said Elizabeth, “of the very great
- disadvantage to us all which must arise from the public notice of
- Lydia’s unguarded and imprudent manner—nay, which has already
- arisen from it, I am sure you would judge differently in the
- affair.”
-
- “Already arisen?” repeated Mr. Bennet. “What, has she frightened
- away some of your lovers? Poor little Lizzy! But do not be cast
- down. Such squeamish youths as cannot bear to be connected with a
- little absurdity are not worth a regret. Come, let me see the
- list of pitiful fellows who have been kept aloof by Lydia’s
- folly.”
-
- “Indeed you are mistaken. I have no such injuries to resent. It
- is not of particular, but of general evils, which I am now
- complaining. Our importance, our respectability in the world must
- be affected by the wild volatility, the assurance and disdain of
- all restraint which mark Lydia’s character. Excuse me, for I must
- speak plainly. If you, my dear father, will not take the trouble
- of checking her exuberant spirits, and of teaching her that her
- present pursuits are not to be the business of her life, she will
- soon be beyond the reach of amendment. Her character will be
- fixed, and she will, at sixteen, be the most determined flirt
- that ever made herself or her family ridiculous; a flirt, too, in
- the worst and meanest degree of flirtation; without any
- attraction beyond youth and a tolerable person; and, from the
- ignorance and emptiness of her mind, wholly unable to ward off
- any portion of that universal contempt which her rage for
- admiration will excite. In this danger Kitty also is
- comprehended. She will follow wherever Lydia leads. Vain,
- ignorant, idle, and absolutely uncontrolled! Oh! my dear father,
- can you suppose it possible that they will not be censured and
- despised wherever they are known, and that their sisters will not
- be often involved in the disgrace?”
-
- Mr. Bennet saw that her whole heart was in the subject, and
- affectionately taking her hand said in reply:
-
- “Do not make yourself uneasy, my love. Wherever you and Jane are
- known you must be respected and valued; and you will not appear
- to less advantage for having a couple of—or I may say, three—very
- silly sisters. We shall have no peace at Longbourn if Lydia does
- not go to Brighton. Let her go, then. Colonel Forster is a
- sensible man, and will keep her out of any real mischief; and she
- is luckily too poor to be an object of prey to anybody. At
- Brighton she will be of less importance even as a common flirt
- than she has been here. The officers will find women better worth
- their notice. Let us hope, therefore, that her being there may
- teach her her own insignificance. At any rate, she cannot grow
- many degrees worse, without authorising us to lock her up for the
- rest of her life.”
-
- With this answer Elizabeth was forced to be content; but her own
- opinion continued the same, and she left him disappointed and
- sorry. It was not in her nature, however, to increase her
- vexations by dwelling on them. She was confident of having
- performed her duty, and to fret over unavoidable evils, or
- augment them by anxiety, was no part of her disposition.
-
- Had Lydia and her mother known the substance of her conference
- with her father, their indignation would hardly have found
- expression in their united volubility. In Lydia’s imagination, a
- visit to Brighton comprised every possibility of earthly
- happiness. She saw, with the creative eye of fancy, the streets
- of that gay bathing-place covered with officers. She saw herself
- the object of attention, to tens and to scores of them at present
- unknown. She saw all the glories of the camp—its tents stretched
- forth in beauteous uniformity of lines, crowded with the young
- and the gay, and dazzling with scarlet; and, to complete the
- view, she saw herself seated beneath a tent, tenderly flirting
- with at least six officers at once.
-
- Had she known her sister sought to tear her from such prospects
- and such realities as these, what would have been her sensations?
- They could have been understood only by her mother, who might
- have felt nearly the same. Lydia’s going to Brighton was all that
- consoled her for her melancholy conviction of her husband’s never
- intending to go there himself.
-
- But they were entirely ignorant of what had passed; and their
- raptures continued, with little intermission, to the very day of
- Lydia’s leaving home.
-
- Elizabeth was now to see Mr. Wickham for the last time. Having
- been frequently in company with him since her return, agitation
- was pretty well over; the agitations of former partiality
- entirely so. She had even learnt to detect, in the very
- gentleness which had first delighted her, an affectation and a
- sameness to disgust and weary. In his present behaviour to
- herself, moreover, she had a fresh source of displeasure, for the
- inclination he soon testified of renewing those intentions which
- had marked the early part of their acquaintance could only serve,
- after what had since passed, to provoke her. She lost all concern
- for him in finding herself thus selected as the object of such
- idle and frivolous gallantry; and while she steadily repressed
- it, could not but feel the reproof contained in his believing,
- that however long, and for whatever cause, his attentions had
- been withdrawn, her vanity would be gratified, and her preference
- secured at any time by their renewal.
-
- On the very last day of the regiment’s remaining at Meryton, he
- dined, with other of the officers, at Longbourn; and so little
- was Elizabeth disposed to part from him in good humour, that on
- his making some inquiry as to the manner in which her time had
- passed at Hunsford, she mentioned Colonel Fitzwilliam’s and Mr.
- Darcy’s having both spent three weeks at Rosings, and asked him,
- if he was acquainted with the former.
-
- He looked surprised, displeased, alarmed; but with a moment’s
- recollection and a returning smile, replied, that he had formerly
- seen him often; and, after observing that he was a very
- gentlemanlike man, asked her how she had liked him. Her answer
- was warmly in his favour. With an air of indifference he soon
- afterwards added:
-
- “How long did you say he was at Rosings?”
-
- “Nearly three weeks.”
-
- “And you saw him frequently?”
-
- “Yes, almost every day.”
-
- “His manners are very different from his cousin’s.”
-
- “Yes, very different. But I think Mr. Darcy improves upon
- acquaintance.”
-
- “Indeed!” cried Mr. Wickham with a look which did not escape her.
- “And pray, may I ask?—” But checking himself, he added, in a
- gayer tone, “Is it in address that he improves? Has he deigned to
- add aught of civility to his ordinary style?—for I dare not
- hope,” he continued in a lower and more serious tone, “that he is
- improved in essentials.”
-
- “Oh, no!” said Elizabeth. “In essentials, I believe, he is very
- much what he ever was.”
-
- While she spoke, Wickham looked as if scarcely knowing whether to
- rejoice over her words, or to distrust their meaning. There was a
- something in her countenance which made him listen with an
- apprehensive and anxious attention, while she added:
-
- “When I said that he improved on acquaintance, I did not mean
- that his mind or his manners were in a state of improvement, but
- that, from knowing him better, his disposition was better
- understood.”
-
- Wickham’s alarm now appeared in a heightened complexion and
- agitated look; for a few minutes he was silent, till, shaking off
- his embarrassment, he turned to her again, and said in the
- gentlest of accents:
-
- “You, who so well know my feeling towards Mr. Darcy, will readily
- comprehend how sincerely I must rejoice that he is wise enough to
- assume even the _appearance_ of what is right. His pride, in that
- direction, may be of service, if not to himself, to many others,
- for it must only deter him from such foul misconduct as I have
- suffered by. I only fear that the sort of cautiousness to which
- you, I imagine, have been alluding, is merely adopted on his
- visits to his aunt, of whose good opinion and judgement he stands
- much in awe. His fear of her has always operated, I know, when
- they were together; and a good deal is to be imputed to his wish
- of forwarding the match with Miss de Bourgh, which I am certain
- he has very much at heart.”
-
- Elizabeth could not repress a smile at this, but she answered
- only by a slight inclination of the head. She saw that he wanted
- to engage her on the old subject of his grievances, and she was
- in no humour to indulge him. The rest of the evening passed with
- the _appearance_, on his side, of usual cheerfulness, but with no
- further attempt to distinguish Elizabeth; and they parted at last
- with mutual civility, and possibly a mutual desire of never
- meeting again.
-
- When the party broke up, Lydia returned with Mrs. Forster to
- Meryton, from whence they were to set out early the next morning.
- The separation between her and her family was rather noisy than
- pathetic. Kitty was the only one who shed tears; but she did weep
- from vexation and envy. Mrs. Bennet was diffuse in her good
- wishes for the felicity of her daughter, and impressive in her
- injunctions that she should not miss the opportunity of enjoying
- herself as much as possible—advice which there was every reason
- to believe would be well attended to; and in the clamorous
- happiness of Lydia herself in bidding farewell, the more gentle
- adieus of her sisters were uttered without being heard.
-
-
-
-
- Chapter 42
-
- Had Elizabeth’s opinion been all drawn from her own family, she
- could not have formed a very pleasing opinion of conjugal
- felicity or domestic comfort. Her father, captivated by youth and
- beauty, and that appearance of good humour which youth and beauty
- generally give, had married a woman whose weak understanding and
- illiberal mind had very early in their marriage put an end to all
- real affection for her. Respect, esteem, and confidence had
- vanished for ever; and all his views of domestic happiness were
- overthrown. But Mr. Bennet was not of a disposition to seek
- comfort for the disappointment which his own imprudence had
- brought on, in any of those pleasures which too often console the
- unfortunate for their folly or their vice. He was fond of the
- country and of books; and from these tastes had arisen his
- principal enjoyments. To his wife he was very little otherwise
- indebted, than as her ignorance and folly had contributed to his
- amusement. This is not the sort of happiness which a man would in
- general wish to owe to his wife; but where other powers of
- entertainment are wanting, the true philosopher will derive
- benefit from such as are given.
-
- Elizabeth, however, had never been blind to the impropriety of
- her father’s behaviour as a husband. She had always seen it with
- pain; but respecting his abilities, and grateful for his
- affectionate treatment of herself, she endeavoured to forget what
- she could not overlook, and to banish from her thoughts that
- continual breach of conjugal obligation and decorum which, in
- exposing his wife to the contempt of her own children, was so
- highly reprehensible. But she had never felt so strongly as now
- the disadvantages which must attend the children of so unsuitable
- a marriage, nor ever been so fully aware of the evils arising
- from so ill-judged a direction of talents; talents, which,
- rightly used, might at least have preserved the respectability of
- his daughters, even if incapable of enlarging the mind of his
- wife.
-
- When Elizabeth had rejoiced over Wickham’s departure she found
- little other cause for satisfaction in the loss of the regiment.
- Their parties abroad were less varied than before, and at home
- she had a mother and sister whose constant repinings at the
- dullness of everything around them threw a real gloom over their
- domestic circle; and, though Kitty might in time regain her
- natural degree of sense, since the disturbers of her brain were
- removed, her other sister, from whose disposition greater evil
- might be apprehended, was likely to be hardened in all her folly
- and assurance by a situation of such double danger as a
- watering-place and a camp. Upon the whole, therefore, she found,
- what has been sometimes found before, that an event to which she
- had been looking with impatient desire did not, in taking place,
- bring all the satisfaction she had promised herself. It was
- consequently necessary to name some other period for the
- commencement of actual felicity—to have some other point on which
- her wishes and hopes might be fixed, and by again enjoying the
- pleasure of anticipation, console herself for the present, and
- prepare for another disappointment. Her tour to the Lakes was now
- the object of her happiest thoughts; it was her best consolation
- for all the uncomfortable hours which the discontentedness of her
- mother and Kitty made inevitable; and could she have included
- Jane in the scheme, every part of it would have been perfect.
-
- “But it is fortunate,” thought she, “that I have something to
- wish for. Were the whole arrangement complete, my disappointment
- would be certain. But here, by carrying with me one ceaseless
- source of regret in my sister’s absence, I may reasonably hope to
- have all my expectations of pleasure realised. A scheme of which
- every part promises delight can never be successful; and general
- disappointment is only warded off by the defence of some little
- peculiar vexation.”
-
- When Lydia went away she promised to write very often and very
- minutely to her mother and Kitty; but her letters were always
- long expected, and always very short. Those to her mother
- contained little else than that they were just returned from the
- library, where such and such officers had attended them, and
- where she had seen such beautiful ornaments as made her quite
- wild; that she had a new gown, or a new parasol, which she would
- have described more fully, but was obliged to leave off in a
- violent hurry, as Mrs. Forster called her, and they were going
- off to the camp; and from her correspondence with her sister,
- there was still less to be learnt—for her letters to Kitty,
- though rather longer, were much too full of lines under the words
- to be made public.
-
- After the first fortnight or three weeks of her absence, health,
- good humour, and cheerfulness began to reappear at Longbourn.
- Everything wore a happier aspect. The families who had been in
- town for the winter came back again, and summer finery and summer
- engagements arose. Mrs. Bennet was restored to her usual
- querulous serenity; and, by the middle of June, Kitty was so much
- recovered as to be able to enter Meryton without tears; an event
- of such happy promise as to make Elizabeth hope that by the
- following Christmas she might be so tolerably reasonable as not
- to mention an officer above once a day, unless, by some cruel and
- malicious arrangement at the War Office, another regiment should
- be quartered in Meryton.
-
- The time fixed for the beginning of their northern tour was now
- fast approaching, and a fortnight only was wanting of it, when a
- letter arrived from Mrs. Gardiner, which at once delayed its
- commencement and curtailed its extent. Mr. Gardiner would be
- prevented by business from setting out till a fortnight later in
- July, and must be in London again within a month, and as that
- left too short a period for them to go so far, and see so much as
- they had proposed, or at least to see it with the leisure and
- comfort they had built on, they were obliged to give up the
- Lakes, and substitute a more contracted tour, and, according to
- the present plan, were to go no farther northwards than
- Derbyshire. In that county there was enough to be seen to occupy
- the chief of their three weeks; and to Mrs. Gardiner it had a
- peculiarly strong attraction. The town where she had formerly
- passed some years of her life, and where they were now to spend a
- few days, was probably as great an object of her curiosity as all
- the celebrated beauties of Matlock, Chatsworth, Dovedale, or the
- Peak.
-
- Elizabeth was excessively disappointed; she had set her heart on
- seeing the Lakes, and still thought there might have been time
- enough. But it was her business to be satisfied—and certainly her
- temper to be happy; and all was soon right again.
-
- With the mention of Derbyshire there were many ideas connected.
- It was impossible for her to see the word without thinking of
- Pemberley and its owner. “But surely,” said she, “I may enter his
- county with impunity, and rob it of a few petrified spars without
- his perceiving me.”
-
- The period of expectation was now doubled. Four weeks were to
- pass away before her uncle and aunt’s arrival. But they did pass
- away, and Mr. and Mrs. Gardiner, with their four children, did at
- length appear at Longbourn. The children, two girls of six and
- eight years old, and two younger boys, were to be left under the
- particular care of their cousin Jane, who was the general
- favourite, and whose steady sense and sweetness of temper exactly
- adapted her for attending to them in every way—teaching them,
- playing with them, and loving them.
-
- The Gardiners stayed only one night at Longbourn, and set off the
- next morning with Elizabeth in pursuit of novelty and amusement.
- One enjoyment was certain—that of suitableness of companions; a
- suitableness which comprehended health and temper to bear
- inconveniences—cheerfulness to enhance every pleasure—and
- affection and intelligence, which might supply it among
- themselves if there were disappointments abroad.
-
- It is not the object of this work to give a description of
- Derbyshire, nor of any of the remarkable places through which
- their route thither lay; Oxford, Blenheim, Warwick, Kenilworth,
- Birmingham, etc. are sufficiently known. A small part of
- Derbyshire is all the present concern. To the little town of
- Lambton, the scene of Mrs. Gardiner’s former residence, and where
- she had lately learned some acquaintance still remained, they
- bent their steps, after having seen all the principal wonders of
- the country; and within five miles of Lambton, Elizabeth found
- from her aunt that Pemberley was situated. It was not in their
- direct road, nor more than a mile or two out of it. In talking
- over their route the evening before, Mrs. Gardiner expressed an
- inclination to see the place again. Mr. Gardiner declared his
- willingness, and Elizabeth was applied to for her approbation.
-
- “My love, should not you like to see a place of which you have
- heard so much?” said her aunt; “a place, too, with which so many
- of your acquaintances are connected. Wickham passed all his youth
- there, you know.”
-
- Elizabeth was distressed. She felt that she had no business at
- Pemberley, and was obliged to assume a disinclination for seeing
- it. She must own that she was tired of seeing great houses; after
- going over so many, she really had no pleasure in fine carpets or
- satin curtains.
-
- Mrs. Gardiner abused her stupidity. “If it were merely a fine
- house richly furnished,” said she, “I should not care about it
- myself; but the grounds are delightful. They have some of the
- finest woods in the country.”
-
- Elizabeth said no more—but her mind could not acquiesce. The
- possibility of meeting Mr. Darcy, while viewing the place,
- instantly occurred. It would be dreadful! She blushed at the very
- idea, and thought it would be better to speak openly to her aunt
- than to run such a risk. But against this there were objections;
- and she finally resolved that it could be the last resource, if
- her private inquiries to the absence of the family were
- unfavourably answered.
-
- Accordingly, when she retired at night, she asked the chambermaid
- whether Pemberley were not a very fine place? what was the name
- of its proprietor? and, with no little alarm, whether the family
- were down for the summer? A most welcome negative followed the
- last question—and her alarms now being removed, she was at
- leisure to feel a great deal of curiosity to see the house
- herself; and when the subject was revived the next morning, and
- she was again applied to, could readily answer, and with a proper
- air of indifference, that she had not really any dislike to the
- scheme. To Pemberley, therefore, they were to go.
-
-
-
-
- Chapter 43
-
- Elizabeth, as they drove along, watched for the first appearance
- of Pemberley Woods with some perturbation; and when at length
- they turned in at the lodge, her spirits were in a high flutter.
-
- The park was very large, and contained great variety of ground.
- They entered it in one of its lowest points, and drove for some
- time through a beautiful wood stretching over a wide extent.
-
- Elizabeth’s mind was too full for conversation, but she saw and
- admired every remarkable spot and point of view. They gradually
- ascended for half-a-mile, and then found themselves at the top of
- a considerable eminence, where the wood ceased, and the eye was
- instantly caught by Pemberley House, situated on the opposite
- side of a valley, into which the road with some abruptness wound.
- It was a large, handsome stone building, standing well on rising
- ground, and backed by a ridge of high woody hills; and in front,
- a stream of some natural importance was swelled into greater, but
- without any artificial appearance. Its banks were neither formal
- nor falsely adorned. Elizabeth was delighted. She had never seen
- a place for which nature had done more, or where natural beauty
- had been so little counteracted by an awkward taste. They were
- all of them warm in their admiration; and at that moment she felt
- that to be mistress of Pemberley might be something!
-
- They descended the hill, crossed the bridge, and drove to the
- door; and, while examining the nearer aspect of the house, all
- her apprehension of meeting its owner returned. She dreaded lest
- the chambermaid had been mistaken. On applying to see the place,
- they were admitted into the hall; and Elizabeth, as they waited
- for the housekeeper, had leisure to wonder at her being where she
- was.
-
- The housekeeper came; a respectable-looking elderly woman, much
- less fine, and more civil, than she had any notion of finding
- her. They followed her into the dining-parlour. It was a large,
- well proportioned room, handsomely fitted up. Elizabeth, after
- slightly surveying it, went to a window to enjoy its prospect.
- The hill, crowned with wood, which they had descended, receiving
- increased abruptness from the distance, was a beautiful object.
- Every disposition of the ground was good; and she looked on the
- whole scene, the river, the trees scattered on its banks and the
- winding of the valley, as far as she could trace it, with
- delight. As they passed into other rooms these objects were
- taking different positions; but from every window there were
- beauties to be seen. The rooms were lofty and handsome, and their
- furniture suitable to the fortune of its proprietor; but
- Elizabeth saw, with admiration of his taste, that it was neither
- gaudy nor uselessly fine; with less of splendour, and more real
- elegance, than the furniture of Rosings.
-
- “And of this place,” thought she, “I might have been mistress!
- With these rooms I might now have been familiarly acquainted!
- Instead of viewing them as a stranger, I might have rejoiced in
- them as my own, and welcomed to them as visitors my uncle and
- aunt. But no,”—recollecting herself—“that could never be; my
- uncle and aunt would have been lost to me; I should not have been
- allowed to invite them.”
-
- This was a lucky recollection—it saved her from something very
- like regret.
-
- She longed to inquire of the housekeeper whether her master was
- really absent, but had not the courage for it. At length however,
- the question was asked by her uncle; and she turned away with
- alarm, while Mrs. Reynolds replied that he was, adding, “But we
- expect him to-morrow, with a large party of friends.” How
- rejoiced was Elizabeth that their own journey had not by any
- circumstance been delayed a day!
-
- Her aunt now called her to look at a picture. She approached and
- saw the likeness of Mr. Wickham, suspended, amongst several other
- miniatures, over the mantelpiece. Her aunt asked her, smilingly,
- how she liked it. The housekeeper came forward, and told them it
- was a picture of a young gentleman, the son of her late master’s
- steward, who had been brought up by him at his own expense. “He
- is now gone into the army,” she added; “but I am afraid he has
- turned out very wild.”
-
- Mrs. Gardiner looked at her niece with a smile, but Elizabeth
- could not return it.
-
- “And that,” said Mrs. Reynolds, pointing to another of the
- miniatures, “is my master—and very like him. It was drawn at the
- same time as the other—about eight years ago.”
-
- “I have heard much of your master’s fine person,” said Mrs.
- Gardiner, looking at the picture; “it is a handsome face. But,
- Lizzy, you can tell us whether it is like or not.”
-
- Mrs. Reynolds respect for Elizabeth seemed to increase on this
- intimation of her knowing her master.
-
- “Does that young lady know Mr. Darcy?”
-
- Elizabeth coloured, and said: “A little.”
-
- “And do not you think him a very handsome gentleman, ma’am?”
-
- “Yes, very handsome.”
-
- “I am sure _I_ know none so handsome; but in the gallery up
- stairs you will see a finer, larger picture of him than this.
- This room was my late master’s favourite room, and these
- miniatures are just as they used to be then. He was very fond of
- them.”
-
- This accounted to Elizabeth for Mr. Wickham’s being among them.
-
- Mrs. Reynolds then directed their attention to one of Miss Darcy,
- drawn when she was only eight years old.
-
- “And is Miss Darcy as handsome as her brother?” said Mrs.
- Gardiner.
-
- “Oh! yes—the handsomest young lady that ever was seen; and so
- accomplished!—She plays and sings all day long. In the next room
- is a new instrument just come down for her—a present from my
- master; she comes here to-morrow with him.”
-
- Mr. Gardiner, whose manners were very easy and pleasant,
- encouraged her communicativeness by his questions and remarks;
- Mrs. Reynolds, either by pride or attachment, had evidently great
- pleasure in talking of her master and his sister.
-
- “Is your master much at Pemberley in the course of the year?”
-
- “Not so much as I could wish, sir; but I dare say he may spend
- half his time here; and Miss Darcy is always down for the summer
- months.”
-
- “Except,” thought Elizabeth, “when she goes to Ramsgate.”
-
- “If your master would marry, you might see more of him.”
-
- “Yes, sir; but I do not know when _that_ will be. I do not know
- who is good enough for him.”
-
- Mr. and Mrs. Gardiner smiled. Elizabeth could not help saying,
- “It is very much to his credit, I am sure, that you should think
- so.”
-
- “I say no more than the truth, and everybody will say that knows
- him,” replied the other. Elizabeth thought this was going pretty
- far; and she listened with increasing astonishment as the
- housekeeper added, “I have never known a cross word from him in
- my life, and I have known him ever since he was four years old.”
-
- This was praise, of all others most extraordinary, most opposite
- to her ideas. That he was not a good-tempered man had been her
- firmest opinion. Her keenest attention was awakened; she longed
- to hear more, and was grateful to her uncle for saying:
-
- “There are very few people of whom so much can be said. You are
- lucky in having such a master.”
-
- “Yes, sir, I know I am. If I were to go through the world, I
- could not meet with a better. But I have always observed, that
- they who are good-natured when children, are good-natured when
- they grow up; and he was always the sweetest-tempered, most
- generous-hearted boy in the world.”
-
- Elizabeth almost stared at her. “Can this be Mr. Darcy?” thought
- she.
-
- “His father was an excellent man,” said Mrs. Gardiner.
-
- “Yes, ma’am, that he was indeed; and his son will be just like
- him—just as affable to the poor.”
-
- Elizabeth listened, wondered, doubted, and was impatient for
- more. Mrs. Reynolds could interest her on no other point. She
- related the subjects of the pictures, the dimensions of the
- rooms, and the price of the furniture, in vain. Mr. Gardiner,
- highly amused by the kind of family prejudice to which he
- attributed her excessive commendation of her master, soon led
- again to the subject; and she dwelt with energy on his many
- merits as they proceeded together up the great staircase.
-
- “He is the best landlord, and the best master,” said she, “that
- ever lived; not like the wild young men nowadays, who think of
- nothing but themselves. There is not one of his tenants or
- servants but will give him a good name. Some people call him
- proud; but I am sure I never saw anything of it. To my fancy, it
- is only because he does not rattle away like other young men.”
-
- “In what an amiable light does this place him!” thought
- Elizabeth.
-
- “This fine account of him,” whispered her aunt as they walked,
- “is not quite consistent with his behaviour to our poor friend.”
-
- “Perhaps we might be deceived.”
-
- “That is not very likely; our authority was too good.”
-
- On reaching the spacious lobby above they were shown into a very
- pretty sitting-room, lately fitted up with greater elegance and
- lightness than the apartments below; and were informed that it
- was but just done to give pleasure to Miss Darcy, who had taken a
- liking to the room when last at Pemberley.
-
- “He is certainly a good brother,” said Elizabeth, as she walked
- towards one of the windows.
-
- Mrs. Reynolds anticipated Miss Darcy’s delight, when she should
- enter the room. “And this is always the way with him,” she added.
- “Whatever can give his sister any pleasure is sure to be done in
- a moment. There is nothing he would not do for her.”
-
- The picture-gallery, and two or three of the principal bedrooms,
- were all that remained to be shown. In the former were many good
- paintings; but Elizabeth knew nothing of the art; and from such
- as had been already visible below, she had willingly turned to
- look at some drawings of Miss Darcy’s, in crayons, whose subjects
- were usually more interesting, and also more intelligible.
-
- In the gallery there were many family portraits, but they could
- have little to fix the attention of a stranger. Elizabeth walked
- in quest of the only face whose features would be known to her.
- At last it arrested her—and she beheld a striking resemblance to
- Mr. Darcy, with such a smile over the face as she remembered to
- have sometimes seen when he looked at her. She stood several
- minutes before the picture, in earnest contemplation, and
- returned to it again before they quitted the gallery. Mrs.
- Reynolds informed them that it had been taken in his father’s
- lifetime.
-
- There was certainly at this moment, in Elizabeth’s mind, a more
- gentle sensation towards the original than she had ever felt at
- the height of their acquaintance. The commendation bestowed on
- him by Mrs. Reynolds was of no trifling nature. What praise is
- more valuable than the praise of an intelligent servant? As a
- brother, a landlord, a master, she considered how many people’s
- happiness were in his guardianship!—how much of pleasure or pain
- was it in his power to bestow!—how much of good or evil must be
- done by him! Every idea that had been brought forward by the
- housekeeper was favourable to his character, and as she stood
- before the canvas on which he was represented, and fixed his eyes
- upon herself, she thought of his regard with a deeper sentiment
- of gratitude than it had ever raised before; she remembered its
- warmth, and softened its impropriety of expression.
-
- When all of the house that was open to general inspection had
- been seen, they returned downstairs, and, taking leave of the
- housekeeper, were consigned over to the gardener, who met them at
- the hall-door.
-
- As they walked across the hall towards the river, Elizabeth
- turned back to look again; her uncle and aunt stopped also, and
- while the former was conjecturing as to the date of the building,
- the owner of it himself suddenly came forward from the road,
- which led behind it to the stables.
-
- They were within twenty yards of each other, and so abrupt was
- his appearance, that it was impossible to avoid his sight. Their
- eyes instantly met, and the cheeks of both were overspread with
- the deepest blush. He absolutely started, and for a moment seemed
- immovable from surprise; but shortly recovering himself, advanced
- towards the party, and spoke to Elizabeth, if not in terms of
- perfect composure, at least of perfect civility.
-
- She had instinctively turned away; but stopping on his approach,
- received his compliments with an embarrassment impossible to be
- overcome. Had his first appearance, or his resemblance to the
- picture they had just been examining, been insufficient to assure
- the other two that they now saw Mr. Darcy, the gardener’s
- expression of surprise, on beholding his master, must immediately
- have told it. They stood a little aloof while he was talking to
- their niece, who, astonished and confused, scarcely dared lift
- her eyes to his face, and knew not what answer she returned to
- his civil inquiries after her family. Amazed at the alteration of
- his manner since they last parted, every sentence that he uttered
- was increasing her embarrassment; and every idea of the
- impropriety of her being found there recurring to her mind, the
- few minutes in which they continued were some of the most
- uncomfortable in her life. Nor did he seem much more at ease;
- when he spoke, his accent had none of its usual sedateness; and
- he repeated his inquiries as to the time of her having left
- Longbourn, and of her having stayed in Derbyshire, so often, and
- in so hurried a way, as plainly spoke the distraction of his
- thoughts.
-
- At length every idea seemed to fail him; and, after standing a
- few moments without saying a word, he suddenly recollected
- himself, and took leave.
-
- The others then joined her, and expressed admiration of his
- figure; but Elizabeth heard not a word, and wholly engrossed by
- her own feelings, followed them in silence. She was overpowered
- by shame and vexation. Her coming there was the most unfortunate,
- the most ill-judged thing in the world! How strange it must
- appear to him! In what a disgraceful light might it not strike so
- vain a man! It might seem as if she had purposely thrown herself
- in his way again! Oh! why did she come? Or, why did he thus come
- a day before he was expected? Had they been only ten minutes
- sooner, they should have been beyond the reach of his
- discrimination; for it was plain that he was that moment
- arrived—that moment alighted from his horse or his carriage. She
- blushed again and again over the perverseness of the meeting. And
- his behaviour, so strikingly altered—what could it mean? That he
- should even speak to her was amazing!—but to speak with such
- civility, to inquire after her family! Never in her life had she
- seen his manners so little dignified, never had he spoken with
- such gentleness as on this unexpected meeting. What a contrast
- did it offer to his last address in Rosings Park, when he put his
- letter into her hand! She knew not what to think, or how to
- account for it.
-
- They had now entered a beautiful walk by the side of the water,
- and every step was bringing forward a nobler fall of ground, or a
- finer reach of the woods to which they were approaching; but it
- was some time before Elizabeth was sensible of any of it; and,
- though she answered mechanically to the repeated appeals of her
- uncle and aunt, and seemed to direct her eyes to such objects as
- they pointed out, she distinguished no part of the scene. Her
- thoughts were all fixed on that one spot of Pemberley House,
- whichever it might be, where Mr. Darcy then was. She longed to
- know what at the moment was passing in his mind—in what manner he
- thought of her, and whether, in defiance of everything, she was
- still dear to him. Perhaps he had been civil only because he felt
- himself at ease; yet there had been _that_ in his voice which was
- not like ease. Whether he had felt more of pain or of pleasure in
- seeing her she could not tell, but he certainly had not seen her
- with composure.
-
- At length, however, the remarks of her companions on her absence
- of mind aroused her, and she felt the necessity of appearing more
- like herself.
-
- They entered the woods, and bidding adieu to the river for a
- while, ascended some of the higher grounds; when, in spots where
- the opening of the trees gave the eye power to wander, were many
- charming views of the valley, the opposite hills, with the long
- range of woods overspreading many, and occasionally part of the
- stream. Mr. Gardiner expressed a wish of going round the whole
- park, but feared it might be beyond a walk. With a triumphant
- smile they were told that it was ten miles round. It settled the
- matter; and they pursued the accustomed circuit; which brought
- them again, after some time, in a descent among hanging woods, to
- the edge of the water, and one of its narrowest parts. They
- crossed it by a simple bridge, in character with the general air
- of the scene; it was a spot less adorned than any they had yet
- visited; and the valley, here contracted into a glen, allowed
- room only for the stream, and a narrow walk amidst the rough
- coppice-wood which bordered it. Elizabeth longed to explore its
- windings; but when they had crossed the bridge, and perceived
- their distance from the house, Mrs. Gardiner, who was not a great
- walker, could go no farther, and thought only of returning to the
- carriage as quickly as possible. Her niece was, therefore,
- obliged to submit, and they took their way towards the house on
- the opposite side of the river, in the nearest direction; but
- their progress was slow, for Mr. Gardiner, though seldom able to
- indulge the taste, was very fond of fishing, and was so much
- engaged in watching the occasional appearance of some trout in
- the water, and talking to the man about them, that he advanced
- but little. Whilst wandering on in this slow manner, they were
- again surprised, and Elizabeth’s astonishment was quite equal to
- what it had been at first, by the sight of Mr. Darcy approaching
- them, and at no great distance. The walk here being here less
- sheltered than on the other side, allowed them to see him before
- they met. Elizabeth, however astonished, was at least more
- prepared for an interview than before, and resolved to appear and
- to speak with calmness, if he really intended to meet them. For a
- few moments, indeed, she felt that he would probably strike into
- some other path. The idea lasted while a turning in the walk
- concealed him from their view; the turning past, he was
- immediately before them. With a glance, she saw that he had lost
- none of his recent civility; and, to imitate his politeness, she
- began, as they met, to admire the beauty of the place; but she
- had not got beyond the words “delightful,” and “charming,” when
- some unlucky recollections obtruded, and she fancied that praise
- of Pemberley from her might be mischievously construed. Her
- colour changed, and she said no more.
-
- Mrs. Gardiner was standing a little behind; and on her pausing,
- he asked her if she would do him the honour of introducing him to
- her friends. This was a stroke of civility for which she was
- quite unprepared; and she could hardly suppress a smile at his
- being now seeking the acquaintance of some of those very people
- against whom his pride had revolted in his offer to herself.
- “What will be his surprise,” thought she, “when he knows who they
- are? He takes them now for people of fashion.”
-
- The introduction, however, was immediately made; and as she named
- their relationship to herself, she stole a sly look at him, to
- see how he bore it, and was not without the expectation of his
- decamping as fast as he could from such disgraceful companions.
- That he was _surprised_ by the connection was evident; he
- sustained it, however, with fortitude, and so far from going
- away, turned back with them, and entered into conversation with
- Mr. Gardiner. Elizabeth could not but be pleased, could not but
- triumph. It was consoling that he should know she had some
- relations for whom there was no need to blush. She listened most
- attentively to all that passed between them, and gloried in every
- expression, every sentence of her uncle, which marked his
- intelligence, his taste, or his good manners.
-
- The conversation soon turned upon fishing; and she heard Mr.
- Darcy invite him, with the greatest civility, to fish there as
- often as he chose while he continued in the neighbourhood,
- offering at the same time to supply him with fishing tackle, and
- pointing out those parts of the stream where there was usually
- most sport. Mrs. Gardiner, who was walking arm-in-arm with
- Elizabeth, gave her a look expressive of wonder. Elizabeth said
- nothing, but it gratified her exceedingly; the compliment must be
- all for herself. Her astonishment, however, was extreme, and
- continually was she repeating, “Why is he so altered? From what
- can it proceed? It cannot be for _me_—it cannot be for _my_ sake
- that his manners are thus softened. My reproofs at Hunsford could
- not work such a change as this. It is impossible that he should
- still love me.”
-
- After walking some time in this way, the two ladies in front, the
- two gentlemen behind, on resuming their places, after descending
- to the brink of the river for the better inspection of some
- curious water-plant, there chanced to be a little alteration. It
- originated in Mrs. Gardiner, who, fatigued by the exercise of the
- morning, found Elizabeth’s arm inadequate to her support, and
- consequently preferred her husband’s. Mr. Darcy took her place by
- her niece, and they walked on together. After a short silence,
- the lady first spoke. She wished him to know that she had been
- assured of his absence before she came to the place, and
- accordingly began by observing, that his arrival had been very
- unexpected—“for your housekeeper,” she added, “informed us that
- you would certainly not be here till to-morrow; and indeed,
- before we left Bakewell, we understood that you were not
- immediately expected in the country.” He acknowledged the truth
- of it all, and said that business with his steward had occasioned
- his coming forward a few hours before the rest of the party with
- whom he had been travelling. “They will join me early to-morrow,”
- he continued, “and among them are some who will claim an
- acquaintance with you—Mr. Bingley and his sisters.”
-
- Elizabeth answered only by a slight bow. Her thoughts were
- instantly driven back to the time when Mr. Bingley’s name had
- been the last mentioned between them; and, if she might judge by
- his complexion, _his_ mind was not very differently engaged.
-
- “There is also one other person in the party,” he continued after
- a pause, “who more particularly wishes to be known to you. Will
- you allow me, or do I ask too much, to introduce my sister to
- your acquaintance during your stay at Lambton?”
-
- The surprise of such an application was great indeed; it was too
- great for her to know in what manner she acceded to it. She
- immediately felt that whatever desire Miss Darcy might have of
- being acquainted with her must be the work of her brother, and,
- without looking farther, it was satisfactory; it was gratifying
- to know that his resentment had not made him think really ill of
- her.
-
- They now walked on in silence, each of them deep in thought.
- Elizabeth was not comfortable; that was impossible; but she was
- flattered and pleased. His wish of introducing his sister to her
- was a compliment of the highest kind. They soon outstripped the
- others, and when they had reached the carriage, Mr. and Mrs.
- Gardiner were half a quarter of a mile behind.
-
- He then asked her to walk into the house—but she declared herself
- not tired, and they stood together on the lawn. At such a time
- much might have been said, and silence was very awkward. She
- wanted to talk, but there seemed to be an embargo on every
- subject. At last she recollected that she had been travelling,
- and they talked of Matlock and Dove Dale with great perseverance.
- Yet time and her aunt moved slowly—and her patience and her ideas
- were nearly worn out before the _tête-à-tête_ was over.
-
- On Mr. and Mrs. Gardiner’s coming up they were all pressed to go
- into the house and take some refreshment; but this was declined,
- and they parted on each side with utmost politeness. Mr. Darcy
- handed the ladies into the carriage; and when it drove off,
- Elizabeth saw him walking slowly towards the house.
-
- The observations of her uncle and aunt now began; and each of
- them pronounced him to be infinitely superior to anything they
- had expected. “He is perfectly well behaved, polite, and
- unassuming,” said her uncle.
-
- “There _is_ something a little stately in him, to be sure,”
- replied her aunt, “but it is confined to his air, and is not
- unbecoming. I can now say with the housekeeper, that though some
- people may call him proud, _I_ have seen nothing of it.”
-
- “I was never more surprised than by his behaviour to us. It was
- more than civil; it was really attentive; and there was no
- necessity for such attention. His acquaintance with Elizabeth was
- very trifling.”
-
- “To be sure, Lizzy,” said her aunt, “he is not so handsome as
- Wickham; or, rather, he has not Wickham’s countenance, for his
- features are perfectly good. But how came you to tell me that he
- was so disagreeable?”
-
- Elizabeth excused herself as well as she could; said that she had
- liked him better when they had met in Kent than before, and that
- she had never seen him so pleasant as this morning.
-
- “But perhaps he may be a little whimsical in his civilities,”
- replied her uncle. “Your great men often are; and therefore I
- shall not take him at his word, as he might change his mind
- another day, and warn me off his grounds.”
-
- Elizabeth felt that they had entirely misunderstood his
- character, but said nothing.
-
- “From what we have seen of him,” continued Mrs. Gardiner, “I
- really should not have thought that he could have behaved in so
- cruel a way by anybody as he has done by poor Wickham. He has not
- an ill-natured look. On the contrary, there is something pleasing
- about his mouth when he speaks. And there is something of dignity
- in his countenance that would not give one an unfavourable idea
- of his heart. But, to be sure, the good lady who showed us his
- house did give him a most flaming character! I could hardly help
- laughing aloud sometimes. But he is a liberal master, I suppose,
- and _that_ in the eye of a servant comprehends every virtue.”
-
- Elizabeth here felt herself called on to say something in
- vindication of his behaviour to Wickham; and therefore gave them
- to understand, in as guarded a manner as she could, that by what
- she had heard from his relations in Kent, his actions were
- capable of a very different construction; and that his character
- was by no means so faulty, nor Wickham’s so amiable, as they had
- been considered in Hertfordshire. In confirmation of this, she
- related the particulars of all the pecuniary transactions in
- which they had been connected, without actually naming her
- authority, but stating it to be such as might be relied on.
-
- Mrs. Gardiner was surprised and concerned; but as they were now
- approaching the scene of her former pleasures, every idea gave
- way to the charm of recollection; and she was too much engaged in
- pointing out to her husband all the interesting spots in its
- environs to think of anything else. Fatigued as she had been by
- the morning’s walk they had no sooner dined than she set off
- again in quest of her former acquaintance, and the evening was
- spent in the satisfactions of an intercourse renewed after many
- years’ discontinuance.
-
- The occurrences of the day were too full of interest to leave
- Elizabeth much attention for any of these new friends; and she
- could do nothing but think, and think with wonder, of Mr. Darcy’s
- civility, and, above all, of his wishing her to be acquainted
- with his sister.
-
-
-
-
- Chapter 44
-
- Elizabeth had settled it that Mr. Darcy would bring his sister to
- visit her the very day after her reaching Pemberley; and was
- consequently resolved not to be out of sight of the inn the whole
- of that morning. But her conclusion was false; for on the very
- morning after their arrival at Lambton, these visitors came. They
- had been walking about the place with some of their new friends,
- and were just returning to the inn to dress themselves for dining
- with the same family, when the sound of a carriage drew them to a
- window, and they saw a gentleman and a lady in a curricle driving
- up the street. Elizabeth immediately recognizing the livery,
- guessed what it meant, and imparted no small degree of her
- surprise to her relations by acquainting them with the honour
- which she expected. Her uncle and aunt were all amazement; and
- the embarrassment of her manner as she spoke, joined to the
- circumstance itself, and many of the circumstances of the
- preceding day, opened to them a new idea on the business. Nothing
- had ever suggested it before, but they felt that there was no
- other way of accounting for such attentions from such a quarter
- than by supposing a partiality for their niece. While these
- newly-born notions were passing in their heads, the perturbation
- of Elizabeth’s feelings was at every moment increasing. She was
- quite amazed at her own discomposure; but amongst other causes of
- disquiet, she dreaded lest the partiality of the brother should
- have said too much in her favour; and, more than commonly anxious
- to please, she naturally suspected that every power of pleasing
- would fail her.
-
- She retreated from the window, fearful of being seen; and as she
- walked up and down the room, endeavouring to compose herself, saw
- such looks of inquiring surprise in her uncle and aunt as made
- everything worse.
-
- Miss Darcy and her brother appeared, and this formidable
- introduction took place. With astonishment did Elizabeth see that
- her new acquaintance was at least as much embarrassed as herself.
- Since her being at Lambton, she had heard that Miss Darcy was
- exceedingly proud; but the observation of a very few minutes
- convinced her that she was only exceedingly shy. She found it
- difficult to obtain even a word from her beyond a monosyllable.
-
- Miss Darcy was tall, and on a larger scale than Elizabeth; and,
- though little more than sixteen, her figure was formed, and her
- appearance womanly and graceful. She was less handsome than her
- brother; but there was sense and good humour in her face, and her
- manners were perfectly unassuming and gentle. Elizabeth, who had
- expected to find in her as acute and unembarrassed an observer as
- ever Mr. Darcy had been, was much relieved by discerning such
- different feelings.
-
- They had not long been together before Mr. Darcy told her that
- Bingley was also coming to wait on her; and she had barely time
- to express her satisfaction, and prepare for such a visitor, when
- Bingley’s quick step was heard on the stairs, and in a moment he
- entered the room. All Elizabeth’s anger against him had been long
- done away; but had she still felt any, it could hardly have stood
- its ground against the unaffected cordiality with which he
- expressed himself on seeing her again. He inquired in a friendly,
- though general way, after her family, and looked and spoke with
- the same good-humoured ease that he had ever done.
-
- To Mr. and Mrs. Gardiner he was scarcely a less interesting
- personage than to herself. They had long wished to see him. The
- whole party before them, indeed, excited a lively attention. The
- suspicions which had just arisen of Mr. Darcy and their niece
- directed their observation towards each with an earnest though
- guarded inquiry; and they soon drew from those inquiries the full
- conviction that one of them at least knew what it was to love. Of
- the lady’s sensations they remained a little in doubt; but that
- the gentleman was overflowing with admiration was evident enough.
-
- Elizabeth, on her side, had much to do. She wanted to ascertain
- the feelings of each of her visitors; she wanted to compose her
- own, and to make herself agreeable to all; and in the latter
- object, where she feared most to fail, she was most sure of
- success, for those to whom she endeavoured to give pleasure were
- prepossessed in her favour. Bingley was ready, Georgiana was
- eager, and Darcy determined, to be pleased.
-
- In seeing Bingley, her thoughts naturally flew to her sister;
- and, oh! how ardently did she long to know whether any of his
- were directed in a like manner. Sometimes she could fancy that he
- talked less than on former occasions, and once or twice pleased
- herself with the notion that, as he looked at her, he was trying
- to trace a resemblance. But, though this might be imaginary, she
- could not be deceived as to his behaviour to Miss Darcy, who had
- been set up as a rival to Jane. No look appeared on either side
- that spoke particular regard. Nothing occurred between them that
- could justify the hopes of his sister. On this point she was soon
- satisfied; and two or three little circumstances occurred ere
- they parted, which, in her anxious interpretation, denoted a
- recollection of Jane not untinctured by tenderness, and a wish of
- saying more that might lead to the mention of her, had he dared.
- He observed to her, at a moment when the others were talking
- together, and in a tone which had something of real regret, that
- it “was a very long time since he had had the pleasure of seeing
- her;” and, before she could reply, he added, “It is above eight
- months. We have not met since the 26th of November, when we were
- all dancing together at Netherfield.”
-
- Elizabeth was pleased to find his memory so exact; and he
- afterwards took occasion to ask her, when unattended to by any of
- the rest, whether _all_ her sisters were at Longbourn. There was
- not much in the question, nor in the preceding remark; but there
- was a look and a manner which gave them meaning.
-
- It was not often that she could turn her eyes on Mr. Darcy
- himself; but, whenever she did catch a glimpse, she saw an
- expression of general complaisance, and in all that he said she
- heard an accent so removed from _hauteur_ or disdain of his
- companions, as convinced her that the improvement of manners
- which she had yesterday witnessed however temporary its existence
- might prove, had at least outlived one day. When she saw him thus
- seeking the acquaintance and courting the good opinion of people
- with whom any intercourse a few months ago would have been a
- disgrace—when she saw him thus civil, not only to herself, but to
- the very relations whom he had openly disdained, and recollected
- their last lively scene in Hunsford Parsonage—the difference, the
- change was so great, and struck so forcibly on her mind, that she
- could hardly restrain her astonishment from being visible. Never,
- even in the company of his dear friends at Netherfield, or his
- dignified relations at Rosings, had she seen him so desirous to
- please, so free from self-consequence or unbending reserve, as
- now, when no importance could result from the success of his
- endeavours, and when even the acquaintance of those to whom his
- attentions were addressed would draw down the ridicule and
- censure of the ladies both of Netherfield and Rosings.
-
- Their visitors stayed with them above half-an-hour; and when they
- arose to depart, Mr. Darcy called on his sister to join him in
- expressing their wish of seeing Mr. and Mrs. Gardiner, and Miss
- Bennet, to dinner at Pemberley, before they left the country.
- Miss Darcy, though with a diffidence which marked her little in
- the habit of giving invitations, readily obeyed. Mrs. Gardiner
- looked at her niece, desirous of knowing how _she_, whom the
- invitation most concerned, felt disposed as to its acceptance,
- but Elizabeth had turned away her head. Presuming however, that
- this studied avoidance spoke rather a momentary embarrassment
- than any dislike of the proposal, and seeing in her husband, who
- was fond of society, a perfect willingness to accept it, she
- ventured to engage for her attendance, and the day after the next
- was fixed on.
-
- Bingley expressed great pleasure in the certainty of seeing
- Elizabeth again, having still a great deal to say to her, and
- many inquiries to make after all their Hertfordshire friends.
- Elizabeth, construing all this into a wish of hearing her speak
- of her sister, was pleased, and on this account, as well as some
- others, found herself, when their visitors left them, capable of
- considering the last half-hour with some satisfaction, though
- while it was passing, the enjoyment of it had been little. Eager
- to be alone, and fearful of inquiries or hints from her uncle and
- aunt, she stayed with them only long enough to hear their
- favourable opinion of Bingley, and then hurried away to dress.
-
- But she had no reason to fear Mr. and Mrs. Gardiner’s curiosity;
- it was not their wish to force her communication. It was evident
- that she was much better acquainted with Mr. Darcy than they had
- before any idea of; it was evident that he was very much in love
- with her. They saw much to interest, but nothing to justify
- inquiry.
-
- Of Mr. Darcy it was now a matter of anxiety to think well; and,
- as far as their acquaintance reached, there was no fault to find.
- They could not be untouched by his politeness; and had they drawn
- his character from their own feelings and his servant’s report,
- without any reference to any other account, the circle in
- Hertfordshire to which he was known would not have recognized it
- for Mr. Darcy. There was now an interest, however, in believing
- the housekeeper; and they soon became sensible that the authority
- of a servant who had known him since he was four years old, and
- whose own manners indicated respectability, was not to be hastily
- rejected. Neither had anything occurred in the intelligence of
- their Lambton friends that could materially lessen its weight.
- They had nothing to accuse him of but pride; pride he probably
- had, and if not, it would certainly be imputed by the inhabitants
- of a small market-town where the family did not visit. It was
- acknowledged, however, that he was a liberal man, and did much
- good among the poor.
-
- With respect to Wickham, the travellers soon found that he was
- not held there in much estimation; for though the chief of his
- concerns with the son of his patron were imperfectly understood,
- it was yet a well-known fact that, on his quitting Derbyshire, he
- had left many debts behind him, which Mr. Darcy afterwards
- discharged.
-
- As for Elizabeth, her thoughts were at Pemberley this evening
- more than the last; and the evening, though as it passed it
- seemed long, was not long enough to determine her feelings
- towards _one_ in that mansion; and she lay awake two whole hours
- endeavouring to make them out. She certainly did not hate him.
- No; hatred had vanished long ago, and she had almost as long been
- ashamed of ever feeling a dislike against him, that could be so
- called. The respect created by the conviction of his valuable
- qualities, though at first unwillingly admitted, had for some
- time ceased to be repugnant to her feeling; and it was now
- heightened into somewhat of a friendlier nature, by the testimony
- so highly in his favour, and bringing forward his disposition in
- so amiable a light, which yesterday had produced. But above all,
- above respect and esteem, there was a motive within her of
- goodwill which could not be overlooked. It was gratitude;
- gratitude, not merely for having once loved her, but for loving
- her still well enough to forgive all the petulance and acrimony
- of her manner in rejecting him, and all the unjust accusations
- accompanying her rejection. He who, she had been persuaded, would
- avoid her as his greatest enemy, seemed, on this accidental
- meeting, most eager to preserve the acquaintance, and without any
- indelicate display of regard, or any peculiarity of manner, where
- their two selves only were concerned, was soliciting the good
- opinion of her friends, and bent on making her known to his
- sister. Such a change in a man of so much pride exciting not only
- astonishment but gratitude—for to love, ardent love, it must be
- attributed; and as such its impression on her was of a sort to be
- encouraged, as by no means unpleasing, though it could not be
- exactly defined. She respected, she esteemed, she was grateful to
- him, she felt a real interest in his welfare; and she only wanted
- to know how far she wished that welfare to depend upon herself,
- and how far it would be for the happiness of both that she should
- employ the power, which her fancy told her she still possessed,
- of bringing on her the renewal of his addresses.
-
- It had been settled in the evening between the aunt and the
- niece, that such a striking civility as Miss Darcy’s in coming to
- see them on the very day of her arrival at Pemberley, for she had
- reached it only to a late breakfast, ought to be imitated, though
- it could not be equalled, by some exertion of politeness on their
- side; and, consequently, that it would be highly expedient to
- wait on her at Pemberley the following morning. They were,
- therefore, to go. Elizabeth was pleased; though when she asked
- herself the reason, she had very little to say in reply.
-
- Mr. Gardiner left them soon after breakfast. The fishing scheme
- had been renewed the day before, and a positive engagement made
- of his meeting some of the gentlemen at Pemberley before noon.
-
-
-
-
- Chapter 45
-
- Convinced as Elizabeth now was that Miss Bingley’s dislike of her
- had originated in jealousy, she could not help feeling how
- unwelcome her appearance at Pemberley must be to her, and was
- curious to know with how much civility on that lady’s side the
- acquaintance would now be renewed.
-
- On reaching the house, they were shown through the hall into the
- saloon, whose northern aspect rendered it delightful for summer.
- Its windows opening to the ground, admitted a most refreshing
- view of the high woody hills behind the house, and of the
- beautiful oaks and Spanish chestnuts which were scattered over
- the intermediate lawn.
-
- In this house they were received by Miss Darcy, who was sitting
- there with Mrs. Hurst and Miss Bingley, and the lady with whom
- she lived in London. Georgiana’s reception of them was very
- civil, but attended with all the embarrassment which, though
- proceeding from shyness and the fear of doing wrong, would easily
- give to those who felt themselves inferior the belief of her
- being proud and reserved. Mrs. Gardiner and her niece, however,
- did her justice, and pitied her.
-
- By Mrs. Hurst and Miss Bingley they were noticed only by a
- curtsey; and, on their being seated, a pause, awkward as such
- pauses must always be, succeeded for a few moments. It was first
- broken by Mrs. Annesley, a genteel, agreeable-looking woman,
- whose endeavour to introduce some kind of discourse proved her to
- be more truly well-bred than either of the others; and between
- her and Mrs. Gardiner, with occasional help from Elizabeth, the
- conversation was carried on. Miss Darcy looked as if she wished
- for courage enough to join in it; and sometimes did venture a
- short sentence when there was least danger of its being heard.
-
- Elizabeth soon saw that she was herself closely watched by Miss
- Bingley, and that she could not speak a word, especially to Miss
- Darcy, without calling her attention. This observation would not
- have prevented her from trying to talk to the latter, had they
- not been seated at an inconvenient distance; but she was not
- sorry to be spared the necessity of saying much. Her own thoughts
- were employing her. She expected every moment that some of the
- gentlemen would enter the room. She wished, she feared that the
- master of the house might be amongst them; and whether she wished
- or feared it most, she could scarcely determine. After sitting in
- this manner a quarter of an hour without hearing Miss Bingley’s
- voice, Elizabeth was roused by receiving from her a cold inquiry
- after the health of her family. She answered with equal
- indifference and brevity, and the other said no more.
-
- The next variation which their visit afforded was produced by the
- entrance of servants with cold meat, cake, and a variety of all
- the finest fruits in season; but this did not take place till
- after many a significant look and smile from Mrs. Annesley to
- Miss Darcy had been given, to remind her of her post. There was
- now employment for the whole party—for though they could not all
- talk, they could all eat; and the beautiful pyramids of grapes,
- nectarines, and peaches soon collected them round the table.
-
- While thus engaged, Elizabeth had a fair opportunity of deciding
- whether she most feared or wished for the appearance of Mr.
- Darcy, by the feelings which prevailed on his entering the room;
- and then, though but a moment before she had believed her wishes
- to predominate, she began to regret that he came.
-
- He had been some time with Mr. Gardiner, who, with two or three
- other gentlemen from the house, was engaged by the river, and had
- left him only on learning that the ladies of the family intended
- a visit to Georgiana that morning. No sooner did he appear than
- Elizabeth wisely resolved to be perfectly easy and unembarrassed;
- a resolution the more necessary to be made, but perhaps not the
- more easily kept, because she saw that the suspicions of the
- whole party were awakened against them, and that there was
- scarcely an eye which did not watch his behaviour when he first
- came into the room. In no countenance was attentive curiosity so
- strongly marked as in Miss Bingley’s, in spite of the smiles
- which overspread her face whenever she spoke to one of its
- objects; for jealousy had not yet made her desperate, and her
- attentions to Mr. Darcy were by no means over. Miss Darcy, on her
- brother’s entrance, exerted herself much more to talk, and
- Elizabeth saw that he was anxious for his sister and herself to
- get acquainted, and forwarded as much as possible, every attempt
- at conversation on either side. Miss Bingley saw all this
- likewise; and, in the imprudence of anger, took the first
- opportunity of saying, with sneering civility:
-
- “Pray, Miss Eliza, are not the ——shire Militia removed from
- Meryton? They must be a great loss to _your_ family.”
-
- In Darcy’s presence she dared not mention Wickham’s name; but
- Elizabeth instantly comprehended that he was uppermost in her
- thoughts; and the various recollections connected with him gave
- her a moment’s distress; but exerting herself vigorously to repel
- the ill-natured attack, she presently answered the question in a
- tolerably detached tone. While she spoke, an involuntary glance
- showed her Darcy, with a heightened complexion, earnestly looking
- at her, and his sister overcome with confusion, and unable to
- lift up her eyes. Had Miss Bingley known what pain she was then
- giving her beloved friend, she undoubtedly would have refrained
- from the hint; but she had merely intended to discompose
- Elizabeth by bringing forward the idea of a man to whom she
- believed her partial, to make her betray a sensibility which
- might injure her in Darcy’s opinion, and, perhaps, to remind the
- latter of all the follies and absurdities by which some part of
- her family were connected with that corps. Not a syllable had
- ever reached her of Miss Darcy’s meditated elopement. To no
- creature had it been revealed, where secrecy was possible, except
- to Elizabeth; and from all Bingley’s connections her brother was
- particularly anxious to conceal it, from the very wish which
- Elizabeth had long ago attributed to him, of their becoming
- hereafter her own. He had certainly formed such a plan, and
- without meaning that it should affect his endeavour to separate
- him from Miss Bennet, it is probable that it might add something
- to his lively concern for the welfare of his friend.
-
- Elizabeth’s collected behaviour, however, soon quieted his
- emotion; and as Miss Bingley, vexed and disappointed, dared not
- approach nearer to Wickham, Georgiana also recovered in time,
- though not enough to be able to speak any more. Her brother,
- whose eye she feared to meet, scarcely recollected her interest
- in the affair, and the very circumstance which had been designed
- to turn his thoughts from Elizabeth seemed to have fixed them on
- her more and more cheerfully.
-
- Their visit did not continue long after the question and answer
- above mentioned; and while Mr. Darcy was attending them to their
- carriage Miss Bingley was venting her feelings in criticisms on
- Elizabeth’s person, behaviour, and dress. But Georgiana would not
- join her. Her brother’s recommendation was enough to ensure her
- favour; his judgement could not err. And he had spoken in such
- terms of Elizabeth as to leave Georgiana without the power of
- finding her otherwise than lovely and amiable. When Darcy
- returned to the saloon, Miss Bingley could not help repeating to
- him some part of what she had been saying to his sister.
-
- “How very ill Miss Eliza Bennet looks this morning, Mr. Darcy,”
- she cried; “I never in my life saw anyone so much altered as she
- is since the winter. She is grown so brown and coarse! Louisa and
- I were agreeing that we should not have known her again.”
-
- However little Mr. Darcy might have liked such an address, he
- contented himself with coolly replying that he perceived no other
- alteration than her being rather tanned, no miraculous
- consequence of travelling in the summer.
-
- “For my own part,” she rejoined, “I must confess that I never
- could see any beauty in her. Her face is too thin; her complexion
- has no brilliancy; and her features are not at all handsome. Her
- nose wants character—there is nothing marked in its lines. Her
- teeth are tolerable, but not out of the common way; and as for
- her eyes, which have sometimes been called so fine, I could never
- see anything extraordinary in them. They have a sharp, shrewish
- look, which I do not like at all; and in her air altogether there
- is a self-sufficiency without fashion, which is intolerable.”
-
- Persuaded as Miss Bingley was that Darcy admired Elizabeth, this
- was not the best method of recommending herself; but angry people
- are not always wise; and in seeing him at last look somewhat
- nettled, she had all the success she expected. He was resolutely
- silent, however, and, from a determination of making him speak,
- she continued:
-
- “I remember, when we first knew her in Hertfordshire, how amazed
- we all were to find that she was a reputed beauty; and I
- particularly recollect your saying one night, after they had been
- dining at Netherfield, ‘_She_ a beauty!—I should as soon call her
- mother a wit.’ But afterwards she seemed to improve on you, and I
- believe you thought her rather pretty at one time.”
-
- “Yes,” replied Darcy, who could contain himself no longer, “but
- _that_ was only when I first saw her, for it is many months since
- I have considered her as one of the handsomest women of my
- acquaintance.”
-
- He then went away, and Miss Bingley was left to all the
- satisfaction of having forced him to say what gave no one any
- pain but herself.
-
- Mrs. Gardiner and Elizabeth talked of all that had occurred
- during their visit, as they returned, except what had
- particularly interested them both. The look and behaviour of
- everybody they had seen were discussed, except of the person who
- had mostly engaged their attention. They talked of his sister,
- his friends, his house, his fruit—of everything but himself; yet
- Elizabeth was longing to know what Mrs. Gardiner thought of him,
- and Mrs. Gardiner would have been highly gratified by her niece’s
- beginning the subject.
-
-
-
-
- Chapter 46
-
- Elizabeth had been a good deal disappointed in not finding a
- letter from Jane on their first arrival at Lambton; and this
- disappointment had been renewed on each of the mornings that had
- now been spent there; but on the third her repining was over, and
- her sister justified, by the receipt of two letters from her at
- once, on one of which was marked that it had been missent
- elsewhere. Elizabeth was not surprised at it, as Jane had written
- the direction remarkably ill.
-
- They had just been preparing to walk as the letters came in; and
- her uncle and aunt, leaving her to enjoy them in quiet, set off
- by themselves. The one missent must first be attended to; it had
- been written five days ago. The beginning contained an account of
- all their little parties and engagements, with such news as the
- country afforded; but the latter half, which was dated a day
- later, and written in evident agitation, gave more important
- intelligence. It was to this effect:
-
- “Since writing the above, dearest Lizzy, something has occurred
- of a most unexpected and serious nature; but I am afraid of
- alarming you—be assured that we are all well. What I have to say
- relates to poor Lydia. An express came at twelve last night, just
- as we were all gone to bed, from Colonel Forster, to inform us
- that she was gone off to Scotland with one of his officers; to
- own the truth, with Wickham! Imagine our surprise. To Kitty,
- however, it does not seem so wholly unexpected. I am very, very
- sorry. So imprudent a match on both sides! But I am willing to
- hope the best, and that his character has been misunderstood.
- Thoughtless and indiscreet I can easily believe him, but this
- step (and let us rejoice over it) marks nothing bad at heart. His
- choice is disinterested at least, for he must know my father can
- give her nothing. Our poor mother is sadly grieved. My father
- bears it better. How thankful am I that we never let them know
- what has been said against him; we must forget it ourselves. They
- were off Saturday night about twelve, as is conjectured, but were
- not missed till yesterday morning at eight. The express was sent
- off directly. My dear Lizzy, they must have passed within ten
- miles of us. Colonel Forster gives us reason to expect him here
- soon. Lydia left a few lines for his wife, informing her of their
- intention. I must conclude, for I cannot be long from my poor
- mother. I am afraid you will not be able to make it out, but I
- hardly know what I have written.”
-
- Without allowing herself time for consideration, and scarcely
- knowing what she felt, Elizabeth on finishing this letter
- instantly seized the other, and opening it with the utmost
- impatience, read as follows: it had been written a day later than
- the conclusion of the first.
-
- “By this time, my dearest sister, you have received my hurried
- letter; I wish this may be more intelligible, but though not
- confined for time, my head is so bewildered that I cannot answer
- for being coherent. Dearest Lizzy, I hardly know what I would
- write, but I have bad news for you, and it cannot be delayed.
- Imprudent as the marriage between Mr. Wickham and our poor Lydia
- would be, we are now anxious to be assured it has taken place,
- for there is but too much reason to fear they are not gone to
- Scotland. Colonel Forster came yesterday, having left Brighton
- the day before, not many hours after the express. Though Lydia’s
- short letter to Mrs. F. gave them to understand that they were
- going to Gretna Green, something was dropped by Denny expressing
- his belief that W. never intended to go there, or to marry Lydia
- at all, which was repeated to Colonel F., who, instantly taking
- the alarm, set off from B. intending to trace their route. He did
- trace them easily to Clapham, but no further; for on entering
- that place, they removed into a hackney coach, and dismissed the
- chaise that brought them from Epsom. All that is known after this
- is, that they were seen to continue the London road. I know not
- what to think. After making every possible inquiry on that side
- London, Colonel F. came on into Hertfordshire, anxiously renewing
- them at all the turnpikes, and at the inns in Barnet and
- Hatfield, but without any success—no such people had been seen to
- pass through. With the kindest concern he came on to Longbourn,
- and broke his apprehensions to us in a manner most creditable to
- his heart. I am sincerely grieved for him and Mrs. F., but no one
- can throw any blame on them. Our distress, my dear Lizzy, is very
- great. My father and mother believe the worst, but I cannot think
- so ill of him. Many circumstances might make it more eligible for
- them to be married privately in town than to pursue their first
- plan; and even if _he_ could form such a design against a young
- woman of Lydia’s connections, which is not likely, can I suppose
- her so lost to everything? Impossible! I grieve to find, however,
- that Colonel F. is not disposed to depend upon their marriage; he
- shook his head when I expressed my hopes, and said he feared W.
- was not a man to be trusted. My poor mother is really ill, and
- keeps her room. Could she exert herself, it would be better; but
- this is not to be expected. And as to my father, I never in my
- life saw him so affected. Poor Kitty has anger for having
- concealed their attachment; but as it was a matter of confidence,
- one cannot wonder. I am truly glad, dearest Lizzy, that you have
- been spared something of these distressing scenes; but now, as
- the first shock is over, shall I own that I long for your return?
- I am not so selfish, however, as to press for it, if
- inconvenient. Adieu! I take up my pen again to do what I have
- just told you I would not; but circumstances are such that I
- cannot help earnestly begging you all to come here as soon as
- possible. I know my dear uncle and aunt so well, that I am not
- afraid of requesting it, though I have still something more to
- ask of the former. My father is going to London with Colonel
- Forster instantly, to try to discover her. What he means to do I
- am sure I know not; but his excessive distress will not allow him
- to pursue any measure in the best and safest way, and Colonel
- Forster is obliged to be at Brighton again to-morrow evening. In
- such an exigence, my uncle’s advice and assistance would be
- everything in the world; he will immediately comprehend what I
- must feel, and I rely upon his goodness.”
-
- “Oh! where, where is my uncle?” cried Elizabeth, darting from her
- seat as she finished the letter, in eagerness to follow him,
- without losing a moment of the time so precious; but as she
- reached the door it was opened by a servant, and Mr. Darcy
- appeared. Her pale face and impetuous manner made him start, and
- before he could recover himself to speak, she, in whose mind
- every idea was superseded by Lydia’s situation, hastily
- exclaimed, “I beg your pardon, but I must leave you. I must find
- Mr. Gardiner this moment, on business that cannot be delayed; I
- have not an instant to lose.”
-
- “Good God! what is the matter?” cried he, with more feeling than
- politeness; then recollecting himself, “I will not detain you a
- minute; but let me, or let the servant go after Mr. and Mrs.
- Gardiner. You are not well enough; you cannot go yourself.”
-
- Elizabeth hesitated, but her knees trembled under her and she
- felt how little would be gained by her attempting to pursue them.
- Calling back the servant, therefore, she commissioned him, though
- in so breathless an accent as made her almost unintelligible, to
- fetch his master and mistress home instantly.
-
- On his quitting the room she sat down, unable to support herself,
- and looking so miserably ill, that it was impossible for Darcy to
- leave her, or to refrain from saying, in a tone of gentleness and
- commiseration, “Let me call your maid. Is there nothing you could
- take to give you present relief? A glass of wine; shall I get you
- one? You are very ill.”
-
- “No, I thank you,” she replied, endeavouring to recover herself.
- “There is nothing the matter with me. I am quite well; I am only
- distressed by some dreadful news which I have just received from
- Longbourn.”
-
- She burst into tears as she alluded to it, and for a few minutes
- could not speak another word. Darcy, in wretched suspense, could
- only say something indistinctly of his concern, and observe her
- in compassionate silence. At length she spoke again. “I have just
- had a letter from Jane, with such dreadful news. It cannot be
- concealed from anyone. My younger sister has left all her
- friends—has eloped; has thrown herself into the power of—of Mr.
- Wickham. They are gone off together from Brighton. _You_ know him
- too well to doubt the rest. She has no money, no connections,
- nothing that can tempt him to—she is lost for ever.”
-
- Darcy was fixed in astonishment. “When I consider,” she added in
- a yet more agitated voice, “that _I_ might have prevented it! _I_
- who knew what he was. Had I but explained some part of it
- only—some part of what I learnt, to my own family! Had his
- character been known, this could not have happened. But it is
- all—all too late now.”
-
- “I am grieved indeed,” cried Darcy; “grieved—shocked. But is it
- certain—absolutely certain?”
-
- “Oh, yes! They left Brighton together on Sunday night, and were
- traced almost to London, but not beyond; they are certainly not
- gone to Scotland.”
-
- “And what has been done, what has been attempted, to recover
- her?”
-
- “My father is gone to London, and Jane has written to beg my
- uncle’s immediate assistance; and we shall be off, I hope, in
- half-an-hour. But nothing can be done—I know very well that
- nothing can be done. How is such a man to be worked on? How are
- they even to be discovered? I have not the smallest hope. It is
- every way horrible!”
-
- Darcy shook his head in silent acquiescence.
-
- “When _my_ eyes were opened to his real character—Oh! had I known
- what I ought, what I dared to do! But I knew not—I was afraid of
- doing too much. Wretched, wretched mistake!”
-
- Darcy made no answer. He seemed scarcely to hear her, and was
- walking up and down the room in earnest meditation, his brow
- contracted, his air gloomy. Elizabeth soon observed, and
- instantly understood it. Her power was sinking; everything _must_
- sink under such a proof of family weakness, such an assurance of
- the deepest disgrace. She could neither wonder nor condemn, but
- the belief of his self-conquest brought nothing consolatory to
- her bosom, afforded no palliation of her distress. It was, on the
- contrary, exactly calculated to make her understand her own
- wishes; and never had she so honestly felt that she could have
- loved him, as now, when all love must be vain.
-
- But self, though it would intrude, could not engross her.
- Lydia—the humiliation, the misery she was bringing on them all,
- soon swallowed up every private care; and covering her face with
- her handkerchief, Elizabeth was soon lost to everything else;
- and, after a pause of several minutes, was only recalled to a
- sense of her situation by the voice of her companion, who, in a
- manner which, though it spoke compassion, spoke likewise
- restraint, said, “I am afraid you have been long desiring my
- absence, nor have I anything to plead in excuse of my stay, but
- real, though unavailing concern. Would to Heaven that anything
- could be either said or done on my part that might offer
- consolation to such distress! But I will not torment you with
- vain wishes, which may seem purposely to ask for your thanks.
- This unfortunate affair will, I fear, prevent my sister’s having
- the pleasure of seeing you at Pemberley to-day.”
-
- “Oh, yes. Be so kind as to apologise for us to Miss Darcy. Say
- that urgent business calls us home immediately. Conceal the
- unhappy truth as long as it is possible, I know it cannot be
- long.”
-
- He readily assured her of his secrecy; again expressed his sorrow
- for her distress, wished it a happier conclusion than there was
- at present reason to hope, and leaving his compliments for her
- relations, with only one serious, parting look, went away.
-
- As he quitted the room, Elizabeth felt how improbable it was that
- they should ever see each other again on such terms of cordiality
- as had marked their several meetings in Derbyshire; and as she
- threw a retrospective glance over the whole of their
- acquaintance, so full of contradictions and varieties, sighed at
- the perverseness of those feelings which would now have promoted
- its continuance, and would formerly have rejoiced in its
- termination.
-
- If gratitude and esteem are good foundations of affection,
- Elizabeth’s change of sentiment will be neither improbable nor
- faulty. But if otherwise—if regard springing from such sources is
- unreasonable or unnatural, in comparison of what is so often
- described as arising on a first interview with its object, and
- even before two words have been exchanged, nothing can be said in
- her defence, except that she had given somewhat of a trial to the
- latter method in her partiality for Wickham, and that its ill
- success might, perhaps, authorise her to seek the other less
- interesting mode of attachment. Be that as it may, she saw him go
- with regret; and in this early example of what Lydia’s infamy
- must produce, found additional anguish as she reflected on that
- wretched business. Never, since reading Jane’s second letter, had
- she entertained a hope of Wickham’s meaning to marry her. No one
- but Jane, she thought, could flatter herself with such an
- expectation. Surprise was the least of her feelings on this
- development. While the contents of the first letter remained in
- her mind, she was all surprise—all astonishment that Wickham
- should marry a girl whom it was impossible he could marry for
- money; and how Lydia could ever have attached him had appeared
- incomprehensible. But now it was all too natural. For such an
- attachment as this she might have sufficient charms; and though
- she did not suppose Lydia to be deliberately engaging in an
- elopement without the intention of marriage, she had no
- difficulty in believing that neither her virtue nor her
- understanding would preserve her from falling an easy prey.
-
- She had never perceived, while the regiment was in Hertfordshire,
- that Lydia had any partiality for him; but she was convinced that
- Lydia wanted only encouragement to attach herself to anybody.
- Sometimes one officer, sometimes another, had been her favourite,
- as their attentions raised them in her opinion. Her affections
- had continually been fluctuating but never without an object. The
- mischief of neglect and mistaken indulgence towards such a
- girl—oh! how acutely did she now feel it!
-
- She was wild to be at home—to hear, to see, to be upon the spot
- to share with Jane in the cares that must now fall wholly upon
- her, in a family so deranged, a father absent, a mother incapable
- of exertion, and requiring constant attendance; and though almost
- persuaded that nothing could be done for Lydia, her uncle’s
- interference seemed of the utmost importance, and till he entered
- the room her impatience was severe. Mr. and Mrs. Gardiner had
- hurried back in alarm, supposing by the servant’s account that
- their niece was taken suddenly ill; but satisfying them instantly
- on that head, she eagerly communicated the cause of their
- summons, reading the two letters aloud, and dwelling on the
- postscript of the last with trembling energy.— Though Lydia had
- never been a favourite with them, Mr. and Mrs. Gardiner could not
- but be deeply afflicted. Not Lydia only, but all were concerned
- in it; and after the first exclamations of surprise and horror,
- Mr. Gardiner promised every assistance in his power. Elizabeth,
- though expecting no less, thanked him with tears of gratitude;
- and all three being actuated by one spirit, everything relating
- to their journey was speedily settled. They were to be off as
- soon as possible. “But what is to be done about Pemberley?” cried
- Mrs. Gardiner. “John told us Mr. Darcy was here when you sent for
- us; was it so?”
-
- “Yes; and I told him we should not be able to keep our
- engagement. _That_ is all settled.”
-
- “What is all settled?” repeated the other, as she ran into her
- room to prepare. “And are they upon such terms as for her to
- disclose the real truth? Oh, that I knew how it was!”
-
- But wishes were vain, or at least could only serve to amuse her
- in the hurry and confusion of the following hour. Had Elizabeth
- been at leisure to be idle, she would have remained certain that
- all employment was impossible to one so wretched as herself; but
- she had her share of business as well as her aunt, and amongst
- the rest there were notes to be written to all their friends at
- Lambton, with false excuses for their sudden departure. An hour,
- however, saw the whole completed; and Mr. Gardiner meanwhile
- having settled his account at the inn, nothing remained to be
- done but to go; and Elizabeth, after all the misery of the
- morning, found herself, in a shorter space of time than she could
- have supposed, seated in the carriage, and on the road to
- Longbourn.
-
-
-
-
- Chapter 47
-
- “I have been thinking it over again, Elizabeth,” said her uncle,
- as they drove from the town; “and really, upon serious
- consideration, I am much more inclined than I was to judge as
- your eldest sister does on the matter. It appears to me so very
- unlikely that any young man should form such a design against a
- girl who is by no means unprotected or friendless, and who was
- actually staying in his colonel’s family, that I am strongly
- inclined to hope the best. Could he expect that her friends would
- not step forward? Could he expect to be noticed again by the
- regiment, after such an affront to Colonel Forster? His
- temptation is not adequate to the risk!”
-
- “Do you really think so?” cried Elizabeth, brightening up for a
- moment.
-
- “Upon my word,” said Mrs. Gardiner, “I begin to be of your
- uncle’s opinion. It is really too great a violation of decency,
- honour, and interest, for him to be guilty of. I cannot think so
- very ill of Wickham. Can you yourself, Lizzy, so wholly give him
- up, as to believe him capable of it?”
-
- “Not, perhaps, of neglecting his own interest; but of every other
- neglect I can believe him capable. If, indeed, it should be so!
- But I dare not hope it. Why should they not go on to Scotland if
- that had been the case?”
-
- “In the first place,” replied Mr. Gardiner, “there is no absolute
- proof that they are not gone to Scotland.”
-
- “Oh! but their removing from the chaise into a hackney coach is
- such a presumption! And, besides, no traces of them were to be
- found on the Barnet road.”
-
- “Well, then—supposing them to be in London. They may be there,
- though for the purpose of concealment, for no more exceptional
- purpose. It is not likely that money should be very abundant on
- either side; and it might strike them that they could be more
- economically, though less expeditiously, married in London than
- in Scotland.”
-
- “But why all this secrecy? Why any fear of detection? Why must
- their marriage be private? Oh, no, no—this is not likely. His
- most particular friend, you see by Jane’s account, was persuaded
- of his never intending to marry her. Wickham will never marry a
- woman without some money. He cannot afford it. And what claims
- has Lydia—what attraction has she beyond youth, health, and good
- humour that could make him, for her sake, forego every chance of
- benefiting himself by marrying well? As to what restraint the
- apprehensions of disgrace in the corps might throw on a
- dishonourable elopement with her, I am not able to judge; for I
- know nothing of the effects that such a step might produce. But
- as to your other objection, I am afraid it will hardly hold good.
- Lydia has no brothers to step forward; and he might imagine, from
- my father’s behaviour, from his indolence and the little
- attention he has ever seemed to give to what was going forward in
- his family, that _he_ would do as little, and think as little
- about it, as any father could do, in such a matter.”
-
- “But can you think that Lydia is so lost to everything but love
- of him as to consent to live with him on any terms other than
- marriage?”
-
- “It does seem, and it is most shocking indeed,” replied
- Elizabeth, with tears in her eyes, “that a sister’s sense of
- decency and virtue in such a point should admit of doubt. But,
- really, I know not what to say. Perhaps I am not doing her
- justice. But she is very young; she has never been taught to
- think on serious subjects; and for the last half-year, nay, for a
- twelvemonth—she has been given up to nothing but amusement and
- vanity. She has been allowed to dispose of her time in the most
- idle and frivolous manner, and to adopt any opinions that came in
- her way. Since the ——shire were first quartered in Meryton,
- nothing but love, flirtation, and officers have been in her head.
- She has been doing everything in her power by thinking and
- talking on the subject, to give greater—what shall I call it?
- susceptibility to her feelings; which are naturally lively
- enough. And we all know that Wickham has every charm of person
- and address that can captivate a woman.”
-
- “But you see that Jane,” said her aunt, “does not think so very
- ill of Wickham as to believe him capable of the attempt.”
-
- “Of whom does Jane ever think ill? And who is there, whatever
- might be their former conduct, that she would think capable of
- such an attempt, till it were proved against them? But Jane
- knows, as well as I do, what Wickham really is. We both know that
- he has been profligate in every sense of the word; that he has
- neither integrity nor honour; that he is as false and deceitful
- as he is insinuating.”
-
- “And do you really know all this?” cried Mrs. Gardiner, whose
- curiosity as to the mode of her intelligence was all alive.
-
- “I do indeed,” replied Elizabeth, colouring. “I told you, the
- other day, of his infamous behaviour to Mr. Darcy; and you
- yourself, when last at Longbourn, heard in what manner he spoke
- of the man who had behaved with such forbearance and liberality
- towards him. And there are other circumstances which I am not at
- liberty—which it is not worth while to relate; but his lies about
- the whole Pemberley family are endless. From what he said of Miss
- Darcy I was thoroughly prepared to see a proud, reserved,
- disagreeable girl. Yet he knew to the contrary himself. He must
- know that she was as amiable and unpretending as we have found
- her.”
-
- “But does Lydia know nothing of this? can she be ignorant of what
- you and Jane seem so well to understand?”
-
- “Oh, yes!—that, that is the worst of all. Till I was in Kent, and
- saw so much both of Mr. Darcy and his relation Colonel
- Fitzwilliam, I was ignorant of the truth myself. And when I
- returned home, the ——shire was to leave Meryton in a week or
- fortnight’s time. As that was the case, neither Jane, to whom I
- related the whole, nor I, thought it necessary to make our
- knowledge public; for of what use could it apparently be to any
- one, that the good opinion which all the neighbourhood had of him
- should then be overthrown? And even when it was settled that
- Lydia should go with Mrs. Forster, the necessity of opening her
- eyes to his character never occurred to me. That _she_ could be
- in any danger from the deception never entered my head. That such
- a consequence as _this_ could ensue, you may easily believe, was
- far enough from my thoughts.”
-
- “When they all removed to Brighton, therefore, you had no reason,
- I suppose, to believe them fond of each other?”
-
- “Not the slightest. I can remember no symptom of affection on
- either side; and had anything of the kind been perceptible, you
- must be aware that ours is not a family on which it could be
- thrown away. When first he entered the corps, she was ready
- enough to admire him; but so we all were. Every girl in or near
- Meryton was out of her senses about him for the first two months;
- but he never distinguished _her_ by any particular attention;
- and, consequently, after a moderate period of extravagant and
- wild admiration, her fancy for him gave way, and others of the
- regiment, who treated her with more distinction, again became her
- favourites.”
-
- It may be easily believed, that however little of novelty could
- be added to their fears, hopes, and conjectures, on this
- interesting subject, by its repeated discussion, no other could
- detain them from it long, during the whole of the journey. From
- Elizabeth’s thoughts it was never absent. Fixed there by the
- keenest of all anguish, self-reproach, she could find no interval
- of ease or forgetfulness.
-
- They travelled as expeditiously as possible, and, sleeping one
- night on the road, reached Longbourn by dinner time the next day.
- It was a comfort to Elizabeth to consider that Jane could not
- have been wearied by long expectations.
-
- The little Gardiners, attracted by the sight of a chaise, were
- standing on the steps of the house as they entered the paddock;
- and, when the carriage drove up to the door, the joyful surprise
- that lighted up their faces, and displayed itself over their
- whole bodies, in a variety of capers and frisks, was the first
- pleasing earnest of their welcome.
-
- Elizabeth jumped out; and, after giving each of them a hasty
- kiss, hurried into the vestibule, where Jane, who came running
- down from her mother’s apartment, immediately met her.
-
- Elizabeth, as she affectionately embraced her, whilst tears
- filled the eyes of both, lost not a moment in asking whether
- anything had been heard of the fugitives.
-
- “Not yet,” replied Jane. “But now that my dear uncle is come, I
- hope everything will be well.”
-
- “Is my father in town?”
-
- “Yes, he went on Tuesday, as I wrote you word.”
-
- “And have you heard from him often?”
-
- “We have heard only twice. He wrote me a few lines on Wednesday
- to say that he had arrived in safety, and to give me his
- directions, which I particularly begged him to do. He merely
- added that he should not write again till he had something of
- importance to mention.”
-
- “And my mother—how is she? How are you all?”
-
- “My mother is tolerably well, I trust; though her spirits are
- greatly shaken. She is up stairs and will have great satisfaction
- in seeing you all. She does not yet leave her dressing-room. Mary
- and Kitty, thank Heaven, are quite well.”
-
- “But you—how are you?” cried Elizabeth. “You look pale. How much
- you must have gone through!”
-
- Her sister, however, assured her of her being perfectly well; and
- their conversation, which had been passing while Mr. and Mrs.
- Gardiner were engaged with their children, was now put an end to
- by the approach of the whole party. Jane ran to her uncle and
- aunt, and welcomed and thanked them both, with alternate smiles
- and tears.
-
- When they were all in the drawing-room, the questions which
- Elizabeth had already asked were of course repeated by the
- others, and they soon found that Jane had no intelligence to
- give. The sanguine hope of good, however, which the benevolence
- of her heart suggested had not yet deserted her; she still
- expected that it would all end well, and that every morning would
- bring some letter, either from Lydia or her father, to explain
- their proceedings, and, perhaps, announce their marriage.
-
- Mrs. Bennet, to whose apartment they all repaired, after a few
- minutes’ conversation together, received them exactly as might be
- expected; with tears and lamentations of regret, invectives
- against the villainous conduct of Wickham, and complaints of her
- own sufferings and ill-usage; blaming everybody but the person to
- whose ill-judging indulgence the errors of her daughter must
- principally be owing.
-
- “If I had been able,” said she, “to carry my point in going to
- Brighton, with all my family, _this_ would not have happened; but
- poor dear Lydia had nobody to take care of her. Why did the
- Forsters ever let her go out of their sight? I am sure there was
- some great neglect or other on their side, for she is not the
- kind of girl to do such a thing if she had been well looked
- after. I always thought they were very unfit to have the charge
- of her; but I was overruled, as I always am. Poor dear child! And
- now here’s Mr. Bennet gone away, and I know he will fight
- Wickham, wherever he meets him and then he will be killed, and
- what is to become of us all? The Collinses will turn us out
- before he is cold in his grave, and if you are not kind to us,
- brother, I do not know what we shall do.”
-
- They all exclaimed against such terrific ideas; and Mr. Gardiner,
- after general assurances of his affection for her and all her
- family, told her that he meant to be in London the very next day,
- and would assist Mr. Bennet in every endeavour for recovering
- Lydia.
-
- “Do not give way to useless alarm,” added he; “though it is right
- to be prepared for the worst, there is no occasion to look on it
- as certain. It is not quite a week since they left Brighton. In a
- few days more we may gain some news of them; and till we know
- that they are not married, and have no design of marrying, do not
- let us give the matter over as lost. As soon as I get to town I
- shall go to my brother, and make him come home with me to
- Gracechurch Street; and then we may consult together as to what
- is to be done.”
-
- “Oh! my dear brother,” replied Mrs. Bennet, “that is exactly what
- I could most wish for. And now do, when you get to town, find
- them out, wherever they may be; and if they are not married
- already, _make_ them marry. And as for wedding clothes, do not
- let them wait for that, but tell Lydia she shall have as much
- money as she chooses to buy them, after they are married. And,
- above all, keep Mr. Bennet from fighting. Tell him what a
- dreadful state I am in, that I am frighted out of my wits—and
- have such tremblings, such flutterings, all over me—such spasms
- in my side and pains in my head, and such beatings at heart, that
- I can get no rest by night nor by day. And tell my dear Lydia not
- to give any directions about her clothes till she has seen me,
- for she does not know which are the best warehouses. Oh, brother,
- how kind you are! I know you will contrive it all.”
-
- But Mr. Gardiner, though he assured her again of his earnest
- endeavours in the cause, could not avoid recommending moderation
- to her, as well in her hopes as her fear; and after talking with
- her in this manner till dinner was on the table, they all left
- her to vent all her feelings on the housekeeper, who attended in
- the absence of her daughters.
-
- Though her brother and sister were persuaded that there was no
- real occasion for such a seclusion from the family, they did not
- attempt to oppose it, for they knew that she had not prudence
- enough to hold her tongue before the servants, while they waited
- at table, and judged it better that _one_ only of the household,
- and the one whom they could most trust should comprehend all her
- fears and solicitude on the subject.
-
- In the dining-room they were soon joined by Mary and Kitty, who
- had been too busily engaged in their separate apartments to make
- their appearance before. One came from her books, and the other
- from her toilette. The faces of both, however, were tolerably
- calm; and no change was visible in either, except that the loss
- of her favourite sister, or the anger which she had herself
- incurred in this business, had given more of fretfulness than
- usual to the accents of Kitty. As for Mary, she was mistress
- enough of herself to whisper to Elizabeth, with a countenance of
- grave reflection, soon after they were seated at table:
-
- “This is a most unfortunate affair, and will probably be much
- talked of. But we must stem the tide of malice, and pour into the
- wounded bosoms of each other the balm of sisterly consolation.”
-
- Then, perceiving in Elizabeth no inclination of replying, she
- added, “Unhappy as the event must be for Lydia, we may draw from
- it this useful lesson: that loss of virtue in a female is
- irretrievable; that one false step involves her in endless ruin;
- that her reputation is no less brittle than it is beautiful; and
- that she cannot be too much guarded in her behaviour towards the
- undeserving of the other sex.”
-
- Elizabeth lifted up her eyes in amazement, but was too much
- oppressed to make any reply. Mary, however, continued to console
- herself with such kind of moral extractions from the evil before
- them.
-
- In the afternoon, the two elder Miss Bennets were able to be for
- half-an-hour by themselves; and Elizabeth instantly availed
- herself of the opportunity of making any inquiries, which Jane
- was equally eager to satisfy. After joining in general
- lamentations over the dreadful sequel of this event, which
- Elizabeth considered as all but certain, and Miss Bennet could
- not assert to be wholly impossible, the former continued the
- subject, by saying, “But tell me all and everything about it
- which I have not already heard. Give me further particulars. What
- did Colonel Forster say? Had they no apprehension of anything
- before the elopement took place? They must have seen them
- together for ever.”
-
- “Colonel Forster did own that he had often suspected some
- partiality, especially on Lydia’s side, but nothing to give him
- any alarm. I am so grieved for him! His behaviour was attentive
- and kind to the utmost. He _was_ coming to us, in order to assure
- us of his concern, before he had any idea of their not being gone
- to Scotland: when that apprehension first got abroad, it hastened
- his journey.”
-
- “And was Denny convinced that Wickham would not marry? Did he
- know of their intending to go off? Had Colonel Forster seen Denny
- himself?”
-
- “Yes; but, when questioned by _him_, Denny denied knowing
- anything of their plans, and would not give his real opinion
- about it. He did not repeat his persuasion of their not
- marrying—and from _that_, I am inclined to hope, he might have
- been misunderstood before.”
-
- “And till Colonel Forster came himself, not one of you
- entertained a doubt, I suppose, of their being really married?”
-
- “How was it possible that such an idea should enter our brains? I
- felt a little uneasy—a little fearful of my sister’s happiness
- with him in marriage, because I knew that his conduct had not
- been always quite right. My father and mother knew nothing of
- that; they only felt how imprudent a match it must be. Kitty then
- owned, with a very natural triumph on knowing more than the rest
- of us, that in Lydia’s last letter she had prepared her for such
- a step. She had known, it seems, of their being in love with each
- other, many weeks.”
-
- “But not before they went to Brighton?”
-
- “No, I believe not.”
-
- “And did Colonel Forster appear to think well of Wickham himself?
- Does he know his real character?”
-
- “I must confess that he did not speak so well of Wickham as he
- formerly did. He believed him to be imprudent and extravagant.
- And since this sad affair has taken place, it is said that he
- left Meryton greatly in debt; but I hope this may be false.”
-
- “Oh, Jane, had we been less secret, had we told what we knew of
- him, this could not have happened!”
-
- “Perhaps it would have been better,” replied her sister. “But to
- expose the former faults of any person without knowing what their
- present feelings were, seemed unjustifiable. We acted with the
- best intentions.”
-
- “Could Colonel Forster repeat the particulars of Lydia’s note to
- his wife?”
-
- “He brought it with him for us to see.”
-
- Jane then took it from her pocket-book, and gave it to Elizabeth.
- These were the contents:
-
- “My dear Harriet,
- “You will laugh when you know where I am gone, and I cannot help
- laughing myself at your surprise to-morrow morning, as soon as I
- am missed. I am going to Gretna Green, and if you cannot guess
- with who, I shall think you a simpleton, for there is but one man
- in the world I love, and he is an angel. I should never be happy
- without him, so think it no harm to be off. You need not send
- them word at Longbourn of my going, if you do not like it, for it
- will make the surprise the greater, when I write to them and sign
- my name ‘Lydia Wickham.’ What a good joke it will be! I can
- hardly write for laughing. Pray make my excuses to Pratt for not
- keeping my engagement, and dancing with him to-night. Tell him I
- hope he will excuse me when he knows all; and tell him I will
- dance with him at the next ball we meet, with great pleasure. I
- shall send for my clothes when I get to Longbourn; but I wish you
- would tell Sally to mend a great slit in my worked muslin gown
- before they are packed up. Good-bye. Give my love to Colonel
- Forster. I hope you will drink to our good journey.
-
- “Your affectionate friend,
- “LYDIA BENNET.”
-
- “Oh! thoughtless, thoughtless Lydia!” cried Elizabeth when she
- had finished it. “What a letter is this, to be written at such a
- moment! But at least it shows that _she_ was serious on the
- subject of their journey. Whatever he might afterwards persuade
- her to, it was not on her side a _scheme_ of infamy. My poor
- father! how he must have felt it!”
-
- “I never saw anyone so shocked. He could not speak a word for
- full ten minutes. My mother was taken ill immediately, and the
- whole house in such confusion!”
-
- “Oh! Jane,” cried Elizabeth, “was there a servant belonging to it
- who did not know the whole story before the end of the day?”
-
- “I do not know. I hope there was. But to be guarded at such a
- time is very difficult. My mother was in hysterics, and though I
- endeavoured to give her every assistance in my power, I am afraid
- I did not do so much as I might have done! But the horror of what
- might possibly happen almost took from me my faculties.”
-
- “Your attendance upon her has been too much for you. You do not
- look well. Oh that I had been with you! you have had every care
- and anxiety upon yourself alone.”
-
- “Mary and Kitty have been very kind, and would have shared in
- every fatigue, I am sure; but I did not think it right for either
- of them. Kitty is slight and delicate; and Mary studies so much,
- that her hours of repose should not be broken in on. My aunt
- Phillips came to Longbourn on Tuesday, after my father went away;
- and was so good as to stay till Thursday with me. She was of
- great use and comfort to us all. And Lady Lucas has been very
- kind; she walked here on Wednesday morning to condole with us,
- and offered her services, or any of her daughters’, if they
- should be of use to us.”
-
- “She had better have stayed at home,” cried Elizabeth; “perhaps
- she _meant_ well, but, under such a misfortune as this, one
- cannot see too little of one’s neighbours. Assistance is
- impossible; condolence insufferable. Let them triumph over us at
- a distance, and be satisfied.”
-
- She then proceeded to inquire into the measures which her father
- had intended to pursue, while in town, for the recovery of his
- daughter.
-
- “He meant I believe,” replied Jane, “to go to Epsom, the place
- where they last changed horses, see the postilions and try if
- anything could be made out from them. His principal object must
- be to discover the number of the hackney coach which took them
- from Clapham. It had come with a fare from London; and as he
- thought that the circumstance of a gentleman and lady’s removing
- from one carriage into another might be remarked he meant to make
- inquiries at Clapham. If he could anyhow discover at what house
- the coachman had before set down his fare, he determined to make
- inquiries there, and hoped it might not be impossible to find out
- the stand and number of the coach. I do not know of any other
- designs that he had formed; but he was in such a hurry to be
- gone, and his spirits so greatly discomposed, that I had
- difficulty in finding out even so much as this.”
-
-
-
-
- Chapter 48
-
- The whole party were in hopes of a letter from Mr. Bennet the
- next morning, but the post came in without bringing a single line
- from him. His family knew him to be, on all common occasions, a
- most negligent and dilatory correspondent; but at such a time
- they had hoped for exertion. They were forced to conclude that he
- had no pleasing intelligence to send; but even of _that_ they
- would have been glad to be certain. Mr. Gardiner had waited only
- for the letters before he set off.
-
- When he was gone, they were certain at least of receiving
- constant information of what was going on, and their uncle
- promised, at parting, to prevail on Mr. Bennet to return to
- Longbourn, as soon as he could, to the great consolation of his
- sister, who considered it as the only security for her husband’s
- not being killed in a duel.
-
- Mrs. Gardiner and the children were to remain in Hertfordshire a
- few days longer, as the former thought her presence might be
- serviceable to her nieces. She shared in their attendance on Mrs.
- Bennet, and was a great comfort to them in their hours of
- freedom. Their other aunt also visited them frequently, and
- always, as she said, with the design of cheering and heartening
- them up—though, as she never came without reporting some fresh
- instance of Wickham’s extravagance or irregularity, she seldom
- went away without leaving them more dispirited than she found
- them.
-
- All Meryton seemed striving to blacken the man who, but three
- months before, had been almost an angel of light. He was declared
- to be in debt to every tradesman in the place, and his intrigues,
- all honoured with the title of seduction, had been extended into
- every tradesman’s family. Everybody declared that he was the
- wickedest young man in the world; and everybody began to find out
- that they had always distrusted the appearance of his goodness.
- Elizabeth, though she did not credit above half of what was said,
- believed enough to make her former assurance of her sister’s ruin
- more certain; and even Jane, who believed still less of it,
- became almost hopeless, more especially as the time was now come
- when, if they had gone to Scotland, which she had never before
- entirely despaired of, they must in all probability have gained
- some news of them.
-
- Mr. Gardiner left Longbourn on Sunday; on Tuesday his wife
- received a letter from him; it told them that, on his arrival, he
- had immediately found out his brother, and persuaded him to come
- to Gracechurch Street; that Mr. Bennet had been to Epsom and
- Clapham, before his arrival, but without gaining any satisfactory
- information; and that he was now determined to inquire at all the
- principal hotels in town, as Mr. Bennet thought it possible they
- might have gone to one of them, on their first coming to London,
- before they procured lodgings. Mr. Gardiner himself did not
- expect any success from this measure, but as his brother was
- eager in it, he meant to assist him in pursuing it. He added that
- Mr. Bennet seemed wholly disinclined at present to leave London
- and promised to write again very soon. There was also a
- postscript to this effect:
-
- “I have written to Colonel Forster to desire him to find out, if
- possible, from some of the young man’s intimates in the regiment,
- whether Wickham has any relations or connections who would be
- likely to know in what part of town he has now concealed himself.
- If there were anyone that one could apply to with a probability
- of gaining such a clue as that, it might be of essential
- consequence. At present we have nothing to guide us. Colonel
- Forster will, I dare say, do everything in his power to satisfy
- us on this head. But, on second thoughts, perhaps, Lizzy could
- tell us what relations he has now living, better than any other
- person.”
-
- Elizabeth was at no loss to understand from whence this deference
- to her authority proceeded; but it was not in her power to give
- any information of so satisfactory a nature as the compliment
- deserved. She had never heard of his having had any relations,
- except a father and mother, both of whom had been dead many
- years. It was possible, however, that some of his companions in
- the ——shire might be able to give more information; and though
- she was not very sanguine in expecting it, the application was a
- something to look forward to.
-
- Every day at Longbourn was now a day of anxiety; but the most
- anxious part of each was when the post was expected. The arrival
- of letters was the grand object of every morning’s impatience.
- Through letters, whatever of good or bad was to be told would be
- communicated, and every succeeding day was expected to bring some
- news of importance.
-
- But before they heard again from Mr. Gardiner, a letter arrived
- for their father, from a different quarter, from Mr. Collins;
- which, as Jane had received directions to open all that came for
- him in his absence, she accordingly read; and Elizabeth, who knew
- what curiosities his letters always were, looked over her, and
- read it likewise. It was as follows:
-
- “My dear Sir,
- “I feel myself called upon, by our relationship, and my situation
- in life, to condole with you on the grievous affliction you are
- now suffering under, of which we were yesterday informed by a
- letter from Hertfordshire. Be assured, my dear sir, that Mrs.
- Collins and myself sincerely sympathise with you and all your
- respectable family, in your present distress, which must be of
- the bitterest kind, because proceeding from a cause which no time
- can remove. No arguments shall be wanting on my part that can
- alleviate so severe a misfortune—or that may comfort you, under a
- circumstance that must be of all others the most afflicting to a
- parent’s mind. The death of your daughter would have been a
- blessing in comparison of this. And it is the more to be
- lamented, because there is reason to suppose as my dear Charlotte
- informs me, that this licentiousness of behaviour in your
- daughter has proceeded from a faulty degree of indulgence;
- though, at the same time, for the consolation of yourself and
- Mrs. Bennet, I am inclined to think that her own disposition must
- be naturally bad, or she could not be guilty of such an enormity,
- at so early an age. Howsoever that may be, you are grievously to
- be pitied; in which opinion I am not only joined by Mrs. Collins,
- but likewise by Lady Catherine and her daughter, to whom I have
- related the affair. They agree with me in apprehending that this
- false step in one daughter will be injurious to the fortunes of
- all the others; for who, as Lady Catherine herself
- condescendingly says, will connect themselves with such a family?
- And this consideration leads me moreover to reflect, with
- augmented satisfaction, on a certain event of last November; for
- had it been otherwise, I must have been involved in all your
- sorrow and disgrace. Let me then advise you, dear sir, to console
- yourself as much as possible, to throw off your unworthy child
- from your affection for ever, and leave her to reap the fruits of
- her own heinous offense.
-
- “I am, dear sir, etc., etc.”
-
- Mr. Gardiner did not write again till he had received an answer
- from Colonel Forster; and then he had nothing of a pleasant
- nature to send. It was not known that Wickham had a single
- relationship with whom he kept up any connection, and it was
- certain that he had no near one living. His former acquaintances
- had been numerous; but since he had been in the militia, it did
- not appear that he was on terms of particular friendship with any
- of them. There was no one, therefore, who could be pointed out as
- likely to give any news of him. And in the wretched state of his
- own finances, there was a very powerful motive for secrecy, in
- addition to his fear of discovery by Lydia’s relations, for it
- had just transpired that he had left gaming debts behind him to a
- very considerable amount. Colonel Forster believed that more than
- a thousand pounds would be necessary to clear his expenses at
- Brighton. He owed a good deal in town, but his debts of honour
- were still more formidable. Mr. Gardiner did not attempt to
- conceal these particulars from the Longbourn family. Jane heard
- them with horror. “A gamester!” she cried. “This is wholly
- unexpected. I had not an idea of it.”
-
- Mr. Gardiner added in his letter, that they might expect to see
- their father at home on the following day, which was Saturday.
- Rendered spiritless by the ill-success of all their endeavours,
- he had yielded to his brother-in-law’s entreaty that he would
- return to his family, and leave it to him to do whatever occasion
- might suggest to be advisable for continuing their pursuit. When
- Mrs. Bennet was told of this, she did not express so much
- satisfaction as her children expected, considering what her
- anxiety for his life had been before.
-
- “What, is he coming home, and without poor Lydia?” she cried.
- “Sure he will not leave London before he has found them. Who is
- to fight Wickham, and make him marry her, if he comes away?”
-
- As Mrs. Gardiner began to wish to be at home, it was settled that
- she and the children should go to London, at the same time that
- Mr. Bennet came from it. The coach, therefore, took them the
- first stage of their journey, and brought its master back to
- Longbourn.
-
- Mrs. Gardiner went away in all the perplexity about Elizabeth and
- her Derbyshire friend that had attended her from that part of the
- world. His name had never been voluntarily mentioned before them
- by her niece; and the kind of half-expectation which Mrs.
- Gardiner had formed, of their being followed by a letter from
- him, had ended in nothing. Elizabeth had received none since her
- return that could come from Pemberley.
-
- The present unhappy state of the family rendered any other excuse
- for the lowness of her spirits unnecessary; nothing, therefore,
- could be fairly conjectured from _that_, though Elizabeth, who
- was by this time tolerably well acquainted with her own feelings,
- was perfectly aware that, had she known nothing of Darcy, she
- could have borne the dread of Lydia’s infamy somewhat better. It
- would have spared her, she thought, one sleepless night out of
- two.
-
- When Mr. Bennet arrived, he had all the appearance of his usual
- philosophic composure. He said as little as he had ever been in
- the habit of saying; made no mention of the business that had
- taken him away, and it was some time before his daughters had
- courage to speak of it.
-
- It was not till the afternoon, when he had joined them at tea,
- that Elizabeth ventured to introduce the subject; and then, on
- her briefly expressing her sorrow for what he must have endured,
- he replied, “Say nothing of that. Who should suffer but myself?
- It has been my own doing, and I ought to feel it.”
-
- “You must not be too severe upon yourself,” replied Elizabeth.
-
- “You may well warn me against such an evil. Human nature is so
- prone to fall into it! No, Lizzy, let me once in my life feel how
- much I have been to blame. I am not afraid of being overpowered
- by the impression. It will pass away soon enough.”
-
- “Do you suppose them to be in London?”
-
- “Yes; where else can they be so well concealed?”
-
- “And Lydia used to want to go to London,” added Kitty.
-
- “She is happy then,” said her father drily; “and her residence
- there will probably be of some duration.”
-
- Then after a short silence he continued:
-
- “Lizzy, I bear you no ill-will for being justified in your advice
- to me last May, which, considering the event, shows some
- greatness of mind.”
-
- They were interrupted by Miss Bennet, who came to fetch her
- mother’s tea.
-
- “This is a parade,” he cried, “which does one good; it gives such
- an elegance to misfortune! Another day I will do the same; I will
- sit in my library, in my nightcap and powdering gown, and give as
- much trouble as I can; or, perhaps, I may defer it till Kitty
- runs away.”
-
- “I am not going to run away, papa,” said Kitty fretfully. “If _I_
- should ever go to Brighton, I would behave better than Lydia.”
-
- “_You_ go to Brighton. I would not trust you so near it as
- Eastbourne for fifty pounds! No, Kitty, I have at last learnt to
- be cautious, and you will feel the effects of it. No officer is
- ever to enter into my house again, nor even to pass through the
- village. Balls will be absolutely prohibited, unless you stand up
- with one of your sisters. And you are never to stir out of doors
- till you can prove that you have spent ten minutes of every day
- in a rational manner.”
-
- Kitty, who took all these threats in a serious light, began to
- cry.
-
- “Well, well,” said he, “do not make yourself unhappy. If you are
- a good girl for the next ten years, I will take you to a review
- at the end of them.”
-
-
-
-
- Chapter 49
-
- Two days after Mr. Bennet’s return, as Jane and Elizabeth were
- walking together in the shrubbery behind the house, they saw the
- housekeeper coming towards them, and, concluding that she came to
- call them to their mother, went forward to meet her; but, instead
- of the expected summons, when they approached her, she said to
- Miss Bennet, “I beg your pardon, madam, for interrupting you, but
- I was in hopes you might have got some good news from town, so I
- took the liberty of coming to ask.”
-
- “What do you mean, Hill? We have heard nothing from town.”
-
- “Dear madam,” cried Mrs. Hill, in great astonishment, “don’t you
- know there is an express come for master from Mr. Gardiner? He
- has been here this half-hour, and master has had a letter.”
-
- Away ran the girls, too eager to get in to have time for speech.
- They ran through the vestibule into the breakfast-room; from
- thence to the library; their father was in neither; and they were
- on the point of seeking him up stairs with their mother, when
- they were met by the butler, who said:
-
- “If you are looking for my master, ma’am, he is walking towards
- the little copse.”
-
- Upon this information, they instantly passed through the hall
- once more, and ran across the lawn after their father, who was
- deliberately pursuing his way towards a small wood on one side of
- the paddock.
-
- Jane, who was not so light nor so much in the habit of running as
- Elizabeth, soon lagged behind, while her sister, panting for
- breath, came up with him, and eagerly cried out:
-
- “Oh, papa, what news—what news? Have you heard from my uncle?”
-
- “Yes I have had a letter from him by express.”
-
- “Well, and what news does it bring—good or bad?”
-
- “What is there of good to be expected?” said he, taking the
- letter from his pocket. “But perhaps you would like to read it.”
-
- Elizabeth impatiently caught it from his hand. Jane now came up.
-
- “Read it aloud,” said their father, “for I hardly know myself
- what it is about.”
-
- “Gracechurch Street, _Monday, August_ 2.
-
- “My dear Brother,
- “At last I am able to send you some tidings of my niece, and such
- as, upon the whole, I hope it will give you satisfaction. Soon
- after you left me on Saturday, I was fortunate enough to find out
- in what part of London they were. The particulars I reserve till
- we meet; it is enough to know they are discovered. I have seen
- them both—”
- “Then it is as I always hoped,” cried Jane; “they are
- married!”
-
- Elizabeth read on:
-
- “I have seen them both. They are not married, nor can I find
- there was any intention of being so; but if you are willing to
- perform the engagements which I have ventured to make on your
- side, I hope it will not be long before they are. All that is
- required of you is, to assure to your daughter, by settlement,
- her equal share of the five thousand pounds secured among your
- children after the decease of yourself and my sister; and,
- moreover, to enter into an engagement of allowing her, during
- your life, one hundred pounds per annum. These are conditions
- which, considering everything, I had no hesitation in complying
- with, as far as I thought myself privileged, for you. I shall
- send this by express, that no time may be lost in bringing me
- your answer. You will easily comprehend, from these particulars,
- that Mr. Wickham’s circumstances are not so hopeless as they are
- generally believed to be. The world has been deceived in that
- respect; and I am happy to say there will be some little money,
- even when all his debts are discharged, to settle on my niece, in
- addition to her own fortune. If, as I conclude will be the case,
- you send me full powers to act in your name throughout the whole
- of this business, I will immediately give directions to
- Haggerston for preparing a proper settlement. There will not be
- the smallest occasion for your coming to town again; therefore
- stay quiet at Longbourn, and depend on my diligence and care.
- Send back your answer as fast as you can, and be careful to write
- explicitly. We have judged it best that my niece should be
- married from this house, of which I hope you will approve. She
- comes to us to-day. I shall write again as soon as anything more
- is determined on. Yours, etc.,
-
- “EDW. GARDINER.”
-
- “Is it possible?” cried Elizabeth, when she had finished. “Can it
- be possible that he will marry her?”
-
- “Wickham is not so undeserving, then, as we thought him,” said
- her sister. “My dear father, I congratulate you.”
-
- “And have you answered the letter?” cried Elizabeth.
-
- “No; but it must be done soon.”
-
- Most earnestly did she then entreat him to lose no more time
- before he wrote.
-
- “Oh! my dear father,” she cried, “come back and write
- immediately. Consider how important every moment is in such a
- case.”
-
- “Let me write for you,” said Jane, “if you dislike the trouble
- yourself.”
-
- “I dislike it very much,” he replied; “but it must be done.”
-
- And so saying, he turned back with them, and walked towards the
- house.
-
- “And may I ask—” said Elizabeth; “but the terms, I suppose, must
- be complied with.”
-
- “Complied with! I am only ashamed of his asking so little.”
-
- “And they _must_ marry! Yet he is _such_ a man!”
-
- “Yes, yes, they must marry. There is nothing else to be done. But
- there are two things that I want very much to know; one is, how
- much money your uncle has laid down to bring it about; and the
- other, how am I ever to pay him.”
-
- “Money! My uncle!” cried Jane, “what do you mean, sir?”
-
- “I mean, that no man in his senses would marry Lydia on so slight
- a temptation as one hundred a year during my life, and fifty
- after I am gone.”
-
- “That is very true,” said Elizabeth; “though it had not occurred
- to me before. His debts to be discharged, and something still to
- remain! Oh! it must be my uncle’s doings! Generous, good man, I
- am afraid he has distressed himself. A small sum could not do all
- this.”
-
- “No,” said her father; “Wickham’s a fool if he takes her with a
- farthing less than ten thousand pounds. I should be sorry to
- think so ill of him, in the very beginning of our relationship.”
-
- “Ten thousand pounds! Heaven forbid! How is half such a sum to be
- repaid?”
-
- Mr. Bennet made no answer, and each of them, deep in thought,
- continued silent till they reached the house. Their father then
- went on to the library to write, and the girls walked into the
- breakfast-room.
-
- “And they are really to be married!” cried Elizabeth, as soon as
- they were by themselves. “How strange this is! And for _this_ we
- are to be thankful. That they should marry, small as is their
- chance of happiness, and wretched as is his character, we are
- forced to rejoice. Oh, Lydia!”
-
- “I comfort myself with thinking,” replied Jane, “that he
- certainly would not marry Lydia if he had not a real regard for
- her. Though our kind uncle has done something towards clearing
- him, I cannot believe that ten thousand pounds, or anything like
- it, has been advanced. He has children of his own, and may have
- more. How could he spare half ten thousand pounds?”
-
- “If he were ever able to learn what Wickham’s debts have been,”
- said Elizabeth, “and how much is settled on his side on our
- sister, we shall exactly know what Mr. Gardiner has done for
- them, because Wickham has not sixpence of his own. The kindness
- of my uncle and aunt can never be requited. Their taking her
- home, and affording her their personal protection and
- countenance, is such a sacrifice to her advantage as years of
- gratitude cannot enough acknowledge. By this time she is actually
- with them! If such goodness does not make her miserable now, she
- will never deserve to be happy! What a meeting for her, when she
- first sees my aunt!”
-
- “We must endeavour to forget all that has passed on either side,”
- said Jane: “I hope and trust they will yet be happy. His
- consenting to marry her is a proof, I will believe, that he is
- come to a right way of thinking. Their mutual affection will
- steady them; and I flatter myself they will settle so quietly,
- and live in so rational a manner, as may in time make their past
- imprudence forgotten.”
-
- “Their conduct has been such,” replied Elizabeth, “as neither
- you, nor I, nor anybody can ever forget. It is useless to talk of
- it.”
-
- It now occurred to the girls that their mother was in all
- likelihood perfectly ignorant of what had happened. They went to
- the library, therefore, and asked their father whether he would
- not wish them to make it known to her. He was writing and,
- without raising his head, coolly replied:
-
- “Just as you please.”
-
- “May we take my uncle’s letter to read to her?”
-
- “Take whatever you like, and get away.”
-
- Elizabeth took the letter from his writing-table, and they went
- up stairs together. Mary and Kitty were both with Mrs. Bennet:
- one communication would, therefore, do for all. After a slight
- preparation for good news, the letter was read aloud. Mrs. Bennet
- could hardly contain herself. As soon as Jane had read Mr.
- Gardiner’s hope of Lydia’s being soon married, her joy burst
- forth, and every following sentence added to its exuberance. She
- was now in an irritation as violent from delight, as she had ever
- been fidgety from alarm and vexation. To know that her daughter
- would be married was enough. She was disturbed by no fear for her
- felicity, nor humbled by any remembrance of her misconduct.
-
- “My dear, dear Lydia!” she cried. “This is delightful indeed! She
- will be married! I shall see her again! She will be married at
- sixteen! My good, kind brother! I knew how it would be. I knew he
- would manage everything! How I long to see her! and to see dear
- Wickham too! But the clothes, the wedding clothes! I will write
- to my sister Gardiner about them directly. Lizzy, my dear, run
- down to your father, and ask him how much he will give her. Stay,
- stay, I will go myself. Ring the bell, Kitty, for Hill. I will
- put on my things in a moment. My dear, dear Lydia! How merry we
- shall be together when we meet!”
-
- Her eldest daughter endeavoured to give some relief to the
- violence of these transports, by leading her thoughts to the
- obligations which Mr. Gardiner’s behaviour laid them all under.
-
- “For we must attribute this happy conclusion,” she added, “in a
- great measure to his kindness. We are persuaded that he has
- pledged himself to assist Mr. Wickham with money.”
-
- “Well,” cried her mother, “it is all very right; who should do it
- but her own uncle? If he had not had a family of his own, I and
- my children must have had all his money, you know; and it is the
- first time we have ever had anything from him, except a few
- presents. Well! I am so happy! In a short time I shall have a
- daughter married. Mrs. Wickham! How well it sounds! And she was
- only sixteen last June. My dear Jane, I am in such a flutter,
- that I am sure I can’t write; so I will dictate, and you write
- for me. We will settle with your father about the money
- afterwards; but the things should be ordered immediately.”
-
- She was then proceeding to all the particulars of calico, muslin,
- and cambric, and would shortly have dictated some very plentiful
- orders, had not Jane, though with some difficulty, persuaded her
- to wait till her father was at leisure to be consulted. One day’s
- delay, she observed, would be of small importance; and her mother
- was too happy to be quite so obstinate as usual. Other schemes,
- too, came into her head.
-
- “I will go to Meryton,” said she, “as soon as I am dressed, and
- tell the good, good news to my sister Philips. And as I come
- back, I can call on Lady Lucas and Mrs. Long. Kitty, run down and
- order the carriage. An airing would do me a great deal of good, I
- am sure. Girls, can I do anything for you in Meryton? Oh! Here
- comes Hill! My dear Hill, have you heard the good news? Miss
- Lydia is going to be married; and you shall all have a bowl of
- punch to make merry at her wedding.”
-
- Mrs. Hill began instantly to express her joy. Elizabeth received
- her congratulations amongst the rest, and then, sick of this
- folly, took refuge in her own room, that she might think with
- freedom.
-
- Poor Lydia’s situation must, at best, be bad enough; but that it
- was no worse, she had need to be thankful. She felt it so; and
- though, in looking forward, neither rational happiness nor
- worldly prosperity could be justly expected for her sister, in
- looking back to what they had feared, only two hours ago, she
- felt all the advantages of what they had gained.
-
-
-
-
- Chapter 50
-
- Mr. Bennet had very often wished before this period of his life
- that, instead of spending his whole income, he had laid by an
- annual sum for the better provision of his children, and of his
- wife, if she survived him. He now wished it more than ever. Had
- he done his duty in that respect, Lydia need not have been
- indebted to her uncle for whatever of honour or credit could now
- be purchased for her. The satisfaction of prevailing on one of
- the most worthless young men in Great Britain to be her husband
- might then have rested in its proper place.
-
- He was seriously concerned that a cause of so little advantage to
- anyone should be forwarded at the sole expense of his
- brother-in-law, and he was determined, if possible, to find out
- the extent of his assistance, and to discharge the obligation as
- soon as he could.
-
- When first Mr. Bennet had married, economy was held to be
- perfectly useless, for, of course, they were to have a son. The
- son was to join in cutting off the entail, as soon as he should
- be of age, and the widow and younger children would by that means
- be provided for. Five daughters successively entered the world,
- but yet the son was to come; and Mrs. Bennet, for many years
- after Lydia’s birth, had been certain that he would. This event
- had at last been despaired of, but it was then too late to be
- saving. Mrs. Bennet had no turn for economy, and her husband’s
- love of independence had alone prevented their exceeding their
- income.
-
- Five thousand pounds was settled by marriage articles on Mrs.
- Bennet and the children. But in what proportions it should be
- divided amongst the latter depended on the will of the parents.
- This was one point, with regard to Lydia, at least, which was now
- to be settled, and Mr. Bennet could have no hesitation in
- acceding to the proposal before him. In terms of grateful
- acknowledgment for the kindness of his brother, though expressed
- most concisely, he then delivered on paper his perfect
- approbation of all that was done, and his willingness to fulfil
- the engagements that had been made for him. He had never before
- supposed that, could Wickham be prevailed on to marry his
- daughter, it would be done with so little inconvenience to
- himself as by the present arrangement. He would scarcely be ten
- pounds a year the loser by the hundred that was to be paid them;
- for, what with her board and pocket allowance, and the continual
- presents in money which passed to her through her mother’s hands,
- Lydia’s expenses had been very little within that sum.
-
- That it would be done with such trifling exertion on his side,
- too, was another very welcome surprise; for his wish at present
- was to have as little trouble in the business as possible. When
- the first transports of rage which had produced his activity in
- seeking her were over, he naturally returned to all his former
- indolence. His letter was soon dispatched; for, though dilatory
- in undertaking business, he was quick in its execution. He begged
- to know further particulars of what he was indebted to his
- brother, but was too angry with Lydia to send any message to her.
-
- The good news spread quickly through the house, and with
- proportionate speed through the neighbourhood. It was borne in
- the latter with decent philosophy. To be sure, it would have been
- more for the advantage of conversation had Miss Lydia Bennet come
- upon the town; or, as the happiest alternative, been secluded
- from the world, in some distant farmhouse. But there was much to
- be talked of in marrying her; and the good-natured wishes for her
- well-doing which had proceeded before from all the spiteful old
- ladies in Meryton lost but a little of their spirit in this
- change of circumstances, because with such an husband her misery
- was considered certain.
-
- It was a fortnight since Mrs. Bennet had been downstairs; but on
- this happy day she again took her seat at the head of her table,
- and in spirits oppressively high. No sentiment of shame gave a
- damp to her triumph. The marriage of a daughter, which had been
- the first object of her wishes since Jane was sixteen, was now on
- the point of accomplishment, and her thoughts and her words ran
- wholly on those attendants of elegant nuptials, fine muslins, new
- carriages, and servants. She was busily searching through the
- neighbourhood for a proper situation for her daughter, and,
- without knowing or considering what their income might be,
- rejected many as deficient in size and importance.
-
- “Haye Park might do,” said she, “if the Gouldings could quit
- it—or the great house at Stoke, if the drawing-room were larger;
- but Ashworth is too far off! I could not bear to have her ten
- miles from me; and as for Pulvis Lodge, the attics are dreadful.”
-
- Her husband allowed her to talk on without interruption while the
- servants remained. But when they had withdrawn, he said to her:
- “Mrs. Bennet, before you take any or all of these houses for your
- son and daughter, let us come to a right understanding. Into
- _one_ house in this neighbourhood they shall never have
- admittance. I will not encourage the impudence of either, by
- receiving them at Longbourn.”
-
- A long dispute followed this declaration; but Mr. Bennet was
- firm. It soon led to another; and Mrs. Bennet found, with
- amazement and horror, that her husband would not advance a guinea
- to buy clothes for his daughter. He protested that she should
- receive from him no mark of affection whatever on the occasion.
- Mrs. Bennet could hardly comprehend it. That his anger could be
- carried to such a point of inconceivable resentment as to refuse
- his daughter a privilege without which her marriage would
- scarcely seem valid, exceeded all she could believe possible. She
- was more alive to the disgrace which her want of new clothes must
- reflect on her daughter’s nuptials, than to any sense of shame at
- her eloping and living with Wickham a fortnight before they took
- place.
-
- Elizabeth was now most heartily sorry that she had, from the
- distress of the moment, been led to make Mr. Darcy acquainted
- with their fears for her sister; for since her marriage would so
- shortly give the proper termination to the elopement, they might
- hope to conceal its unfavourable beginning from all those who
- were not immediately on the spot.
-
- She had no fear of its spreading farther through his means. There
- were few people on whose secrecy she would have more confidently
- depended; but, at the same time, there was no one whose knowledge
- of a sister’s frailty would have mortified her so much—not,
- however, from any fear of disadvantage from it individually to
- herself, for, at any rate, there seemed a gulf impassable between
- them. Had Lydia’s marriage been concluded on the most honourable
- terms, it was not to be supposed that Mr. Darcy would connect
- himself with a family where, to every other objection, would now
- be added an alliance and relationship of the nearest kind with a
- man whom he so justly scorned.
-
- From such a connection she could not wonder that he would shrink.
- The wish of procuring her regard, which she had assured herself
- of his feeling in Derbyshire, could not in rational expectation
- survive such a blow as this. She was humbled, she was grieved;
- she repented, though she hardly knew of what. She became jealous
- of his esteem, when she could no longer hope to be benefited by
- it. She wanted to hear of him, when there seemed the least chance
- of gaining intelligence. She was convinced that she could have
- been happy with him, when it was no longer likely they should
- meet.
-
- What a triumph for him, as she often thought, could he know that
- the proposals which she had proudly spurned only four months ago,
- would now have been most gladly and gratefully received! He was
- as generous, she doubted not, as the most generous of his sex;
- but while he was mortal, there must be a triumph.
-
- She began now to comprehend that he was exactly the man who, in
- disposition and talents, would most suit her. His understanding
- and temper, though unlike her own, would have answered all her
- wishes. It was an union that must have been to the advantage of
- both; by her ease and liveliness, his mind might have been
- softened, his manners improved; and from his judgement,
- information, and knowledge of the world, she must have received
- benefit of greater importance.
-
- But no such happy marriage could now teach the admiring multitude
- what connubial felicity really was. An union of a different
- tendency, and precluding the possibility of the other, was soon
- to be formed in their family.
-
- How Wickham and Lydia were to be supported in tolerable
- independence, she could not imagine. But how little of permanent
- happiness could belong to a couple who were only brought together
- because their passions were stronger than their virtue, she could
- easily conjecture.
-
- Mr. Gardiner soon wrote again to his brother. To Mr. Bennet’s
- acknowledgments he briefly replied, with assurance of his
- eagerness to promote the welfare of any of his family; and
- concluded with entreaties that the subject might never be
- mentioned to him again. The principal purport of his letter was
- to inform them that Mr. Wickham had resolved on quitting the
- militia.
-
- “It was greatly my wish that he should do so,” he added, “as soon
- as his marriage was fixed on. And I think you will agree with me,
- in considering the removal from that corps as highly advisable,
- both on his account and my niece’s. It is Mr. Wickham’s intention
- to go into the regulars; and among his former friends, there are
- still some who are able and willing to assist him in the army. He
- has the promise of an ensigncy in General ——’s regiment, now
- quartered in the North. It is an advantage to have it so far from
- this part of the kingdom. He promises fairly; and I hope among
- different people, where they may each have a character to
- preserve, they will both be more prudent. I have written to
- Colonel Forster, to inform him of our present arrangements, and
- to request that he will satisfy the various creditors of Mr.
- Wickham in and near Brighton, with assurances of speedy payment,
- for which I have pledged myself. And will you give yourself the
- trouble of carrying similar assurances to his creditors in
- Meryton, of whom I shall subjoin a list according to his
- information? He has given in all his debts; I hope at least he
- has not deceived us. Haggerston has our directions, and all will
- be completed in a week. They will then join his regiment, unless
- they are first invited to Longbourn; and I understand from Mrs.
- Gardiner, that my niece is very desirous of seeing you all before
- she leaves the South. She is well, and begs to be dutifully
- remembered to you and her mother.—Yours, etc.,
-
- “E. GARDINER.”
-
- Mr. Bennet and his daughters saw all the advantages of Wickham’s
- removal from the ——shire as clearly as Mr. Gardiner could do. But
- Mrs. Bennet was not so well pleased with it. Lydia’s being
- settled in the North, just when she had expected most pleasure
- and pride in her company, for she had by no means given up her
- plan of their residing in Hertfordshire, was a severe
- disappointment; and, besides, it was such a pity that Lydia
- should be taken from a regiment where she was acquainted with
- everybody, and had so many favourites.
-
- “She is so fond of Mrs. Forster,” said she, “it will be quite
- shocking to send her away! And there are several of the young
- men, too, that she likes very much. The officers may not be so
- pleasant in General ——’s regiment.”
-
- His daughter’s request, for such it might be considered, of being
- admitted into her family again before she set off for the North,
- received at first an absolute negative. But Jane and Elizabeth,
- who agreed in wishing, for the sake of their sister’s feelings
- and consequence, that she should be noticed on her marriage by
- her parents, urged him so earnestly yet so rationally and so
- mildly, to receive her and her husband at Longbourn, as soon as
- they were married, that he was prevailed on to think as they
- thought, and act as they wished. And their mother had the
- satisfaction of knowing that she would be able to show her
- married daughter in the neighbourhood before she was banished to
- the North. When Mr. Bennet wrote again to his brother, therefore,
- he sent his permission for them to come; and it was settled, that
- as soon as the ceremony was over, they should proceed to
- Longbourn. Elizabeth was surprised, however, that Wickham should
- consent to such a scheme, and had she consulted only her own
- inclination, any meeting with him would have been the last object
- of her wishes.
-
-
-
-
- Chapter 51
-
- Their sister’s wedding day arrived; and Jane and Elizabeth felt
- for her probably more than she felt for herself. The carriage was
- sent to meet them at ——, and they were to return in it by
- dinner-time. Their arrival was dreaded by the elder Miss Bennets,
- and Jane more especially, who gave Lydia the feelings which would
- have attended herself, had _she_ been the culprit, and was
- wretched in the thought of what her sister must endure.
-
- They came. The family were assembled in the breakfast room to
- receive them. Smiles decked the face of Mrs. Bennet as the
- carriage drove up to the door; her husband looked impenetrably
- grave; her daughters, alarmed, anxious, uneasy.
-
- Lydia’s voice was heard in the vestibule; the door was thrown
- open, and she ran into the room. Her mother stepped forwards,
- embraced her, and welcomed her with rapture; gave her hand, with
- an affectionate smile, to Wickham, who followed his lady; and
- wished them both joy with an alacrity which shewed no doubt of
- their happiness.
-
- Their reception from Mr. Bennet, to whom they then turned, was
- not quite so cordial. His countenance rather gained in austerity;
- and he scarcely opened his lips. The easy assurance of the young
- couple, indeed, was enough to provoke him. Elizabeth was
- disgusted, and even Miss Bennet was shocked. Lydia was Lydia
- still; untamed, unabashed, wild, noisy, and fearless. She turned
- from sister to sister, demanding their congratulations; and when
- at length they all sat down, looked eagerly round the room, took
- notice of some little alteration in it, and observed, with a
- laugh, that it was a great while since she had been there.
-
- Wickham was not at all more distressed than herself, but his
- manners were always so pleasing, that had his character and his
- marriage been exactly what they ought, his smiles and his easy
- address, while he claimed their relationship, would have
- delighted them all. Elizabeth had not before believed him quite
- equal to such assurance; but she sat down, resolving within
- herself to draw no limits in future to the impudence of an
- impudent man. _She_ blushed, and Jane blushed; but the cheeks of
- the two who caused their confusion suffered no variation of
- colour.
-
- There was no want of discourse. The bride and her mother could
- neither of them talk fast enough; and Wickham, who happened to
- sit near Elizabeth, began inquiring after his acquaintance in
- that neighbourhood, with a good humoured ease which she felt very
- unable to equal in her replies. They seemed each of them to have
- the happiest memories in the world. Nothing of the past was
- recollected with pain; and Lydia led voluntarily to subjects
- which her sisters would not have alluded to for the world.
-
- “Only think of its being three months,” she cried, “since I went
- away; it seems but a fortnight I declare; and yet there have been
- things enough happened in the time. Good gracious! when I went
- away, I am sure I had no more idea of being married till I came
- back again! though I thought it would be very good fun if I was.”
-
- Her father lifted up his eyes. Jane was distressed. Elizabeth
- looked expressively at Lydia; but she, who never heard nor saw
- anything of which she chose to be insensible, gaily continued,
- “Oh! mamma, do the people hereabouts know I am married to-day? I
- was afraid they might not; and we overtook William Goulding in
- his curricle, so I was determined he should know it, and so I let
- down the side-glass next to him, and took off my glove, and let
- my hand just rest upon the window frame, so that he might see the
- ring, and then I bowed and smiled like anything.”
-
- Elizabeth could bear it no longer. She got up, and ran out of the
- room; and returned no more, till she heard them passing through
- the hall to the dining parlour. She then joined them soon enough
- to see Lydia, with anxious parade, walk up to her mother’s right
- hand, and hear her say to her eldest sister, “Ah! Jane, I take
- your place now, and you must go lower, because I am a married
- woman.”
-
- It was not to be supposed that time would give Lydia that
- embarrassment from which she had been so wholly free at first.
- Her ease and good spirits increased. She longed to see Mrs.
- Phillips, the Lucases, and all their other neighbours, and to
- hear herself called “Mrs. Wickham” by each of them; and in the
- mean time, she went after dinner to show her ring, and boast of
- being married, to Mrs. Hill and the two housemaids.
-
- “Well, mamma,” said she, when they were all returned to the
- breakfast room, “and what do you think of my husband? Is not he a
- charming man? I am sure my sisters must all envy me. I only hope
- they may have half my good luck. They must all go to Brighton.
- That is the place to get husbands. What a pity it is, mamma, we
- did not all go.”
-
- “Very true; and if I had my will, we should. But my dear Lydia, I
- don’t at all like your going such a way off. Must it be so?”
-
- “Oh, lord! yes;—there is nothing in that. I shall like it of all
- things. You and papa, and my sisters, must come down and see us.
- We shall be at Newcastle all the winter, and I dare say there
- will be some balls, and I will take care to get good partners for
- them all.”
-
- “I should like it beyond anything!” said her mother.
-
- “And then when you go away, you may leave one or two of my
- sisters behind you; and I dare say I shall get husbands for them
- before the winter is over.”
-
- “I thank you for my share of the favour,” said Elizabeth; “but I
- do not particularly like your way of getting husbands.”
-
- Their visitors were not to remain above ten days with them. Mr.
- Wickham had received his commission before he left London, and he
- was to join his regiment at the end of a fortnight.
-
- No one but Mrs. Bennet regretted that their stay would be so
- short; and she made the most of the time by visiting about with
- her daughter, and having very frequent parties at home. These
- parties were acceptable to all; to avoid a family circle was even
- more desirable to such as did think, than such as did not.
-
- Wickham’s affection for Lydia was just what Elizabeth had
- expected to find it; not equal to Lydia’s for him. She had
- scarcely needed her present observation to be satisfied, from the
- reason of things, that their elopement had been brought on by the
- strength of her love, rather than by his; and she would have
- wondered why, without violently caring for her, he chose to elope
- with her at all, had she not felt certain that his flight was
- rendered necessary by distress of circumstances; and if that were
- the case, he was not the young man to resist an opportunity of
- having a companion.
-
- Lydia was exceedingly fond of him. He was her dear Wickham on
- every occasion; no one was to be put in competition with him. He
- did every thing best in the world; and she was sure he would kill
- more birds on the first of September, than any body else in the
- country.
-
- One morning, soon after their arrival, as she was sitting with
- her two elder sisters, she said to Elizabeth:
-
- “Lizzy, I never gave _you_ an account of my wedding, I believe.
- You were not by, when I told mamma and the others all about it.
- Are not you curious to hear how it was managed?”
-
- “No really,” replied Elizabeth; “I think there cannot be too
- little said on the subject.”
-
- “La! You are so strange! But I must tell you how it went off. We
- were married, you know, at St. Clement’s, because Wickham’s
- lodgings were in that parish. And it was settled that we should
- all be there by eleven o’clock. My uncle and aunt and I were to
- go together; and the others were to meet us at the church. Well,
- Monday morning came, and I was in such a fuss! I was so afraid,
- you know, that something would happen to put it off, and then I
- should have gone quite distracted. And there was my aunt, all the
- time I was dressing, preaching and talking away just as if she
- was reading a sermon. However, I did not hear above one word in
- ten, for I was thinking, you may suppose, of my dear Wickham. I
- longed to know whether he would be married in his blue coat.”
-
- “Well, and so we breakfasted at ten as usual; I thought it would
- never be over; for, by the bye, you are to understand, that my
- uncle and aunt were horrid unpleasant all the time I was with
- them. If you’ll believe me, I did not once put my foot out of
- doors, though I was there a fortnight. Not one party, or scheme,
- or anything. To be sure London was rather thin, but, however, the
- Little Theatre was open. Well, and so just as the carriage came
- to the door, my uncle was called away upon business to that
- horrid man Mr. Stone. And then, you know, when once they get
- together, there is no end of it. Well, I was so frightened I did
- not know what to do, for my uncle was to give me away; and if we
- were beyond the hour, we could not be married all day. But,
- luckily, he came back again in ten minutes’ time, and then we all
- set out. However, I recollected afterwards that if he _had_ been
- prevented going, the wedding need not be put off, for Mr. Darcy
- might have done as well.”
-
- “Mr. Darcy!” repeated Elizabeth, in utter amazement.
-
- “Oh, yes!—he was to come there with Wickham, you know. But
- gracious me! I quite forgot! I ought not to have said a word
- about it. I promised them so faithfully! What will Wickham say?
- It was to be such a secret!”
-
- “If it was to be secret,” said Jane, “say not another word on the
- subject. You may depend upon my seeking no further.”
-
- “Oh! certainly,” said Elizabeth, though burning with curiosity;
- “we will ask you no questions.”
-
- “Thank you,” said Lydia, “for if you did, I should certainly tell
- you all, and then Wickham would be angry.”
-
- On such encouragement to ask, Elizabeth was forced to put it out
- of her power, by running away.
-
- But to live in ignorance on such a point was impossible; or at
- least it was impossible not to try for information. Mr. Darcy had
- been at her sister’s wedding. It was exactly a scene, and exactly
- among people, where he had apparently least to do, and least
- temptation to go. Conjectures as to the meaning of it, rapid and
- wild, hurried into her brain; but she was satisfied with none.
- Those that best pleased her, as placing his conduct in the
- noblest light, seemed most improbable. She could not bear such
- suspense; and hastily seizing a sheet of paper, wrote a short
- letter to her aunt, to request an explanation of what Lydia had
- dropt, if it were compatible with the secrecy which had been
- intended.
-
- “You may readily comprehend,” she added, “what my curiosity must
- be to know how a person unconnected with any of us, and
- (comparatively speaking) a stranger to our family, should have
- been amongst you at such a time. Pray write instantly, and let me
- understand it—unless it is, for very cogent reasons, to remain in
- the secrecy which Lydia seems to think necessary; and then I must
- endeavour to be satisfied with ignorance.”
-
- “Not that I _shall_, though,” she added to herself, as she
- finished the letter; “and my dear aunt, if you do not tell me in
- an honourable manner, I shall certainly be reduced to tricks and
- stratagems to find it out.”
-
- Jane’s delicate sense of honour would not allow her to speak to
- Elizabeth privately of what Lydia had let fall; Elizabeth was
- glad of it;—till it appeared whether her inquiries would receive
- any satisfaction, she had rather be without a confidante.
-
-
-
-
- Chapter 52
-
- Elizabeth had the satisfaction of receiving an answer to her
- letter as soon as she possibly could. She was no sooner in
- possession of it than, hurrying into the little copse, where she
- was least likely to be interrupted, she sat down on one of the
- benches and prepared to be happy; for the length of the letter
- convinced her that it did not contain a denial.
-
- “Gracechurch Street, _Sept_. 6.
-
- “My dear Niece,
-
- “I have just received your letter, and shall devote this whole
- morning to answering it, as I foresee that a _little_ writing
- will not comprise what I have to tell you. I must confess myself
- surprised by your application; I did not expect it from _you_.
- Don’t think me angry, however, for I only mean to let you know
- that I had not imagined such inquiries to be necessary on _your_
- side. If you do not choose to understand me, forgive my
- impertinence. Your uncle is as much surprised as I am—and nothing
- but the belief of your being a party concerned would have allowed
- him to act as he has done. But if you are really innocent and
- ignorant, I must be more explicit.
-
- “On the very day of my coming home from Longbourn, your uncle had
- a most unexpected visitor. Mr. Darcy called, and was shut up with
- him several hours. It was all over before I arrived; so my
- curiosity was not so dreadfully racked as _yours_ seems to have
- been. He came to tell Mr. Gardiner that he had found out where
- your sister and Mr. Wickham were, and that he had seen and talked
- with them both; Wickham repeatedly, Lydia once. From what I can
- collect, he left Derbyshire only one day after ourselves, and
- came to town with the resolution of hunting for them. The motive
- professed was his conviction of its being owing to himself that
- Wickham’s worthlessness had not been so well known as to make it
- impossible for any young woman of character to love or confide in
- him. He generously imputed the whole to his mistaken pride, and
- confessed that he had before thought it beneath him to lay his
- private actions open to the world. His character was to speak for
- itself. He called it, therefore, his duty to step forward, and
- endeavour to remedy an evil which had been brought on by himself.
- If he _had another_ motive, I am sure it would never disgrace
- him. He had been some days in town, before he was able to
- discover them; but he had something to direct his search, which
- was more than _we_ had; and the consciousness of this was another
- reason for his resolving to follow us.
-
- “There is a lady, it seems, a Mrs. Younge, who was some time ago
- governess to Miss Darcy, and was dismissed from her charge on
- some cause of disapprobation, though he did not say what. She
- then took a large house in Edward-street, and has since
- maintained herself by letting lodgings. This Mrs. Younge was, he
- knew, intimately acquainted with Wickham; and he went to her for
- intelligence of him as soon as he got to town. But it was two or
- three days before he could get from her what he wanted. She would
- not betray her trust, I suppose, without bribery and corruption,
- for she really did know where her friend was to be found. Wickham
- indeed had gone to her on their first arrival in London, and had
- she been able to receive them into her house, they would have
- taken up their abode with her. At length, however, our kind
- friend procured the wished-for direction. They were in —— street.
- He saw Wickham, and afterwards insisted on seeing Lydia. His
- first object with her, he acknowledged, had been to persuade her
- to quit her present disgraceful situation, and return to her
- friends as soon as they could be prevailed on to receive her,
- offering his assistance, as far as it would go. But he found
- Lydia absolutely resolved on remaining where she was. She cared
- for none of her friends; she wanted no help of his; she would not
- hear of leaving Wickham. She was sure they should be married some
- time or other, and it did not much signify when. Since such were
- her feelings, it only remained, he thought, to secure and
- expedite a marriage, which, in his very first conversation with
- Wickham, he easily learnt had never been _his_ design. He
- confessed himself obliged to leave the regiment, on account of
- some debts of honour, which were very pressing; and scrupled not
- to lay all the ill-consequences of Lydia’s flight on her own
- folly alone. He meant to resign his commission immediately; and
- as to his future situation, he could conjecture very little about
- it. He must go somewhere, but he did not know where, and he knew
- he should have nothing to live on.
-
- “Mr. Darcy asked him why he had not married your sister at once.
- Though Mr. Bennet was not imagined to be very rich, he would have
- been able to do something for him, and his situation must have
- been benefited by marriage. But he found, in reply to this
- question, that Wickham still cherished the hope of more
- effectually making his fortune by marriage in some other country.
- Under such circumstances, however, he was not likely to be proof
- against the temptation of immediate relief.
-
- “They met several times, for there was much to be discussed.
- Wickham of course wanted more than he could get; but at length
- was reduced to be reasonable.
-
- “Everything being settled between _them_, Mr. Darcy’s next step
- was to make your uncle acquainted with it, and he first called in
- Gracechurch street the evening before I came home. But Mr.
- Gardiner could not be seen, and Mr. Darcy found, on further
- inquiry, that your father was still with him, but would quit town
- the next morning. He did not judge your father to be a person
- whom he could so properly consult as your uncle, and therefore
- readily postponed seeing him till after the departure of the
- former. He did not leave his name, and till the next day it was
- only known that a gentleman had called on business.
-
- “On Saturday he came again. Your father was gone, your uncle at
- home, and, as I said before, they had a great deal of talk
- together.
-
- “They met again on Sunday, and then _I_ saw him too. It was not
- all settled before Monday: as soon as it was, the express was
- sent off to Longbourn. But our visitor was very obstinate. I
- fancy, Lizzy, that obstinacy is the real defect of his character,
- after all. He has been accused of many faults at different times,
- but _this_ is the true one. Nothing was to be done that he did
- not do himself; though I am sure (and I do not speak it to be
- thanked, therefore say nothing about it), your uncle would most
- readily have settled the whole.
-
- “They battled it together for a long time, which was more than
- either the gentleman or lady concerned in it deserved. But at
- last your uncle was forced to yield, and instead of being allowed
- to be of use to his niece, was forced to put up with only having
- the probable credit of it, which went sorely against the grain;
- and I really believe your letter this morning gave him great
- pleasure, because it required an explanation that would rob him
- of his borrowed feathers, and give the praise where it was due.
- But, Lizzy, this must go no farther than yourself, or Jane at
- most.
-
- “You know pretty well, I suppose, what has been done for the
- young people. His debts are to be paid, amounting, I believe, to
- considerably more than a thousand pounds, another thousand in
- addition to her own settled upon _her_, and his commission
- purchased. The reason why all this was to be done by him alone,
- was such as I have given above. It was owing to him, to his
- reserve and want of proper consideration, that Wickham’s
- character had been so misunderstood, and consequently that he had
- been received and noticed as he was. Perhaps there was some truth
- in _this_; though I doubt whether _his_ reserve, or _anybody’s_
- reserve, can be answerable for the event. But in spite of all
- this fine talking, my dear Lizzy, you may rest perfectly assured
- that your uncle would never have yielded, if we had not given him
- credit for _another interest_ in the affair.
-
- “When all this was resolved on, he returned again to his friends,
- who were still staying at Pemberley; but it was agreed that he
- should be in London once more when the wedding took place, and
- all money matters were then to receive the last finish.
-
- “I believe I have now told you every thing. It is a relation
- which you tell me is to give you great surprise; I hope at least
- it will not afford you any displeasure. Lydia came to us; and
- Wickham had constant admission to the house. _He_ was exactly
- what he had been, when I knew him in Hertfordshire; but I would
- not tell you how little I was satisfied with _her_ behaviour
- while she staid with us, if I had not perceived, by Jane’s letter
- last Wednesday, that her conduct on coming home was exactly of a
- piece with it, and therefore what I now tell you can give you no
- fresh pain. I talked to her repeatedly in the most serious
- manner, representing to her all the wickedness of what she had
- done, and all the unhappiness she had brought on her family. If
- she heard me, it was by good luck, for I am sure she did not
- listen. I was sometimes quite provoked, but then I recollected my
- dear Elizabeth and Jane, and for their sakes had patience with
- her.
-
- “Mr. Darcy was punctual in his return, and as Lydia informed you,
- attended the wedding. He dined with us the next day, and was to
- leave town again on Wednesday or Thursday. Will you be very angry
- with me, my dear Lizzy, if I take this opportunity of saying
- (what I was never bold enough to say before) how much I like him.
- His behaviour to us has, in every respect, been as pleasing as
- when we were in Derbyshire. His understanding and opinions all
- please me; he wants nothing but a little more liveliness, and
- _that_, if he marry _prudently_, his wife may teach him. I
- thought him very sly;—he hardly ever mentioned your name. But
- slyness seems the fashion.
-
- “Pray forgive me if I have been very presuming, or at least do
- not punish me so far as to exclude me from P. I shall never be
- quite happy till I have been all round the park. A low phaeton,
- with a nice little pair of ponies, would be the very thing.
-
- “But I must write no more. The children have been wanting me this
- half hour.
-
- “Yours, very sincerely,
- “M. GARDINER.”
-
- The contents of this letter threw Elizabeth into a flutter of
- spirits, in which it was difficult to determine whether pleasure
- or pain bore the greatest share. The vague and unsettled
- suspicions which uncertainty had produced of what Mr. Darcy might
- have been doing to forward her sister’s match, which she had
- feared to encourage as an exertion of goodness too great to be
- probable, and at the same time dreaded to be just, from the pain
- of obligation, were proved beyond their greatest extent to be
- true! He had followed them purposely to town, he had taken on
- himself all the trouble and mortification attendant on such a
- research; in which supplication had been necessary to a woman
- whom he must abominate and despise, and where he was reduced to
- meet, frequently meet, reason with, persuade, and finally bribe,
- the man whom he always most wished to avoid, and whose very name
- it was punishment to him to pronounce. He had done all this for a
- girl whom he could neither regard nor esteem. Her heart did
- whisper that he had done it for her. But it was a hope shortly
- checked by other considerations, and she soon felt that even her
- vanity was insufficient, when required to depend on his affection
- for her—for a woman who had already refused him—as able to
- overcome a sentiment so natural as abhorrence against
- relationship with Wickham. Brother-in-law of Wickham! Every kind
- of pride must revolt from the connection. He had, to be sure,
- done much. She was ashamed to think how much. But he had given a
- reason for his interference, which asked no extraordinary stretch
- of belief. It was reasonable that he should feel he had been
- wrong; he had liberality, and he had the means of exercising it;
- and though she would not place herself as his principal
- inducement, she could, perhaps, believe that remaining partiality
- for her might assist his endeavours in a cause where her peace of
- mind must be materially concerned. It was painful, exceedingly
- painful, to know that they were under obligations to a person who
- could never receive a return. They owed the restoration of Lydia,
- her character, every thing, to him. Oh! how heartily did she
- grieve over every ungracious sensation she had ever encouraged,
- every saucy speech she had ever directed towards him. For herself
- she was humbled; but she was proud of him. Proud that in a cause
- of compassion and honour, he had been able to get the better of
- himself. She read over her aunt’s commendation of him again and
- again. It was hardly enough; but it pleased her. She was even
- sensible of some pleasure, though mixed with regret, on finding
- how steadfastly both she and her uncle had been persuaded that
- affection and confidence subsisted between Mr. Darcy and herself.
-
- She was roused from her seat, and her reflections, by some one’s
- approach; and before she could strike into another path, she was
- overtaken by Wickham.
-
- “I am afraid I interrupt your solitary ramble, my dear sister?”
- said he, as he joined her.
-
- “You certainly do,” she replied with a smile; “but it does not
- follow that the interruption must be unwelcome.”
-
- “I should be sorry indeed, if it were. _We_ were always good
- friends; and now we are better.”
-
- “True. Are the others coming out?”
-
- “I do not know. Mrs. Bennet and Lydia are going in the carriage
- to Meryton. And so, my dear sister, I find, from our uncle and
- aunt, that you have actually seen Pemberley.”
-
- She replied in the affirmative.
-
- “I almost envy you the pleasure, and yet I believe it would be
- too much for me, or else I could take it in my way to Newcastle.
- And you saw the old housekeeper, I suppose? Poor Reynolds, she
- was always very fond of me. But of course she did not mention my
- name to you.”
-
- “Yes, she did.”
-
- “And what did she say?”
-
- “That you were gone into the army, and she was afraid had—not
- turned out well. At such a distance as _that_, you know, things
- are strangely misrepresented.”
-
- “Certainly,” he replied, biting his lips. Elizabeth hoped she had
- silenced him; but he soon afterwards said:
-
- “I was surprised to see Darcy in town last month. We passed each
- other several times. I wonder what he can be doing there.”
-
- “Perhaps preparing for his marriage with Miss de Bourgh,” said
- Elizabeth. “It must be something particular, to take him there at
- this time of year.”
-
- “Undoubtedly. Did you see him while you were at Lambton? I
- thought I understood from the Gardiners that you had.”
-
- “Yes; he introduced us to his sister.”
-
- “And do you like her?”
-
- “Very much.”
-
- “I have heard, indeed, that she is uncommonly improved within
- this year or two. When I last saw her, she was not very
- promising. I am very glad you liked her. I hope she will turn out
- well.”
-
- “I dare say she will; she has got over the most trying age.”
-
- “Did you go by the village of Kympton?”
-
- “I do not recollect that we did.”
-
- “I mention it, because it is the living which I ought to have
- had. A most delightful place!—Excellent Parsonage House! It would
- have suited me in every respect.”
-
- “How should you have liked making sermons?”
-
- “Exceedingly well. I should have considered it as part of my
- duty, and the exertion would soon have been nothing. One ought
- not to repine;—but, to be sure, it would have been such a thing
- for me! The quiet, the retirement of such a life would have
- answered all my ideas of happiness! But it was not to be. Did you
- ever hear Darcy mention the circumstance, when you were in Kent?”
-
- “I _have_ heard from authority, which I thought _as good_, that
- it was left you conditionally only, and at the will of the
- present patron.”
-
- “You have. Yes, there was something in _that_; I told you so from
- the first, you may remember.”
-
- “I _did_ hear, too, that there was a time, when sermon-making was
- not so palatable to you as it seems to be at present; that you
- actually declared your resolution of never taking orders, and
- that the business had been compromised accordingly.”
-
- “You did! and it was not wholly without foundation. You may
- remember what I told you on that point, when first we talked of
- it.”
-
- They were now almost at the door of the house, for she had walked
- fast to get rid of him; and unwilling, for her sister’s sake, to
- provoke him, she only said in reply, with a good-humoured smile:
-
- “Come, Mr. Wickham, we are brother and sister, you know. Do not
- let us quarrel about the past. In future, I hope we shall be
- always of one mind.”
-
- She held out her hand; he kissed it with affectionate gallantry,
- though he hardly knew how to look, and they entered the house.
-
-
-
-
- Chapter 53
-
- Mr. Wickham was so perfectly satisfied with this conversation
- that he never again distressed himself, or provoked his dear
- sister Elizabeth, by introducing the subject of it; and she was
- pleased to find that she had said enough to keep him quiet.
-
- The day of his and Lydia’s departure soon came, and Mrs. Bennet
- was forced to submit to a separation, which, as her husband by no
- means entered into her scheme of their all going to Newcastle,
- was likely to continue at least a twelvemonth.
-
- “Oh! my dear Lydia,” she cried, “when shall we meet again?”
-
- “Oh, lord! I don’t know. Not these two or three years, perhaps.”
-
- “Write to me very often, my dear.”
-
- “As often as I can. But you know married women have never much
- time for writing. My sisters may write to _me_. They will have
- nothing else to do.”
-
- Mr. Wickham’s adieus were much more affectionate than his wife’s.
- He smiled, looked handsome, and said many pretty things.
-
- “He is as fine a fellow,” said Mr. Bennet, as soon as they were
- out of the house, “as ever I saw. He simpers, and smirks, and
- makes love to us all. I am prodigiously proud of him. I defy even
- Sir William Lucas himself to produce a more valuable son-in-law.”
-
- The loss of her daughter made Mrs. Bennet very dull for several
- days.
-
- “I often think,” said she, “that there is nothing so bad as
- parting with one’s friends. One seems so forlorn without them.”
-
- “This is the consequence, you see, Madam, of marrying a
- daughter,” said Elizabeth. “It must make you better satisfied
- that your other four are single.”
-
- “It is no such thing. Lydia does not leave me because she is
- married, but only because her husband’s regiment happens to be so
- far off. If that had been nearer, she would not have gone so
- soon.”
-
- But the spiritless condition which this event threw her into was
- shortly relieved, and her mind opened again to the agitation of
- hope, by an article of news which then began to be in
- circulation. The housekeeper at Netherfield had received orders
- to prepare for the arrival of her master, who was coming down in
- a day or two, to shoot there for several weeks. Mrs. Bennet was
- quite in the fidgets. She looked at Jane, and smiled and shook
- her head by turns.
-
- “Well, well, and so Mr. Bingley is coming down, sister,” (for
- Mrs. Phillips first brought her the news). “Well, so much the
- better. Not that I care about it, though. He is nothing to us,
- you know, and I am sure I never want to see him again. But,
- however, he is very welcome to come to Netherfield, if he likes
- it. And who knows what _may_ happen? But that is nothing to us.
- You know, sister, we agreed long ago never to mention a word
- about it. And so, is it quite certain he is coming?”
-
- “You may depend on it,” replied the other, “for Mrs. Nicholls was
- in Meryton last night; I saw her passing by, and went out myself
- on purpose to know the truth of it; and she told me that it was
- certain true. He comes down on Thursday at the latest, very
- likely on Wednesday. She was going to the butcher’s, she told me,
- on purpose to order in some meat on Wednesday, and she has got
- three couple of ducks just fit to be killed.”
-
- Miss Bennet had not been able to hear of his coming without
- changing colour. It was many months since she had mentioned his
- name to Elizabeth; but now, as soon as they were alone together,
- she said:
-
- “I saw you look at me to-day, Lizzy, when my aunt told us of the
- present report; and I know I appeared distressed. But don’t
- imagine it was from any silly cause. I was only confused for the
- moment, because I felt that I _should_ be looked at. I do assure
- you that the news does not affect me either with pleasure or
- pain. I am glad of one thing, that he comes alone; because we
- shall see the less of him. Not that I am afraid of _myself_, but
- I dread other people’s remarks.”
-
- Elizabeth did not know what to make of it. Had she not seen him
- in Derbyshire, she might have supposed him capable of coming
- there with no other view than what was acknowledged; but she
- still thought him partial to Jane, and she wavered as to the
- greater probability of his coming there _with_ his friend’s
- permission, or being bold enough to come without it.
-
- “Yet it is hard,” she sometimes thought, “that this poor man
- cannot come to a house which he has legally hired, without
- raising all this speculation! I _will_ leave him to himself.”
-
- In spite of what her sister declared, and really believed to be
- her feelings in the expectation of his arrival, Elizabeth could
- easily perceive that her spirits were affected by it. They were
- more disturbed, more unequal, than she had often seen them.
-
- The subject which had been so warmly canvassed between their
- parents, about a twelvemonth ago, was now brought forward again.
-
- “As soon as ever Mr. Bingley comes, my dear,” said Mrs. Bennet,
- “you will wait on him of course.”
-
- “No, no. You forced me into visiting him last year, and promised,
- if I went to see him, he should marry one of my daughters. But it
- ended in nothing, and I will not be sent on a fool’s errand
- again.”
-
- His wife represented to him how absolutely necessary such an
- attention would be from all the neighbouring gentlemen, on his
- returning to Netherfield.
-
- “’Tis an _etiquette_ I despise,” said he. “If he wants our
- society, let him seek it. He knows where we live. I will not
- spend _my_ hours in running after my neighbours every time they
- go away and come back again.”
-
- “Well, all I know is, that it will be abominably rude if you do
- not wait on him. But, however, that shan’t prevent my asking him
- to dine here, I am determined. We must have Mrs. Long and the
- Gouldings soon. That will make thirteen with ourselves, so there
- will be just room at table for him.”
-
- Consoled by this resolution, she was the better able to bear her
- husband’s incivility; though it was very mortifying to know that
- her neighbours might all see Mr. Bingley, in consequence of it,
- before _they_ did. As the day of his arrival drew near,—
-
- “I begin to be sorry that he comes at all,” said Jane to her
- sister. “It would be nothing; I could see him with perfect
- indifference, but I can hardly bear to hear it thus perpetually
- talked of. My mother means well; but she does not know, no one
- can know, how much I suffer from what she says. Happy shall I be,
- when his stay at Netherfield is over!”
-
- “I wish I could say anything to comfort you,” replied Elizabeth;
- “but it is wholly out of my power. You must feel it; and the
- usual satisfaction of preaching patience to a sufferer is denied
- me, because you have always so much.”
-
- Mr. Bingley arrived. Mrs. Bennet, through the assistance of
- servants, contrived to have the earliest tidings of it, that the
- period of anxiety and fretfulness on her side might be as long as
- it could. She counted the days that must intervene before their
- invitation could be sent; hopeless of seeing him before. But on
- the third morning after his arrival in Hertfordshire, she saw
- him, from her dressing-room window, enter the paddock and ride
- towards the house.
-
- Her daughters were eagerly called to partake of her joy. Jane
- resolutely kept her place at the table; but Elizabeth, to satisfy
- her mother, went to the window—she looked,—she saw Mr. Darcy with
- him, and sat down again by her sister.
-
- “There is a gentleman with him, mamma,” said Kitty; “who can it
- be?”
-
- “Some acquaintance or other, my dear, I suppose; I am sure I do
- not know.”
-
- “La!” replied Kitty, “it looks just like that man that used to be
- with him before. Mr. what’s-his-name. That tall, proud man.”
-
- “Good gracious! Mr. Darcy!—and so it does, I vow. Well, any
- friend of Mr. Bingley’s will always be welcome here, to be sure;
- but else I must say that I hate the very sight of him.”
-
- Jane looked at Elizabeth with surprise and concern. She knew but
- little of their meeting in Derbyshire, and therefore felt for the
- awkwardness which must attend her sister, in seeing him almost
- for the first time after receiving his explanatory letter. Both
- sisters were uncomfortable enough. Each felt for the other, and
- of course for themselves; and their mother talked on, of her
- dislike of Mr. Darcy, and her resolution to be civil to him only
- as Mr. Bingley’s friend, without being heard by either of them.
- But Elizabeth had sources of uneasiness which could not be
- suspected by Jane, to whom she had never yet had courage to shew
- Mrs. Gardiner’s letter, or to relate her own change of sentiment
- towards him. To Jane, he could be only a man whose proposals she
- had refused, and whose merit she had undervalued; but to her own
- more extensive information, he was the person to whom the whole
- family were indebted for the first of benefits, and whom she
- regarded herself with an interest, if not quite so tender, at
- least as reasonable and just as what Jane felt for Bingley. Her
- astonishment at his coming—at his coming to Netherfield, to
- Longbourn, and voluntarily seeking her again, was almost equal to
- what she had known on first witnessing his altered behaviour in
- Derbyshire.
-
- The colour which had been driven from her face, returned for half
- a minute with an additional glow, and a smile of delight added
- lustre to her eyes, as she thought for that space of time that
- his affection and wishes must still be unshaken. But she would
- not be secure.
-
- “Let me first see how he behaves,” said she; “it will then be
- early enough for expectation.”
-
- She sat intently at work, striving to be composed, and without
- daring to lift up her eyes, till anxious curiosity carried them
- to the face of her sister as the servant was approaching the
- door. Jane looked a little paler than usual, but more sedate than
- Elizabeth had expected. On the gentlemen’s appearing, her colour
- increased; yet she received them with tolerable ease, and with a
- propriety of behaviour equally free from any symptom of
- resentment or any unnecessary complaisance.
-
- Elizabeth said as little to either as civility would allow, and
- sat down again to her work, with an eagerness which it did not
- often command. She had ventured only one glance at Darcy. He
- looked serious, as usual; and, she thought, more as he had been
- used to look in Hertfordshire, than as she had seen him at
- Pemberley. But, perhaps he could not in her mother’s presence be
- what he was before her uncle and aunt. It was a painful, but not
- an improbable, conjecture.
-
- Bingley, she had likewise seen for an instant, and in that short
- period saw him looking both pleased and embarrassed. He was
- received by Mrs. Bennet with a degree of civility which made her
- two daughters ashamed, especially when contrasted with the cold
- and ceremonious politeness of her curtsey and address to his
- friend.
-
- Elizabeth, particularly, who knew that her mother owed to the
- latter the preservation of her favourite daughter from
- irremediable infamy, was hurt and distressed to a most painful
- degree by a distinction so ill applied.
-
- Darcy, after inquiring of her how Mr. and Mrs. Gardiner did, a
- question which she could not answer without confusion, said
- scarcely anything. He was not seated by her; perhaps that was the
- reason of his silence; but it had not been so in Derbyshire.
- There he had talked to her friends, when he could not to herself.
- But now several minutes elapsed without bringing the sound of his
- voice; and when occasionally, unable to resist the impulse of
- curiosity, she raised her eyes to his face, she as often found
- him looking at Jane as at herself, and frequently on no object
- but the ground. More thoughtfulness and less anxiety to please,
- than when they last met, were plainly expressed. She was
- disappointed, and angry with herself for being so.
-
- “Could I expect it to be otherwise!” said she. “Yet why did he
- come?”
-
- She was in no humour for conversation with anyone but himself;
- and to him she had hardly courage to speak.
-
- She inquired after his sister, but could do no more.
-
- “It is a long time, Mr. Bingley, since you went away,” said Mrs.
- Bennet.
-
- He readily agreed to it.
-
- “I began to be afraid you would never come back again. People
- _did_ say you meant to quit the place entirely at Michaelmas;
- but, however, I hope it is not true. A great many changes have
- happened in the neighbourhood, since you went away. Miss Lucas is
- married and settled. And one of my own daughters. I suppose you
- have heard of it; indeed, you must have seen it in the papers. It
- was in The Times and The Courier, I know; though it was not put
- in as it ought to be. It was only said, ‘Lately, George Wickham,
- Esq. to Miss Lydia Bennet,’ without there being a syllable said
- of her father, or the place where she lived, or anything. It was
- my brother Gardiner’s drawing up too, and I wonder how he came to
- make such an awkward business of it. Did you see it?”
-
- Bingley replied that he did, and made his congratulations.
- Elizabeth dared not lift up her eyes. How Mr. Darcy looked,
- therefore, she could not tell.
-
- “It is a delightful thing, to be sure, to have a daughter well
- married,” continued her mother, “but at the same time, Mr.
- Bingley, it is very hard to have her taken such a way from me.
- They are gone down to Newcastle, a place quite northward, it
- seems, and there they are to stay I do not know how long. His
- regiment is there; for I suppose you have heard of his leaving
- the ——shire, and of his being gone into the regulars. Thank
- Heaven! he has _some_ friends, though perhaps not so many as he
- deserves.”
-
- Elizabeth, who knew this to be levelled at Mr. Darcy, was in such
- misery of shame, that she could hardly keep her seat. It drew
- from her, however, the exertion of speaking, which nothing else
- had so effectually done before; and she asked Bingley whether he
- meant to make any stay in the country at present. A few weeks, he
- believed.
-
- “When you have killed all your own birds, Mr. Bingley,” said her
- mother, “I beg you will come here, and shoot as many as you
- please on Mr. Bennet’s manor. I am sure he will be vastly happy
- to oblige you, and will save all the best of the covies for you.”
-
- Elizabeth’s misery increased, at such unnecessary, such officious
- attention! Were the same fair prospect to arise at present as had
- flattered them a year ago, every thing, she was persuaded, would
- be hastening to the same vexatious conclusion. At that instant,
- she felt that years of happiness could not make Jane or herself
- amends for moments of such painful confusion.
-
- “The first wish of my heart,” said she to herself, “is never more
- to be in company with either of them. Their society can afford no
- pleasure that will atone for such wretchedness as this! Let me
- never see either one or the other again!”
-
- Yet the misery, for which years of happiness were to offer no
- compensation, received soon afterwards material relief, from
- observing how much the beauty of her sister re-kindled the
- admiration of her former lover. When first he came in, he had
- spoken to her but little; but every five minutes seemed to be
- giving her more of his attention. He found her as handsome as she
- had been last year; as good natured, and as unaffected, though
- not quite so chatty. Jane was anxious that no difference should
- be perceived in her at all, and was really persuaded that she
- talked as much as ever. But her mind was so busily engaged, that
- she did not always know when she was silent.
-
- When the gentlemen rose to go away, Mrs. Bennet was mindful of
- her intended civility, and they were invited and engaged to dine
- at Longbourn in a few days time.
-
- “You are quite a visit in my debt, Mr. Bingley,” she added, “for
- when you went to town last winter, you promised to take a family
- dinner with us, as soon as you returned. I have not forgot, you
- see; and I assure you, I was very much disappointed that you did
- not come back and keep your engagement.”
-
- Bingley looked a little silly at this reflection, and said
- something of his concern at having been prevented by business.
- They then went away.
-
- Mrs. Bennet had been strongly inclined to ask them to stay and
- dine there that day; but, though she always kept a very good
- table, she did not think anything less than two courses could be
- good enough for a man on whom she had such anxious designs, or
- satisfy the appetite and pride of one who had ten thousand a
- year.
-
-
-
-
- Chapter 54
-
- As soon as they were gone, Elizabeth walked out to recover her
- spirits; or in other words, to dwell without interruption on
- those subjects that must deaden them more. Mr. Darcy’s behaviour
- astonished and vexed her.
-
- “Why, if he came only to be silent, grave, and indifferent,” said
- she, “did he come at all?”
-
- She could settle it in no way that gave her pleasure.
-
- “He could be still amiable, still pleasing, to my uncle and aunt,
- when he was in town; and why not to me? If he fears me, why come
- hither? If he no longer cares for me, why silent? Teasing,
- teasing, man! I will think no more about him.”
-
- Her resolution was for a short time involuntarily kept by the
- approach of her sister, who joined her with a cheerful look,
- which showed her better satisfied with their visitors, than
- Elizabeth.
-
- “Now,” said she, “that this first meeting is over, I feel
- perfectly easy. I know my own strength, and I shall never be
- embarrassed again by his coming. I am glad he dines here on
- Tuesday. It will then be publicly seen that, on both sides, we
- meet only as common and indifferent acquaintance.”
-
- “Yes, very indifferent indeed,” said Elizabeth, laughingly. “Oh,
- Jane, take care.”
-
- “My dear Lizzy, you cannot think me so weak, as to be in danger
- now?”
-
- “I think you are in very great danger of making him as much in
- love with you as ever.”
-
- They did not see the gentlemen again till Tuesday; and Mrs.
- Bennet, in the meanwhile, was giving way to all the happy
- schemes, which the good humour and common politeness of Bingley,
- in half an hour’s visit, had revived.
-
- On Tuesday there was a large party assembled at Longbourn; and
- the two who were most anxiously expected, to the credit of their
- punctuality as sportsmen, were in very good time. When they
- repaired to the dining-room, Elizabeth eagerly watched to see
- whether Bingley would take the place, which, in all their former
- parties, had belonged to him, by her sister. Her prudent mother,
- occupied by the same ideas, forbore to invite him to sit by
- herself. On entering the room, he seemed to hesitate; but Jane
- happened to look round, and happened to smile: it was decided. He
- placed himself by her.
-
- Elizabeth, with a triumphant sensation, looked towards his
- friend. He bore it with noble indifference, and she would have
- imagined that Bingley had received his sanction to be happy, had
- she not seen his eyes likewise turned towards Mr. Darcy, with an
- expression of half-laughing alarm.
-
- His behaviour to her sister was such, during dinner time, as
- showed an admiration of her, which, though more guarded than
- formerly, persuaded Elizabeth, that if left wholly to himself,
- Jane’s happiness, and his own, would be speedily secured. Though
- she dared not depend upon the consequence, she yet received
- pleasure from observing his behaviour. It gave her all the
- animation that her spirits could boast; for she was in no
- cheerful humour. Mr. Darcy was almost as far from her as the
- table could divide them. He was on one side of her mother. She
- knew how little such a situation would give pleasure to either,
- or make either appear to advantage. She was not near enough to
- hear any of their discourse, but she could see how seldom they
- spoke to each other, and how formal and cold was their manner
- whenever they did. Her mother’s ungraciousness, made the sense of
- what they owed him more painful to Elizabeth’s mind; and she
- would, at times, have given anything to be privileged to tell him
- that his kindness was neither unknown nor unfelt by the whole of
- the family.
-
- She was in hopes that the evening would afford some opportunity
- of bringing them together; that the whole of the visit would not
- pass away without enabling them to enter into something more of
- conversation than the mere ceremonious salutation attending his
- entrance. Anxious and uneasy, the period which passed in the
- drawing-room, before the gentlemen came, was wearisome and dull
- to a degree that almost made her uncivil. She looked forward to
- their entrance as the point on which all her chance of pleasure
- for the evening must depend.
-
- “If he does not come to me, _then_,” said she, “I shall give him
- up for ever.”
-
- The gentlemen came; and she thought he looked as if he would have
- answered her hopes; but, alas! the ladies had crowded round the
- table, where Miss Bennet was making tea, and Elizabeth pouring
- out the coffee, in so close a confederacy that there was not a
- single vacancy near her which would admit of a chair. And on the
- gentlemen’s approaching, one of the girls moved closer to her
- than ever, and said, in a whisper:
-
- “The men shan’t come and part us, I am determined. We want none
- of them; do we?”
-
- Darcy had walked away to another part of the room. She followed
- him with her eyes, envied everyone to whom he spoke, had scarcely
- patience enough to help anybody to coffee; and then was enraged
- against herself for being so silly!
-
- “A man who has once been refused! How could I ever be foolish
- enough to expect a renewal of his love? Is there one among the
- sex, who would not protest against such a weakness as a second
- proposal to the same woman? There is no indignity so abhorrent to
- their feelings!”
-
- She was a little revived, however, by his bringing back his
- coffee cup himself; and she seized the opportunity of saying:
-
- “Is your sister at Pemberley still?”
-
- “Yes, she will remain there till Christmas.”
-
- “And quite alone? Have all her friends left her?”
-
- “Mrs. Annesley is with her. The others have been gone on to
- Scarborough, these three weeks.”
-
- She could think of nothing more to say; but if he wished to
- converse with her, he might have better success. He stood by her,
- however, for some minutes, in silence; and, at last, on the young
- lady’s whispering to Elizabeth again, he walked away.
-
- When the tea-things were removed, and the card-tables placed, the
- ladies all rose, and Elizabeth was then hoping to be soon joined
- by him, when all her views were overthrown by seeing him fall a
- victim to her mother’s rapacity for whist players, and in a few
- moments after seated with the rest of the party. She now lost
- every expectation of pleasure. They were confined for the evening
- at different tables, and she had nothing to hope, but that his
- eyes were so often turned towards her side of the room, as to
- make him play as unsuccessfully as herself.
-
- Mrs. Bennet had designed to keep the two Netherfield gentlemen to
- supper; but their carriage was unluckily ordered before any of
- the others, and she had no opportunity of detaining them.
-
- “Well girls,” said she, as soon as they were left to themselves,
- “What say you to the day? I think every thing has passed off
- uncommonly well, I assure you. The dinner was as well dressed as
- any I ever saw. The venison was roasted to a turn—and everybody
- said they never saw so fat a haunch. The soup was fifty times
- better than what we had at the Lucases’ last week; and even Mr.
- Darcy acknowledged, that the partridges were remarkably well
- done; and I suppose he has two or three French cooks at least.
- And, my dear Jane, I never saw you look in greater beauty. Mrs.
- Long said so too, for I asked her whether you did not. And what
- do you think she said besides? ‘Ah! Mrs. Bennet, we shall have
- her at Netherfield at last.’ She did indeed. I do think Mrs. Long
- is as good a creature as ever lived—and her nieces are very
- pretty behaved girls, and not at all handsome: I like them
- prodigiously.”
-
- Mrs. Bennet, in short, was in very great spirits; she had seen
- enough of Bingley’s behaviour to Jane, to be convinced that she
- would get him at last; and her expectations of advantage to her
- family, when in a happy humour, were so far beyond reason, that
- she was quite disappointed at not seeing him there again the next
- day, to make his proposals.
-
- “It has been a very agreeable day,” said Miss Bennet to
- Elizabeth. “The party seemed so well selected, so suitable one
- with the other. I hope we may often meet again.”
-
- Elizabeth smiled.
-
- “Lizzy, you must not do so. You must not suspect me. It mortifies
- me. I assure you that I have now learnt to enjoy his conversation
- as an agreeable and sensible young man, without having a wish
- beyond it. I am perfectly satisfied, from what his manners now
- are, that he never had any design of engaging my affection. It is
- only that he is blessed with greater sweetness of address, and a
- stronger desire of generally pleasing, than any other man.”
-
- “You are very cruel,” said her sister, “you will not let me
- smile, and are provoking me to it every moment.”
-
- “How hard it is in some cases to be believed!”
-
- “And how impossible in others!”
-
- “But why should you wish to persuade me that I feel more than I
- acknowledge?”
-
- “That is a question which I hardly know how to answer. We all
- love to instruct, though we can teach only what is not worth
- knowing. Forgive me; and if you persist in indifference, do not
- make _me_ your confidante.”
-
-
-
-
- Chapter 55
-
- A few days after this visit, Mr. Bingley called again, and alone.
- His friend had left him that morning for London, but was to
- return home in ten days time. He sat with them above an hour, and
- was in remarkably good spirits. Mrs. Bennet invited him to dine
- with them; but, with many expressions of concern, he confessed
- himself engaged elsewhere.
-
- “Next time you call,” said she, “I hope we shall be more lucky.”
-
- He should be particularly happy at any time, etc. etc.; and if
- she would give him leave, would take an early opportunity of
- waiting on them.
-
- “Can you come to-morrow?”
-
- Yes, he had no engagement at all for to-morrow; and her
- invitation was accepted with alacrity.
-
- He came, and in such very good time that the ladies were none of
- them dressed. In ran Mrs. Bennet to her daughter’s room, in her
- dressing gown, and with her hair half finished, crying out:
-
- “My dear Jane, make haste and hurry down. He is come—Mr. Bingley
- is come. He is, indeed. Make haste, make haste. Here, Sarah, come
- to Miss Bennet this moment, and help her on with her gown. Never
- mind Miss Lizzy’s hair.”
-
- “We will be down as soon as we can,” said Jane; “but I dare say
- Kitty is forwarder than either of us, for she went up stairs half
- an hour ago.”
-
- “Oh! hang Kitty! what has she to do with it? Come be quick, be
- quick! Where is your sash, my dear?”
-
- But when her mother was gone, Jane would not be prevailed on to
- go down without one of her sisters.
-
- The same anxiety to get them by themselves was visible again in
- the evening. After tea, Mr. Bennet retired to the library, as was
- his custom, and Mary went up stairs to her instrument. Two
- obstacles of the five being thus removed, Mrs. Bennet sat looking
- and winking at Elizabeth and Catherine for a considerable time,
- without making any impression on them. Elizabeth would not
- observe her; and when at last Kitty did, she very innocently
- said, “What is the matter mamma? What do you keep winking at me
- for? What am I to do?”
-
- “Nothing child, nothing. I did not wink at you.” She then sat
- still five minutes longer; but unable to waste such a precious
- occasion, she suddenly got up, and saying to Kitty, “Come here,
- my love, I want to speak to you,” took her out of the room. Jane
- instantly gave a look at Elizabeth which spoke her distress at
- such premeditation, and her entreaty that _she_ would not give in
- to it. In a few minutes, Mrs. Bennet half-opened the door and
- called out:
-
- “Lizzy, my dear, I want to speak with you.”
-
- Elizabeth was forced to go.
-
- “We may as well leave them by themselves you know;” said her
- mother, as soon as she was in the hall. “Kitty and I are going up
- stairs to sit in my dressing-room.”
-
- Elizabeth made no attempt to reason with her mother, but remained
- quietly in the hall, till she and Kitty were out of sight, then
- returned into the drawing-room.
-
- Mrs. Bennet’s schemes for this day were ineffectual. Bingley was
- every thing that was charming, except the professed lover of her
- daughter. His ease and cheerfulness rendered him a most agreeable
- addition to their evening party; and he bore with the ill-judged
- officiousness of the mother, and heard all her silly remarks with
- a forbearance and command of countenance particularly grateful to
- the daughter.
-
- He scarcely needed an invitation to stay supper; and before he
- went away, an engagement was formed, chiefly through his own and
- Mrs. Bennet’s means, for his coming next morning to shoot with
- her husband.
-
- After this day, Jane said no more of her indifference. Not a word
- passed between the sisters concerning Bingley; but Elizabeth went
- to bed in the happy belief that all must speedily be concluded,
- unless Mr. Darcy returned within the stated time. Seriously,
- however, she felt tolerably persuaded that all this must have
- taken place with that gentleman’s concurrence.
-
- Bingley was punctual to his appointment; and he and Mr. Bennet
- spent the morning together, as had been agreed on. The latter was
- much more agreeable than his companion expected. There was
- nothing of presumption or folly in Bingley that could provoke his
- ridicule, or disgust him into silence; and he was more
- communicative, and less eccentric, than the other had ever seen
- him. Bingley of course returned with him to dinner; and in the
- evening Mrs. Bennet’s invention was again at work to get every
- body away from him and her daughter. Elizabeth, who had a letter
- to write, went into the breakfast room for that purpose soon
- after tea; for as the others were all going to sit down to cards,
- she could not be wanted to counteract her mother’s schemes.
-
- But on returning to the drawing-room, when her letter was
- finished, she saw, to her infinite surprise, there was reason to
- fear that her mother had been too ingenious for her. On opening
- the door, she perceived her sister and Bingley standing together
- over the hearth, as if engaged in earnest conversation; and had
- this led to no suspicion, the faces of both, as they hastily
- turned round and moved away from each other, would have told it
- all. _Their_ situation was awkward enough; but _hers_ she thought
- was still worse. Not a syllable was uttered by either; and
- Elizabeth was on the point of going away again, when Bingley, who
- as well as the other had sat down, suddenly rose, and whispering
- a few words to her sister, ran out of the room.
-
- Jane could have no reserves from Elizabeth, where confidence
- would give pleasure; and instantly embracing her, acknowledged,
- with the liveliest emotion, that she was the happiest creature in
- the world.
-
- “’Tis too much!” she added, “by far too much. I do not deserve
- it. Oh! why is not everybody as happy?”
-
- Elizabeth’s congratulations were given with a sincerity, a
- warmth, a delight, which words could but poorly express. Every
- sentence of kindness was a fresh source of happiness to Jane. But
- she would not allow herself to stay with her sister, or say half
- that remained to be said for the present.
-
- “I must go instantly to my mother;” she cried. “I would not on
- any account trifle with her affectionate solicitude; or allow her
- to hear it from anyone but myself. He is gone to my father
- already. Oh! Lizzy, to know that what I have to relate will give
- such pleasure to all my dear family! how shall I bear so much
- happiness!”
-
- She then hastened away to her mother, who had purposely broken up
- the card party, and was sitting up stairs with Kitty.
-
- Elizabeth, who was left by herself, now smiled at the rapidity
- and ease with which an affair was finally settled, that had given
- them so many previous months of suspense and vexation.
-
- “And this,” said she, “is the end of all his friend’s anxious
- circumspection! of all his sister’s falsehood and contrivance!
- the happiest, wisest, most reasonable end!”
-
- In a few minutes she was joined by Bingley, whose conference with
- her father had been short and to the purpose.
-
- “Where is your sister?” said he hastily, as he opened the door.
-
- “With my mother up stairs. She will be down in a moment, I dare
- say.”
-
- He then shut the door, and, coming up to her, claimed the good
- wishes and affection of a sister. Elizabeth honestly and heartily
- expressed her delight in the prospect of their relationship. They
- shook hands with great cordiality; and then, till her sister came
- down, she had to listen to all he had to say of his own
- happiness, and of Jane’s perfections; and in spite of his being a
- lover, Elizabeth really believed all his expectations of felicity
- to be rationally founded, because they had for basis the
- excellent understanding, and super-excellent disposition of Jane,
- and a general similarity of feeling and taste between her and
- himself.
-
- It was an evening of no common delight to them all; the
- satisfaction of Miss Bennet’s mind gave a glow of such sweet
- animation to her face, as made her look handsomer than ever.
- Kitty simpered and smiled, and hoped her turn was coming soon.
- Mrs. Bennet could not give her consent or speak her approbation
- in terms warm enough to satisfy her feelings, though she talked
- to Bingley of nothing else for half an hour; and when Mr. Bennet
- joined them at supper, his voice and manner plainly showed how
- really happy he was.
-
- Not a word, however, passed his lips in allusion to it, till
- their visitor took his leave for the night; but as soon as he was
- gone, he turned to his daughter, and said:
-
- “Jane, I congratulate you. You will be a very happy woman.”
-
- Jane went to him instantly, kissed him, and thanked him for his
- goodness.
-
- “You are a good girl;” he replied, “and I have great pleasure in
- thinking you will be so happily settled. I have not a doubt of
- your doing very well together. Your tempers are by no means
- unlike. You are each of you so complying, that nothing will ever
- be resolved on; so easy, that every servant will cheat you; and
- so generous, that you will always exceed your income.”
-
- “I hope not so. Imprudence or thoughtlessness in money matters
- would be unpardonable in _me_.”
-
- “Exceed their income! My dear Mr. Bennet,” cried his wife, “what
- are you talking of? Why, he has four or five thousand a year, and
- very likely more.” Then addressing her daughter, “Oh! my dear,
- dear Jane, I am so happy! I am sure I shan’t get a wink of sleep
- all night. I knew how it would be. I always said it must be so,
- at last. I was sure you could not be so beautiful for nothing! I
- remember, as soon as ever I saw him, when he first came into
- Hertfordshire last year, I thought how likely it was that you
- should come together. Oh! he is the handsomest young man that
- ever was seen!”
-
- Wickham, Lydia, were all forgotten. Jane was beyond competition
- her favourite child. At that moment, she cared for no other. Her
- younger sisters soon began to make interest with her for objects
- of happiness which she might in future be able to dispense.
-
- Mary petitioned for the use of the library at Netherfield; and
- Kitty begged very hard for a few balls there every winter.
-
- Bingley, from this time, was of course a daily visitor at
- Longbourn; coming frequently before breakfast, and always
- remaining till after supper; unless when some barbarous
- neighbour, who could not be enough detested, had given him an
- invitation to dinner which he thought himself obliged to accept.
-
- Elizabeth had now but little time for conversation with her
- sister; for while he was present, Jane had no attention to bestow
- on anyone else; but she found herself considerably useful to both
- of them in those hours of separation that must sometimes occur.
- In the absence of Jane, he always attached himself to Elizabeth,
- for the pleasure of talking of her; and when Bingley was gone,
- Jane constantly sought the same means of relief.
-
- “He has made me so happy,” said she, one evening, “by telling me
- that he was totally ignorant of my being in town last spring! I
- had not believed it possible.”
-
- “I suspected as much,” replied Elizabeth. “But how did he account
- for it?”
-
- “It must have been his sister’s doing. They were certainly no
- friends to his acquaintance with me, which I cannot wonder at,
- since he might have chosen so much more advantageously in many
- respects. But when they see, as I trust they will, that their
- brother is happy with me, they will learn to be contented, and we
- shall be on good terms again; though we can never be what we once
- were to each other.”
-
- “That is the most unforgiving speech,” said Elizabeth, “that I
- ever heard you utter. Good girl! It would vex me, indeed, to see
- you again the dupe of Miss Bingley’s pretended regard.”
-
- “Would you believe it, Lizzy, that when he went to town last
- November, he really loved me, and nothing but a persuasion of
- _my_ being indifferent would have prevented his coming down
- again!”
-
- “He made a little mistake to be sure; but it is to the credit of
- his modesty.”
-
- This naturally introduced a panegyric from Jane on his
- diffidence, and the little value he put on his own good
- qualities. Elizabeth was pleased to find that he had not betrayed
- the interference of his friend; for, though Jane had the most
- generous and forgiving heart in the world, she knew it was a
- circumstance which must prejudice her against him.
-
- “I am certainly the most fortunate creature that ever existed!”
- cried Jane. “Oh! Lizzy, why am I thus singled from my family, and
- blessed above them all! If I could but see you as happy! If there
- were but such another man for you!”
-
- “If you were to give me forty such men, I never could be so happy
- as you. Till I have your disposition, your goodness, I never can
- have your happiness. No, no, let me shift for myself; and,
- perhaps, if I have very good luck, I may meet with another Mr.
- Collins in time.”
-
- The situation of affairs in the Longbourn family could not be
- long a secret. Mrs. Bennet was privileged to whisper it to Mrs.
- Phillips, and she ventured, without any permission, to do the
- same by all her neighbours in Meryton.
-
- The Bennets were speedily pronounced to be the luckiest family in
- the world, though only a few weeks before, when Lydia had first
- run away, they had been generally proved to be marked out for
- misfortune.
-
-
-
-
- Chapter 56
-
- One morning, about a week after Bingley’s engagement with Jane
- had been formed, as he and the females of the family were sitting
- together in the dining-room, their attention was suddenly drawn
- to the window, by the sound of a carriage; and they perceived a
- chaise and four driving up the lawn. It was too early in the
- morning for visitors, and besides, the equipage did not answer to
- that of any of their neighbours. The horses were post; and
- neither the carriage, nor the livery of the servant who preceded
- it, were familiar to them. As it was certain, however, that
- somebody was coming, Bingley instantly prevailed on Miss Bennet
- to avoid the confinement of such an intrusion, and walk away with
- him into the shrubbery. They both set off, and the conjectures of
- the remaining three continued, though with little satisfaction,
- till the door was thrown open and their visitor entered. It was
- Lady Catherine de Bourgh.
-
- They were of course all intending to be surprised; but their
- astonishment was beyond their expectation; and on the part of
- Mrs. Bennet and Kitty, though she was perfectly unknown to them,
- even inferior to what Elizabeth felt.
-
- She entered the room with an air more than usually ungracious,
- made no other reply to Elizabeth’s salutation than a slight
- inclination of the head, and sat down without saying a word.
- Elizabeth had mentioned her name to her mother on her ladyship’s
- entrance, though no request of introduction had been made.
-
- Mrs. Bennet, all amazement, though flattered by having a guest of
- such high importance, received her with the utmost politeness.
- After sitting for a moment in silence, she said very stiffly to
- Elizabeth,
-
- “I hope you are well, Miss Bennet. That lady, I suppose, is your
- mother.”
-
- Elizabeth replied very concisely that she was.
-
- “And _that_ I suppose is one of your sisters.”
-
- “Yes, madam,” said Mrs. Bennet, delighted to speak to Lady
- Catherine. “She is my youngest girl but one. My youngest of all
- is lately married, and my eldest is somewhere about the grounds,
- walking with a young man who, I believe, will soon become a part
- of the family.”
-
- “You have a very small park here,” returned Lady Catherine after
- a short silence.
-
- “It is nothing in comparison of Rosings, my lady, I dare say; but
- I assure you it is much larger than Sir William Lucas’s.”
-
- “This must be a most inconvenient sitting room for the evening,
- in summer; the windows are full west.”
-
- Mrs. Bennet assured her that they never sat there after dinner,
- and then added:
-
- “May I take the liberty of asking your ladyship whether you left
- Mr. and Mrs. Collins well.”
-
- “Yes, very well. I saw them the night before last.”
-
- Elizabeth now expected that she would produce a letter for her
- from Charlotte, as it seemed the only probable motive for her
- calling. But no letter appeared, and she was completely puzzled.
-
- Mrs. Bennet, with great civility, begged her ladyship to take
- some refreshment; but Lady Catherine very resolutely, and not
- very politely, declined eating anything; and then, rising up,
- said to Elizabeth,
-
- “Miss Bennet, there seemed to be a prettyish kind of a little
- wilderness on one side of your lawn. I should be glad to take a
- turn in it, if you will favour me with your company.”
-
- “Go, my dear,” cried her mother, “and show her ladyship about the
- different walks. I think she will be pleased with the hermitage.”
-
- Elizabeth obeyed, and running into her own room for her parasol,
- attended her noble guest downstairs. As they passed through the
- hall, Lady Catherine opened the doors into the dining-parlour and
- drawing-room, and pronouncing them, after a short survey, to be
- decent looking rooms, walked on.
-
- Her carriage remained at the door, and Elizabeth saw that her
- waiting-woman was in it. They proceeded in silence along the
- gravel walk that led to the copse; Elizabeth was determined to
- make no effort for conversation with a woman who was now more
- than usually insolent and disagreeable.
-
- “How could I ever think her like her nephew?” said she, as she
- looked in her face.
-
- As soon as they entered the copse, Lady Catherine began in the
- following manner:—
-
- “You can be at no loss, Miss Bennet, to understand the reason of
- my journey hither. Your own heart, your own conscience, must tell
- you why I come.”
-
- Elizabeth looked with unaffected astonishment.
-
- “Indeed, you are mistaken, Madam. I have not been at all able to
- account for the honour of seeing you here.”
-
- “Miss Bennet,” replied her ladyship, in an angry tone, “you ought
- to know, that I am not to be trifled with. But however insincere
- _you_ may choose to be, you shall not find _me_ so. My character
- has ever been celebrated for its sincerity and frankness, and in
- a cause of such moment as this, I shall certainly not depart from
- it. A report of a most alarming nature reached me two days ago. I
- was told that not only your sister was on the point of being most
- advantageously married, but that you, that Miss Elizabeth Bennet,
- would, in all likelihood, be soon afterwards united to my nephew,
- my own nephew, Mr. Darcy. Though I _know_ it must be a scandalous
- falsehood, though I would not injure him so much as to suppose
- the truth of it possible, I instantly resolved on setting off for
- this place, that I might make my sentiments known to you.”
-
- “If you believed it impossible to be true,” said Elizabeth,
- colouring with astonishment and disdain, “I wonder you took the
- trouble of coming so far. What could your ladyship propose by
- it?”
-
- “At once to insist upon having such a report universally
- contradicted.”
-
- “Your coming to Longbourn, to see me and my family,” said
- Elizabeth coolly, “will be rather a confirmation of it; if,
- indeed, such a report is in existence.”
-
- “If! Do you then pretend to be ignorant of it? Has it not been
- industriously circulated by yourselves? Do you not know that such
- a report is spread abroad?”
-
- “I never heard that it was.”
-
- “And can you likewise declare, that there is no _foundation_ for
- it?”
-
- “I do not pretend to possess equal frankness with your ladyship.
- _You_ may ask questions which _I_ shall not choose to answer.”
-
- “This is not to be borne. Miss Bennet, I insist on being
- satisfied. Has he, has my nephew, made you an offer of marriage?”
-
- “Your ladyship has declared it to be impossible.”
-
- “It ought to be so; it must be so, while he retains the use of
- his reason. But _your_ arts and allurements may, in a moment of
- infatuation, have made him forget what he owes to himself and to
- all his family. You may have drawn him in.”
-
- “If I have, I shall be the last person to confess it.”
-
- “Miss Bennet, do you know who I am? I have not been accustomed to
- such language as this. I am almost the nearest relation he has in
- the world, and am entitled to know all his dearest concerns.”
-
- “But you are not entitled to know _mine;_ nor will such behaviour
- as this, ever induce me to be explicit.”
-
- “Let me be rightly understood. This match, to which you have the
- presumption to aspire, can never take place. No, never. Mr. Darcy
- is engaged to _my daughter_. Now what have you to say?”
-
- “Only this; that if he is so, you can have no reason to suppose
- he will make an offer to me.”
-
- Lady Catherine hesitated for a moment, and then replied:
-
- “The engagement between them is of a peculiar kind. From their
- infancy, they have been intended for each other. It was the
- favourite wish of _his_ mother, as well as of hers. While in
- their cradles, we planned the union: and now, at the moment when
- the wishes of both sisters would be accomplished in their
- marriage, to be prevented by a young woman of inferior birth, of
- no importance in the world, and wholly unallied to the family! Do
- you pay no regard to the wishes of his friends? To his tacit
- engagement with Miss de Bourgh? Are you lost to every feeling of
- propriety and delicacy? Have you not heard me say that from his
- earliest hours he was destined for his cousin?”
-
- “Yes, and I had heard it before. But what is that to me? If there
- is no other objection to my marrying your nephew, I shall
- certainly not be kept from it by knowing that his mother and aunt
- wished him to marry Miss de Bourgh. You both did as much as you
- could in planning the marriage. Its completion depended on
- others. If Mr. Darcy is neither by honour nor inclination
- confined to his cousin, why is not he to make another choice? And
- if I am that choice, why may not I accept him?”
-
- “Because honour, decorum, prudence, nay, interest, forbid it.
- Yes, Miss Bennet, interest; for do not expect to be noticed by
- his family or friends, if you wilfully act against the
- inclinations of all. You will be censured, slighted, and
- despised, by everyone connected with him. Your alliance will be a
- disgrace; your name will never even be mentioned by any of us.”
-
- “These are heavy misfortunes,” replied Elizabeth. “But the wife
- of Mr. Darcy must have such extraordinary sources of happiness
- necessarily attached to her situation, that she could, upon the
- whole, have no cause to repine.”
-
- “Obstinate, headstrong girl! I am ashamed of you! Is this your
- gratitude for my attentions to you last spring? Is nothing due to
- me on that score? Let us sit down. You are to understand, Miss
- Bennet, that I came here with the determined resolution of
- carrying my purpose; nor will I be dissuaded from it. I have not
- been used to submit to any person’s whims. I have not been in the
- habit of brooking disappointment.”
-
- “_That_ will make your ladyship’s situation at present more
- pitiable; but it will have no effect on _me_.”
-
- “I will not be interrupted. Hear me in silence. My daughter and
- my nephew are formed for each other. They are descended, on the
- maternal side, from the same noble line; and, on the father’s,
- from respectable, honourable, and ancient—though
- untitled—families. Their fortune on both sides is splendid. They
- are destined for each other by the voice of every member of their
- respective houses; and what is to divide them? The upstart
- pretensions of a young woman without family, connections, or
- fortune. Is this to be endured! But it must not, shall not be. If
- you were sensible of your own good, you would not wish to quit
- the sphere in which you have been brought up.”
-
- “In marrying your nephew, I should not consider myself as
- quitting that sphere. He is a gentleman; I am a gentleman’s
- daughter; so far we are equal.”
-
- “True. You _are_ a gentleman’s daughter. But who was your mother?
- Who are your uncles and aunts? Do not imagine me ignorant of
- their condition.”
-
- “Whatever my connections may be,” said Elizabeth, “if your nephew
- does not object to them, they can be nothing to _you_.”
-
- “Tell me once for all, are you engaged to him?”
-
- Though Elizabeth would not, for the mere purpose of obliging Lady
- Catherine, have answered this question, she could not but say,
- after a moment’s deliberation:
-
- “I am not.”
-
- Lady Catherine seemed pleased.
-
- “And will you promise me, never to enter into such an
- engagement?”
-
- “I will make no promise of the kind.”
-
- “Miss Bennet I am shocked and astonished. I expected to find a
- more reasonable young woman. But do not deceive yourself into a
- belief that I will ever recede. I shall not go away till you have
- given me the assurance I require.”
-
- “And I certainly _never_ shall give it. I am not to be
- intimidated into anything so wholly unreasonable. Your ladyship
- wants Mr. Darcy to marry your daughter; but would my giving you
- the wished-for promise make _their_ marriage at all more
- probable? Supposing him to be attached to me, would _my_ refusing
- to accept his hand make him wish to bestow it on his cousin?
- Allow me to say, Lady Catherine, that the arguments with which
- you have supported this extraordinary application have been as
- frivolous as the application was ill-judged. You have widely
- mistaken my character, if you think I can be worked on by such
- persuasions as these. How far your nephew might approve of your
- interference in _his_ affairs, I cannot tell; but you have
- certainly no right to concern yourself in mine. I must beg,
- therefore, to be importuned no farther on the subject.”
-
- “Not so hasty, if you please. I have by no means done. To all the
- objections I have already urged, I have still another to add. I
- am no stranger to the particulars of your youngest sister’s
- infamous elopement. I know it all; that the young man’s marrying
- her was a patched-up business, at the expence of your father and
- uncles. And is _such_ a girl to be my nephew’s sister? Is _her_
- husband, who is the son of his late father’s steward, to be his
- brother? Heaven and earth!—of what are you thinking? Are the
- shades of Pemberley to be thus polluted?”
-
- “You can _now_ have nothing further to say,” she resentfully
- answered. “You have insulted me in every possible method. I must
- beg to return to the house.”
-
- And she rose as she spoke. Lady Catherine rose also, and they
- turned back. Her ladyship was highly incensed.
-
- “You have no regard, then, for the honour and credit of my
- nephew! Unfeeling, selfish girl! Do you not consider that a
- connection with you must disgrace him in the eyes of everybody?”
-
- “Lady Catherine, I have nothing further to say. You know my
- sentiments.”
-
- “You are then resolved to have him?”
-
- “I have said no such thing. I am only resolved to act in that
- manner, which will, in my own opinion, constitute my happiness,
- without reference to _you_, or to any person so wholly
- unconnected with me.”
-
- “It is well. You refuse, then, to oblige me. You refuse to obey
- the claims of duty, honour, and gratitude. You are determined to
- ruin him in the opinion of all his friends, and make him the
- contempt of the world.”
-
- “Neither duty, nor honour, nor gratitude,” replied Elizabeth,
- “have any possible claim on me, in the present instance. No
- principle of either would be violated by my marriage with Mr.
- Darcy. And with regard to the resentment of his family, or the
- indignation of the world, if the former _were_ excited by his
- marrying me, it would not give me one moment’s concern—and the
- world in general would have too much sense to join in the scorn.”
-
- “And this is your real opinion! This is your final resolve! Very
- well. I shall now know how to act. Do not imagine, Miss Bennet,
- that your ambition will ever be gratified. I came to try you. I
- hoped to find you reasonable; but, depend upon it, I will carry
- my point.”
-
- In this manner Lady Catherine talked on, till they were at the
- door of the carriage, when, turning hastily round, she added, “I
- take no leave of you, Miss Bennet. I send no compliments to your
- mother. You deserve no such attention. I am most seriously
- displeased.”
-
- Elizabeth made no answer; and without attempting to persuade her
- ladyship to return into the house, walked quietly into it
- herself. She heard the carriage drive away as she proceeded up
- stairs. Her mother impatiently met her at the door of the
- dressing-room, to ask why Lady Catherine would not come in again
- and rest herself.
-
- “She did not choose it,” said her daughter, “she would go.”
-
- “She is a very fine-looking woman! and her calling here was
- prodigiously civil! for she only came, I suppose, to tell us the
- Collinses were well. She is on her road somewhere, I dare say,
- and so, passing through Meryton, thought she might as well call
- on you. I suppose she had nothing particular to say to you,
- Lizzy?”
-
- Elizabeth was forced to give into a little falsehood here; for to
- acknowledge the substance of their conversation was impossible.
-
-
-
-
- Chapter 57
-
- The discomposure of spirits which this extraordinary visit threw
- Elizabeth into, could not be easily overcome; nor could she, for
- many hours, learn to think of it less than incessantly. Lady
- Catherine, it appeared, had actually taken the trouble of this
- journey from Rosings, for the sole purpose of breaking off her
- supposed engagement with Mr. Darcy. It was a rational scheme, to
- be sure! but from what the report of their engagement could
- originate, Elizabeth was at a loss to imagine; till she
- recollected that _his_ being the intimate friend of Bingley, and
- _her_ being the sister of Jane, was enough, at a time when the
- expectation of one wedding made everybody eager for another, to
- supply the idea. She had not herself forgotten to feel that the
- marriage of her sister must bring them more frequently together.
- And her neighbours at Lucas Lodge, therefore (for through their
- communication with the Collinses, the report, she concluded, had
- reached Lady Catherine), had only set _that_ down as almost
- certain and immediate, which _she_ had looked forward to as
- possible at some future time.
-
- In revolving Lady Catherine’s expressions, however, she could not
- help feeling some uneasiness as to the possible consequence of
- her persisting in this interference. From what she had said of
- her resolution to prevent their marriage, it occurred to
- Elizabeth that she must meditate an application to her nephew;
- and how he might take a similar representation of the evils
- attached to a connection with her, she dared not pronounce. She
- knew not the exact degree of his affection for his aunt, or his
- dependence on her judgment, but it was natural to suppose that he
- thought much higher of her ladyship than _she_ could do; and it
- was certain that, in enumerating the miseries of a marriage with
- _one_, whose immediate connections were so unequal to his own,
- his aunt would address him on his weakest side. With his notions
- of dignity, he would probably feel that the arguments, which to
- Elizabeth had appeared weak and ridiculous, contained much good
- sense and solid reasoning.
-
- If he had been wavering before as to what he should do, which had
- often seemed likely, the advice and entreaty of so near a
- relation might settle every doubt, and determine him at once to
- be as happy as dignity unblemished could make him. In that case
- he would return no more. Lady Catherine might see him in her way
- through town; and his engagement to Bingley of coming again to
- Netherfield must give way.
-
- “If, therefore, an excuse for not keeping his promise should come
- to his friend within a few days,” she added, “I shall know how to
- understand it. I shall then give over every expectation, every
- wish of his constancy. If he is satisfied with only regretting
- me, when he might have obtained my affections and hand, I shall
- soon cease to regret him at all.”
-
- The surprise of the rest of the family, on hearing who their
- visitor had been, was very great; but they obligingly satisfied
- it, with the same kind of supposition which had appeased Mrs.
- Bennet’s curiosity; and Elizabeth was spared from much teasing on
- the subject.
-
- The next morning, as she was going downstairs, she was met by her
- father, who came out of his library with a letter in his hand.
-
- “Lizzy,” said he, “I was going to look for you; come into my
- room.”
-
- She followed him thither; and her curiosity to know what he had
- to tell her was heightened by the supposition of its being in
- some manner connected with the letter he held. It suddenly struck
- her that it might be from Lady Catherine; and she anticipated
- with dismay all the consequent explanations.
-
- She followed her father to the fire place, and they both sat
- down. He then said,
-
- “I have received a letter this morning that has astonished me
- exceedingly. As it principally concerns yourself, you ought to
- know its contents. I did not know before, that I had _two_
- daughters on the brink of matrimony. Let me congratulate you on a
- very important conquest.”
-
- The colour now rushed into Elizabeth’s cheeks in the
- instantaneous conviction of its being a letter from the nephew,
- instead of the aunt; and she was undetermined whether most to be
- pleased that he explained himself at all, or offended that his
- letter was not rather addressed to herself; when her father
- continued:
-
- “You look conscious. Young ladies have great penetration in such
- matters as these; but I think I may defy even _your_ sagacity, to
- discover the name of your admirer. This letter is from Mr.
- Collins.”
-
- “From Mr. Collins! and what can _he_ have to say?”
-
- “Something very much to the purpose of course. He begins with
- congratulations on the approaching nuptials of my eldest
- daughter, of which, it seems, he has been told by some of the
- good-natured, gossiping Lucases. I shall not sport with your
- impatience, by reading what he says on that point. What relates
- to yourself, is as follows: ‘Having thus offered you the sincere
- congratulations of Mrs. Collins and myself on this happy event,
- let me now add a short hint on the subject of another; of which
- we have been advertised by the same authority. Your daughter
- Elizabeth, it is presumed, will not long bear the name of Bennet,
- after her elder sister has resigned it, and the chosen partner of
- her fate may be reasonably looked up to as one of the most
- illustrious personages in this land.’
-
- “Can you possibly guess, Lizzy, who is meant by this? ‘This young
- gentleman is blessed, in a peculiar way, with every thing the
- heart of mortal can most desire,—splendid property, noble
- kindred, and extensive patronage. Yet in spite of all these
- temptations, let me warn my cousin Elizabeth, and yourself, of
- what evils you may incur by a precipitate closure with this
- gentleman’s proposals, which, of course, you will be inclined to
- take immediate advantage of.’
-
- “Have you any idea, Lizzy, who this gentleman is? But now it
- comes out:
-
- “‘My motive for cautioning you is as follows. We have reason to
- imagine that his aunt, Lady Catherine de Bourgh, does not look on
- the match with a friendly eye.’
-
- “_Mr. Darcy_, you see, is the man! Now, Lizzy, I think I _have_
- surprised you. Could he, or the Lucases, have pitched on any man
- within the circle of our acquaintance, whose name would have
- given the lie more effectually to what they related? Mr. Darcy,
- who never looks at any woman but to see a blemish, and who
- probably never looked at _you_ in his life! It is admirable!”
-
- Elizabeth tried to join in her father’s pleasantry, but could
- only force one most reluctant smile. Never had his wit been
- directed in a manner so little agreeable to her.
-
- “Are you not diverted?”
-
- “Oh! yes. Pray read on.”
-
- “‘After mentioning the likelihood of this marriage to her
- ladyship last night, she immediately, with her usual
- condescension, expressed what she felt on the occasion; when it
- became apparent, that on the score of some family objections on
- the part of my cousin, she would never give her consent to what
- she termed so disgraceful a match. I thought it my duty to give
- the speediest intelligence of this to my cousin, that she and her
- noble admirer may be aware of what they are about, and not run
- hastily into a marriage which has not been properly sanctioned.’
- Mr. Collins moreover adds, ‘I am truly rejoiced that my cousin
- Lydia’s sad business has been so well hushed up, and am only
- concerned that their living together before the marriage took
- place should be so generally known. I must not, however, neglect
- the duties of my station, or refrain from declaring my amazement
- at hearing that you received the young couple into your house as
- soon as they were married. It was an encouragement of vice; and
- had I been the rector of Longbourn, I should very strenuously
- have opposed it. You ought certainly to forgive them, as a
- Christian, but never to admit them in your sight, or allow their
- names to be mentioned in your hearing.’ _That_ is his notion of
- Christian forgiveness! The rest of his letter is only about his
- dear Charlotte’s situation, and his expectation of a young
- olive-branch. But, Lizzy, you look as if you did not enjoy it.
- You are not going to be _missish_, I hope, and pretend to be
- affronted at an idle report. For what do we live, but to make
- sport for our neighbours, and laugh at them in our turn?”
-
- “Oh!” cried Elizabeth, “I am excessively diverted. But it is so
- strange!”
-
- “Yes—_that_ is what makes it amusing. Had they fixed on any other
- man it would have been nothing; but _his_ perfect indifference,
- and _your_ pointed dislike, make it so delightfully absurd! Much
- as I abominate writing, I would not give up Mr. Collins’s
- correspondence for any consideration. Nay, when I read a letter
- of his, I cannot help giving him the preference even over
- Wickham, much as I value the impudence and hypocrisy of my
- son-in-law. And pray, Lizzy, what said Lady Catherine about this
- report? Did she call to refuse her consent?”
-
- To this question his daughter replied only with a laugh; and as
- it had been asked without the least suspicion, she was not
- distressed by his repeating it. Elizabeth had never been more at
- a loss to make her feelings appear what they were not. It was
- necessary to laugh, when she would rather have cried. Her father
- had most cruelly mortified her, by what he said of Mr. Darcy’s
- indifference, and she could do nothing but wonder at such a want
- of penetration, or fear that perhaps, instead of his seeing too
- _little_, she might have fancied too _much_.
-
-
-
-
- Chapter 58
-
- Instead of receiving any such letter of excuse from his friend,
- as Elizabeth half expected Mr. Bingley to do, he was able to
- bring Darcy with him to Longbourn before many days had passed
- after Lady Catherine’s visit. The gentlemen arrived early; and,
- before Mrs. Bennet had time to tell him of their having seen his
- aunt, of which her daughter sat in momentary dread, Bingley, who
- wanted to be alone with Jane, proposed their all walking out. It
- was agreed to. Mrs. Bennet was not in the habit of walking; Mary
- could never spare time; but the remaining five set off together.
- Bingley and Jane, however, soon allowed the others to outstrip
- them. They lagged behind, while Elizabeth, Kitty, and Darcy were
- to entertain each other. Very little was said by either; Kitty
- was too much afraid of him to talk; Elizabeth was secretly
- forming a desperate resolution; and perhaps he might be doing the
- same.
-
- They walked towards the Lucases, because Kitty wished to call
- upon Maria; and as Elizabeth saw no occasion for making it a
- general concern, when Kitty left them she went boldly on with him
- alone. Now was the moment for her resolution to be executed, and,
- while her courage was high, she immediately said:
-
- “Mr. Darcy, I am a very selfish creature; and, for the sake of
- giving relief to my own feelings, care not how much I may be
- wounding yours. I can no longer help thanking you for your
- unexampled kindness to my poor sister. Ever since I have known
- it, I have been most anxious to acknowledge to you how gratefully
- I feel it. Were it known to the rest of my family, I should not
- have merely my own gratitude to express.”
-
- “I am sorry, exceedingly sorry,” replied Darcy, in a tone of
- surprise and emotion, “that you have ever been informed of what
- may, in a mistaken light, have given you uneasiness. I did not
- think Mrs. Gardiner was so little to be trusted.”
-
- “You must not blame my aunt. Lydia’s thoughtlessness first
- betrayed to me that you had been concerned in the matter; and, of
- course, I could not rest till I knew the particulars. Let me
- thank you again and again, in the name of all my family, for that
- generous compassion which induced you to take so much trouble,
- and bear so many mortifications, for the sake of discovering
- them.”
-
- “If you _will_ thank me,” he replied, “let it be for yourself
- alone. That the wish of giving happiness to you might add force
- to the other inducements which led me on, I shall not attempt to
- deny. But your _family_ owe me nothing. Much as I respect them, I
- believe I thought only of _you_.”
-
- Elizabeth was too much embarrassed to say a word. After a short
- pause, her companion added, “You are too generous to trifle with
- me. If your feelings are still what they were last April, tell me
- so at once. _My_ affections and wishes are unchanged, but one
- word from you will silence me on this subject for ever.”
-
- Elizabeth, feeling all the more than common awkwardness and
- anxiety of his situation, now forced herself to speak; and
- immediately, though not very fluently, gave him to understand
- that her sentiments had undergone so material a change, since the
- period to which he alluded, as to make her receive with gratitude
- and pleasure his present assurances. The happiness which this
- reply produced, was such as he had probably never felt before;
- and he expressed himself on the occasion as sensibly and as
- warmly as a man violently in love can be supposed to do. Had
- Elizabeth been able to encounter his eye, she might have seen how
- well the expression of heartfelt delight, diffused over his face,
- became him; but, though she could not look, she could listen, and
- he told her of feelings, which, in proving of what importance she
- was to him, made his affection every moment more valuable.
-
- They walked on, without knowing in what direction. There was too
- much to be thought, and felt, and said, for attention to any
- other objects. She soon learnt that they were indebted for their
- present good understanding to the efforts of his aunt, who _did_
- call on him in her return through London, and there relate her
- journey to Longbourn, its motive, and the substance of her
- conversation with Elizabeth; dwelling emphatically on every
- expression of the latter which, in her ladyship’s apprehension,
- peculiarly denoted her perverseness and assurance; in the belief
- that such a relation must assist her endeavours to obtain that
- promise from her nephew which _she_ had refused to give. But,
- unluckily for her ladyship, its effect had been exactly
- contrariwise.
-
- “It taught me to hope,” said he, “as I had scarcely ever allowed
- myself to hope before. I knew enough of your disposition to be
- certain that, had you been absolutely, irrevocably decided
- against me, you would have acknowledged it to Lady Catherine,
- frankly and openly.”
-
- Elizabeth coloured and laughed as she replied, “Yes, you know
- enough of my _frankness_ to believe me capable of _that_. After
- abusing you so abominably to your face, I could have no scruple
- in abusing you to all your relations.”
-
- “What did you say of me, that I did not deserve? For, though your
- accusations were ill-founded, formed on mistaken premises, my
- behaviour to you at the time had merited the severest reproof. It
- was unpardonable. I cannot think of it without abhorrence.”
-
- “We will not quarrel for the greater share of blame annexed to
- that evening,” said Elizabeth. “The conduct of neither, if
- strictly examined, will be irreproachable; but since then, we
- have both, I hope, improved in civility.”
-
- “I cannot be so easily reconciled to myself. The recollection of
- what I then said, of my conduct, my manners, my expressions
- during the whole of it, is now, and has been many months,
- inexpressibly painful to me. Your reproof, so well applied, I
- shall never forget: ‘had you behaved in a more gentlemanlike
- manner.’ Those were your words. You know not, you can scarcely
- conceive, how they have tortured me;—though it was some time, I
- confess, before I was reasonable enough to allow their justice.”
-
- “I was certainly very far from expecting them to make so strong
- an impression. I had not the smallest idea of their being ever
- felt in such a way.”
-
- “I can easily believe it. You thought me then devoid of every
- proper feeling, I am sure you did. The turn of your countenance I
- shall never forget, as you said that I could not have addressed
- you in any possible way that would induce you to accept me.”
-
- “Oh! do not repeat what I then said. These recollections will not
- do at all. I assure you that I have long been most heartily
- ashamed of it.”
-
- Darcy mentioned his letter. “Did it,” said he, “did it soon make
- you think better of me? Did you, on reading it, give any credit
- to its contents?”
-
- She explained what its effect on her had been, and how gradually
- all her former prejudices had been removed.
-
- “I knew,” said he, “that what I wrote must give you pain, but it
- was necessary. I hope you have destroyed the letter. There was
- one part especially, the opening of it, which I should dread your
- having the power of reading again. I can remember some
- expressions which might justly make you hate me.”
-
- “The letter shall certainly be burnt, if you believe it essential
- to the preservation of my regard; but, though we have both reason
- to think my opinions not entirely unalterable, they are not, I
- hope, quite so easily changed as that implies.”
-
- “When I wrote that letter,” replied Darcy, “I believed myself
- perfectly calm and cool, but I am since convinced that it was
- written in a dreadful bitterness of spirit.”
-
- “The letter, perhaps, began in bitterness, but it did not end so.
- The adieu is charity itself. But think no more of the letter. The
- feelings of the person who wrote, and the person who received it,
- are now so widely different from what they were then, that every
- unpleasant circumstance attending it ought to be forgotten. You
- must learn some of my philosophy. Think only of the past as its
- remembrance gives you pleasure.”
-
- “I cannot give you credit for any philosophy of the kind. _Your_
- retrospections must be so totally void of reproach, that the
- contentment arising from them is not of philosophy, but, what is
- much better, of innocence. But with _me_, it is not so. Painful
- recollections will intrude which cannot, which ought not, to be
- repelled. I have been a selfish being all my life, in practice,
- though not in principle. As a child I was taught what was
- _right_, but I was not taught to correct my temper. I was given
- good principles, but left to follow them in pride and conceit.
- Unfortunately an only son (for many years an only _child_), I was
- spoilt by my parents, who, though good themselves (my father,
- particularly, all that was benevolent and amiable), allowed,
- encouraged, almost taught me to be selfish and overbearing; to
- care for none beyond my own family circle; to think meanly of all
- the rest of the world; to _wish_ at least to think meanly of
- their sense and worth compared with my own. Such I was, from
- eight to eight and twenty; and such I might still have been but
- for you, dearest, loveliest Elizabeth! What do I not owe you! You
- taught me a lesson, hard indeed at first, but most advantageous.
- By you, I was properly humbled. I came to you without a doubt of
- my reception. You showed me how insufficient were all my
- pretensions to please a woman worthy of being pleased.”
-
- “Had you then persuaded yourself that I should?”
-
- “Indeed I had. What will you think of my vanity? I believed you
- to be wishing, expecting my addresses.”
-
- “My manners must have been in fault, but not intentionally, I
- assure you. I never meant to deceive you, but my spirits might
- often lead me wrong. How you must have hated me after _that_
- evening?”
-
- “Hate you! I was angry perhaps at first, but my anger soon began
- to take a proper direction.”
-
- “I am almost afraid of asking what you thought of me, when we met
- at Pemberley. You blamed me for coming?”
-
- “No indeed; I felt nothing but surprise.”
-
- “Your surprise could not be greater than _mine_ in being noticed
- by you. My conscience told me that I deserved no extraordinary
- politeness, and I confess that I did not expect to receive _more_
- than my due.”
-
- “My object _then_,” replied Darcy, “was to show you, by every
- civility in my power, that I was not so mean as to resent the
- past; and I hoped to obtain your forgiveness, to lessen your ill
- opinion, by letting you see that your reproofs had been attended
- to. How soon any other wishes introduced themselves I can hardly
- tell, but I believe in about half an hour after I had seen you.”
-
- He then told her of Georgiana’s delight in her acquaintance, and
- of her disappointment at its sudden interruption; which naturally
- leading to the cause of that interruption, she soon learnt that
- his resolution of following her from Derbyshire in quest of her
- sister had been formed before he quitted the inn, and that his
- gravity and thoughtfulness there had arisen from no other
- struggles than what such a purpose must comprehend.
-
- She expressed her gratitude again, but it was too painful a
- subject to each, to be dwelt on farther.
-
- After walking several miles in a leisurely manner, and too busy
- to know anything about it, they found at last, on examining their
- watches, that it was time to be at home.
-
- “What could become of Mr. Bingley and Jane!” was a wonder which
- introduced the discussion of _their_ affairs. Darcy was delighted
- with their engagement; his friend had given him the earliest
- information of it.
-
- “I must ask whether you were surprised?” said Elizabeth.
-
- “Not at all. When I went away, I felt that it would soon happen.”
-
- “That is to say, you had given your permission. I guessed as
- much.” And though he exclaimed at the term, she found that it had
- been pretty much the case.
-
- “On the evening before my going to London,” said he, “I made a
- confession to him, which I believe I ought to have made long ago.
- I told him of all that had occurred to make my former
- interference in his affairs absurd and impertinent. His surprise
- was great. He had never had the slightest suspicion. I told him,
- moreover, that I believed myself mistaken in supposing, as I had
- done, that your sister was indifferent to him; and as I could
- easily perceive that his attachment to her was unabated, I felt
- no doubt of their happiness together.”
-
- Elizabeth could not help smiling at his easy manner of directing
- his friend.
-
- “Did you speak from your own observation,” said she, “when you
- told him that my sister loved him, or merely from my information
- last spring?”
-
- “From the former. I had narrowly observed her during the two
- visits which I had lately made here; and I was convinced of her
- affection.”
-
- “And your assurance of it, I suppose, carried immediate
- conviction to him.”
-
- “It did. Bingley is most unaffectedly modest. His diffidence had
- prevented his depending on his own judgment in so anxious a case,
- but his reliance on mine made every thing easy. I was obliged to
- confess one thing, which for a time, and not unjustly, offended
- him. I could not allow myself to conceal that your sister had
- been in town three months last winter, that I had known it, and
- purposely kept it from him. He was angry. But his anger, I am
- persuaded, lasted no longer than he remained in any doubt of your
- sister’s sentiments. He has heartily forgiven me now.”
-
- Elizabeth longed to observe that Mr. Bingley had been a most
- delightful friend; so easily guided that his worth was
- invaluable; but she checked herself. She remembered that he had
- yet to learn to be laughed at, and it was rather too early to
- begin. In anticipating the happiness of Bingley, which of course
- was to be inferior only to his own, he continued the conversation
- till they reached the house. In the hall they parted.
-
-
-
-
- Chapter 59
-
- “My dear Lizzy, where can you have been walking to?” was a
- question which Elizabeth received from Jane as soon as she
- entered their room, and from all the others when they sat down to
- table. She had only to say in reply, that they had wandered
- about, till she was beyond her own knowledge. She coloured as she
- spoke; but neither that, nor anything else, awakened a suspicion
- of the truth.
-
- The evening passed quietly, unmarked by anything extraordinary.
- The acknowledged lovers talked and laughed, the unacknowledged
- were silent. Darcy was not of a disposition in which happiness
- overflows in mirth; and Elizabeth, agitated and confused, rather
- _knew_ that she was happy than _felt_ herself to be so; for,
- besides the immediate embarrassment, there were other evils
- before her. She anticipated what would be felt in the family when
- her situation became known; she was aware that no one liked him
- but Jane; and even feared that with the others it was a _dislike_
- which not all his fortune and consequence might do away.
-
- At night she opened her heart to Jane. Though suspicion was very
- far from Miss Bennet’s general habits, she was absolutely
- incredulous here.
-
- “You are joking, Lizzy. This cannot be!—engaged to Mr. Darcy! No,
- no, you shall not deceive me. I know it to be impossible.”
-
- “This is a wretched beginning indeed! My sole dependence was on
- you; and I am sure nobody else will believe me, if you do not.
- Yet, indeed, I am in earnest. I speak nothing but the truth. He
- still loves me, and we are engaged.”
-
- Jane looked at her doubtingly. “Oh, Lizzy! it cannot be. I know
- how much you dislike him.”
-
- “You know nothing of the matter. _That_ is all to be forgot.
- Perhaps I did not always love him so well as I do now. But in
- such cases as these, a good memory is unpardonable. This is the
- last time I shall ever remember it myself.”
-
- Miss Bennet still looked all amazement. Elizabeth again, and more
- seriously assured her of its truth.
-
- “Good Heaven! can it be really so! Yet now I must believe you,”
- cried Jane. “My dear, dear Lizzy, I would—I do congratulate
- you—but are you certain? forgive the question—are you quite
- certain that you can be happy with him?”
-
- “There can be no doubt of that. It is settled between us already,
- that we are to be the happiest couple in the world. But are you
- pleased, Jane? Shall you like to have such a brother?”
-
- “Very, very much. Nothing could give either Bingley or myself
- more delight. But we considered it, we talked of it as
- impossible. And do you really love him quite well enough? Oh,
- Lizzy! do anything rather than marry without affection. Are you
- quite sure that you feel what you ought to do?”
-
- “Oh, yes! You will only think I feel _more_ than I ought to do,
- when I tell you all.”
-
- “What do you mean?”
-
- “Why, I must confess that I love him better than I do Bingley. I
- am afraid you will be angry.”
-
- “My dearest sister, now _be_ serious. I want to talk very
- seriously. Let me know every thing that I am to know, without
- delay. Will you tell me how long you have loved him?”
-
- “It has been coming on so gradually, that I hardly know when it
- began. But I believe I must date it from my first seeing his
- beautiful grounds at Pemberley.”
-
- Another entreaty that she would be serious, however, produced the
- desired effect; and she soon satisfied Jane by her solemn
- assurances of attachment. When convinced on that article, Miss
- Bennet had nothing further to wish.
-
- “Now I am quite happy,” said she, “for you will be as happy as
- myself. I always had a value for him. Were it for nothing but his
- love of you, I must always have esteemed him; but now, as
- Bingley’s friend and your husband, there can be only Bingley and
- yourself more dear to me. But Lizzy, you have been very sly, very
- reserved with me. How little did you tell me of what passed at
- Pemberley and Lambton! I owe all that I know of it to another,
- not to you.”
-
- Elizabeth told her the motives of her secrecy. She had been
- unwilling to mention Bingley; and the unsettled state of her own
- feelings had made her equally avoid the name of his friend. But
- now she would no longer conceal from her his share in Lydia’s
- marriage. All was acknowledged, and half the night spent in
- conversation.
-
- “Good gracious!” cried Mrs. Bennet, as she stood at a window the
- next morning, “if that disagreeable Mr. Darcy is not coming here
- again with our dear Bingley! What can he mean by being so
- tiresome as to be always coming here? I had no notion but he
- would go a-shooting, or something or other, and not disturb us
- with his company. What shall we do with him? Lizzy, you must walk
- out with him again, that he may not be in Bingley’s way.”
-
- Elizabeth could hardly help laughing at so convenient a proposal;
- yet was really vexed that her mother should be always giving him
- such an epithet.
-
- As soon as they entered, Bingley looked at her so expressively,
- and shook hands with such warmth, as left no doubt of his good
- information; and he soon afterwards said aloud, “Mrs. Bennet,
- have you no more lanes hereabouts in which Lizzy may lose her way
- again to-day?”
-
- “I advise Mr. Darcy, and Lizzy, and Kitty,” said Mrs. Bennet, “to
- walk to Oakham Mount this morning. It is a nice long walk, and
- Mr. Darcy has never seen the view.”
-
- “It may do very well for the others,” replied Mr. Bingley; “but I
- am sure it will be too much for Kitty. Won’t it, Kitty?” Kitty
- owned that she had rather stay at home. Darcy professed a great
- curiosity to see the view from the Mount, and Elizabeth silently
- consented. As she went up stairs to get ready, Mrs. Bennet
- followed her, saying:
-
- “I am quite sorry, Lizzy, that you should be forced to have that
- disagreeable man all to yourself. But I hope you will not mind
- it: it is all for Jane’s sake, you know; and there is no occasion
- for talking to him, except just now and then. So, do not put
- yourself to inconvenience.”
-
- During their walk, it was resolved that Mr. Bennet’s consent
- should be asked in the course of the evening. Elizabeth reserved
- to herself the application for her mother’s. She could not
- determine how her mother would take it; sometimes doubting
- whether all his wealth and grandeur would be enough to overcome
- her abhorrence of the man. But whether she were violently set
- against the match, or violently delighted with it, it was certain
- that her manner would be equally ill adapted to do credit to her
- sense; and she could no more bear that Mr. Darcy should hear the
- first raptures of her joy, than the first vehemence of her
- disapprobation.
-
- In the evening, soon after Mr. Bennet withdrew to the library,
- she saw Mr. Darcy rise also and follow him, and her agitation on
- seeing it was extreme. She did not fear her father’s opposition,
- but he was going to be made unhappy; and that it should be
- through her means—that _she_, his favourite child, should be
- distressing him by her choice, should be filling him with fears
- and regrets in disposing of her—was a wretched reflection, and
- she sat in misery till Mr. Darcy appeared again, when, looking at
- him, she was a little relieved by his smile. In a few minutes he
- approached the table where she was sitting with Kitty; and, while
- pretending to admire her work said in a whisper, “Go to your
- father, he wants you in the library.” She was gone directly.
-
- Her father was walking about the room, looking grave and anxious.
- “Lizzy,” said he, “what are you doing? Are you out of your
- senses, to be accepting this man? Have not you always hated him?”
-
- How earnestly did she then wish that her former opinions had been
- more reasonable, her expressions more moderate! It would have
- spared her from explanations and professions which it was
- exceedingly awkward to give; but they were now necessary, and she
- assured him, with some confusion, of her attachment to Mr. Darcy.
-
- “Or, in other words, you are determined to have him. He is rich,
- to be sure, and you may have more fine clothes and fine carriages
- than Jane. But will they make you happy?”
-
- “Have you any other objection,” said Elizabeth, “than your belief
- of my indifference?”
-
- “None at all. We all know him to be a proud, unpleasant sort of
- man; but this would be nothing if you really liked him.”
-
- “I do, I do like him,” she replied, with tears in her eyes, “I
- love him. Indeed he has no improper pride. He is perfectly
- amiable. You do not know what he really is; then pray do not pain
- me by speaking of him in such terms.”
-
- “Lizzy,” said her father, “I have given him my consent. He is the
- kind of man, indeed, to whom I should never dare refuse anything,
- which he condescended to ask. I now give it to _you_, if you are
- resolved on having him. But let me advise you to think better of
- it. I know your disposition, Lizzy. I know that you could be
- neither happy nor respectable, unless you truly esteemed your
- husband; unless you looked up to him as a superior. Your lively
- talents would place you in the greatest danger in an unequal
- marriage. You could scarcely escape discredit and misery. My
- child, let me not have the grief of seeing _you_ unable to
- respect your partner in life. You know not what you are about.”
-
- Elizabeth, still more affected, was earnest and solemn in her
- reply; and at length, by repeated assurances that Mr. Darcy was
- really the object of her choice, by explaining the gradual change
- which her estimation of him had undergone, relating her absolute
- certainty that his affection was not the work of a day, but had
- stood the test of many months’ suspense, and enumerating with
- energy all his good qualities, she did conquer her father’s
- incredulity, and reconcile him to the match.
-
- “Well, my dear,” said he, when she ceased speaking, “I have no
- more to say. If this be the case, he deserves you. I could not
- have parted with you, my Lizzy, to anyone less worthy.”
-
- To complete the favourable impression, she then told him what Mr.
- Darcy had voluntarily done for Lydia. He heard her with
- astonishment.
-
- “This is an evening of wonders, indeed! And so, Darcy did every
- thing; made up the match, gave the money, paid the fellow’s
- debts, and got him his commission! So much the better. It will
- save me a world of trouble and economy. Had it been your uncle’s
- doing, I must and _would_ have paid him; but these violent young
- lovers carry every thing their own way. I shall offer to pay him
- to-morrow; he will rant and storm about his love for you, and
- there will be an end of the matter.”
-
- He then recollected her embarrassment a few days before, on his
- reading Mr. Collins’s letter; and after laughing at her some
- time, allowed her at last to go—saying, as she quitted the room,
- “If any young men come for Mary or Kitty, send them in, for I am
- quite at leisure.”
-
- Elizabeth’s mind was now relieved from a very heavy weight; and,
- after half an hour’s quiet reflection in her own room, she was
- able to join the others with tolerable composure. Every thing was
- too recent for gaiety, but the evening passed tranquilly away;
- there was no longer anything material to be dreaded, and the
- comfort of ease and familiarity would come in time.
-
- When her mother went up to her dressing-room at night, she
- followed her, and made the important communication. Its effect
- was most extraordinary; for on first hearing it, Mrs. Bennet sat
- quite still, and unable to utter a syllable. Nor was it under
- many, many minutes that she could comprehend what she heard;
- though not in general backward to credit what was for the
- advantage of her family, or that came in the shape of a lover to
- any of them. She began at length to recover, to fidget about in
- her chair, get up, sit down again, wonder, and bless herself.
-
- “Good gracious! Lord bless me! only think! dear me! Mr. Darcy!
- Who would have thought it! And is it really true? Oh! my sweetest
- Lizzy! how rich and how great you will be! What pin-money, what
- jewels, what carriages you will have! Jane’s is nothing to
- it—nothing at all. I am so pleased—so happy. Such a charming
- man!—so handsome! so tall!—Oh, my dear Lizzy! pray apologise for
- my having disliked him so much before. I hope he will overlook
- it. Dear, dear Lizzy. A house in town! Every thing that is
- charming! Three daughters married! Ten thousand a year! Oh, Lord!
- What will become of me. I shall go distracted.”
-
- This was enough to prove that her approbation need not be
- doubted: and Elizabeth, rejoicing that such an effusion was heard
- only by herself, soon went away. But before she had been three
- minutes in her own room, her mother followed her.
-
- “My dearest child,” she cried, “I can think of nothing else! Ten
- thousand a year, and very likely more! ’Tis as good as a Lord!
- And a special licence. You must and shall be married by a special
- licence. But my dearest love, tell me what dish Mr. Darcy is
- particularly fond of, that I may have it to-morrow.”
-
- This was a sad omen of what her mother’s behaviour to the
- gentleman himself might be; and Elizabeth found that, though in
- the certain possession of his warmest affection, and secure of
- her relations’ consent, there was still something to be wished
- for. But the morrow passed off much better than she expected; for
- Mrs. Bennet luckily stood in such awe of her intended son-in-law
- that she ventured not to speak to him, unless it was in her power
- to offer him any attention, or mark her deference for his
- opinion.
-
- Elizabeth had the satisfaction of seeing her father taking pains
- to get acquainted with him; and Mr. Bennet soon assured her that
- he was rising every hour in his esteem.
-
- “I admire all my three sons-in-law highly,” said he. “Wickham,
- perhaps, is my favourite; but I think I shall like _your_ husband
- quite as well as Jane’s.”
-
-
-
-
- Chapter 60
-
- Elizabeth’s spirits soon rising to playfulness again, she wanted
- Mr. Darcy to account for his having ever fallen in love with her.
- “How could you begin?” said she. “I can comprehend your going on
- charmingly, when you had once made a beginning; but what could
- set you off in the first place?”
-
- “I cannot fix on the hour, or the spot, or the look, or the
- words, which laid the foundation. It is too long ago. I was in
- the middle before I knew that I _had_ begun.”
-
- “My beauty you had early withstood, and as for my manners—my
- behaviour to _you_ was at least always bordering on the uncivil,
- and I never spoke to you without rather wishing to give you pain
- than not. Now be sincere; did you admire me for my impertinence?”
-
- “For the liveliness of your mind, I did.”
-
- “You may as well call it impertinence at once. It was very little
- less. The fact is, that you were sick of civility, of deference,
- of officious attention. You were disgusted with the women who
- were always speaking, and looking, and thinking for _your_
- approbation alone. I roused, and interested you, because I was so
- unlike _them_. Had you not been really amiable, you would have
- hated me for it; but in spite of the pains you took to disguise
- yourself, your feelings were always noble and just; and in your
- heart, you thoroughly despised the persons who so assiduously
- courted you. There—I have saved you the trouble of accounting for
- it; and really, all things considered, I begin to think it
- perfectly reasonable. To be sure, you knew no actual good of
- me—but nobody thinks of _that_ when they fall in love.”
-
- “Was there no good in your affectionate behaviour to Jane while
- she was ill at Netherfield?”
-
- “Dearest Jane! who could have done less for her? But make a
- virtue of it by all means. My good qualities are under your
- protection, and you are to exaggerate them as much as possible;
- and, in return, it belongs to me to find occasions for teasing
- and quarrelling with you as often as may be; and I shall begin
- directly by asking you what made you so unwilling to come to the
- point at last. What made you so shy of me, when you first called,
- and afterwards dined here? Why, especially, when you called, did
- you look as if you did not care about me?”
-
- “Because you were grave and silent, and gave me no
- encouragement.”
-
- “But I was embarrassed.”
-
- “And so was I.”
-
- “You might have talked to me more when you came to dinner.”
-
- “A man who had felt less, might.”
-
- “How unlucky that you should have a reasonable answer to give,
- and that I should be so reasonable as to admit it! But I wonder
- how long you _would_ have gone on, if you had been left to
- yourself. I wonder when you _would_ have spoken, if I had not
- asked you! My resolution of thanking you for your kindness to
- Lydia had certainly great effect. _Too much_, I am afraid; for
- what becomes of the moral, if our comfort springs from a breach
- of promise? for I ought not to have mentioned the subject. This
- will never do.”
-
- “You need not distress yourself. The moral will be perfectly
- fair. Lady Catherine’s unjustifiable endeavours to separate us
- were the means of removing all my doubts. I am not indebted for
- my present happiness to your eager desire of expressing your
- gratitude. I was not in a humour to wait for any opening of
- yours. My aunt’s intelligence had given me hope, and I was
- determined at once to know every thing.”
-
- “Lady Catherine has been of infinite use, which ought to make her
- happy, for she loves to be of use. But tell me, what did you come
- down to Netherfield for? Was it merely to ride to Longbourn and
- be embarrassed? or had you intended any more serious
- consequence?”
-
- “My real purpose was to see _you_, and to judge, if I could,
- whether I might ever hope to make you love me. My avowed one, or
- what I avowed to myself, was to see whether your sister were
- still partial to Bingley, and if she were, to make the confession
- to him which I have since made.”
-
- “Shall you ever have courage to announce to Lady Catherine what
- is to befall her?”
-
- “I am more likely to want more time than courage, Elizabeth. But
- it ought to be done, and if you will give me a sheet of paper, it
- shall be done directly.”
-
- “And if I had not a letter to write myself, I might sit by you
- and admire the evenness of your writing, as another young lady
- once did. But I have an aunt, too, who must not be longer
- neglected.”
-
- From an unwillingness to confess how much her intimacy with Mr.
- Darcy had been over-rated, Elizabeth had never yet answered Mrs.
- Gardiner’s long letter; but now, having _that_ to communicate
- which she knew would be most welcome, she was almost ashamed to
- find that her uncle and aunt had already lost three days of
- happiness, and immediately wrote as follows:
-
- “I would have thanked you before, my dear aunt, as I ought to
- have done, for your long, kind, satisfactory, detail of
- particulars; but to say the truth, I was too cross to write. You
- supposed more than really existed. But _now_ suppose as much as
- you choose; give a loose rein to your fancy, indulge your
- imagination in every possible flight which the subject will
- afford, and unless you believe me actually married, you cannot
- greatly err. You must write again very soon, and praise him a
- great deal more than you did in your last. I thank you, again and
- again, for not going to the Lakes. How could I be so silly as to
- wish it! Your idea of the ponies is delightful. We will go round
- the Park every day. I am the happiest creature in the world.
- Perhaps other people have said so before, but not one with such
- justice. I am happier even than Jane; she only smiles, I laugh.
- Mr. Darcy sends you all the love in the world that he can spare
- from me. You are all to come to Pemberley at Christmas. Yours,
- etc.”
-
- Mr. Darcy’s letter to Lady Catherine was in a different style;
- and still different from either was what Mr. Bennet sent to Mr.
- Collins, in reply to his last.
-
- “Dear Sir,
- “I must trouble you once more for congratulations. Elizabeth will
- soon be the wife of Mr. Darcy. Console Lady Catherine as well as
- you can. But, if I were you, I would stand by the nephew. He has
- more to give.
-
- “Yours sincerely, etc.”
-
- Miss Bingley’s congratulations to her brother, on his approaching
- marriage, were all that was affectionate and insincere. She wrote
- even to Jane on the occasion, to express her delight, and repeat
- all her former professions of regard. Jane was not deceived, but
- she was affected; and though feeling no reliance on her, could
- not help writing her a much kinder answer than she knew was
- deserved.
-
- The joy which Miss Darcy expressed on receiving similar
- information, was as sincere as her brother’s in sending it. Four
- sides of paper were insufficient to contain all her delight, and
- all her earnest desire of being loved by her sister.
-
- Before any answer could arrive from Mr. Collins, or any
- congratulations to Elizabeth from his wife, the Longbourn family
- heard that the Collinses were come themselves to Lucas Lodge. The
- reason of this sudden removal was soon evident. Lady Catherine
- had been rendered so exceedingly angry by the contents of her
- nephew’s letter, that Charlotte, really rejoicing in the match,
- was anxious to get away till the storm was blown over. At such a
- moment, the arrival of her friend was a sincere pleasure to
- Elizabeth, though in the course of their meetings she must
- sometimes think the pleasure dearly bought, when she saw Mr.
- Darcy exposed to all the parading and obsequious civility of her
- husband. He bore it, however, with admirable calmness. He could
- even listen to Sir William Lucas, when he complimented him on
- carrying away the brightest jewel of the country, and expressed
- his hopes of their all meeting frequently at St. James’s, with
- very decent composure. If he did shrug his shoulders, it was not
- till Sir William was out of sight.
-
- Mrs. Phillips’s vulgarity was another, and perhaps a greater, tax
- on his forbearance; and though Mrs. Phillips, as well as her
- sister, stood in too much awe of him to speak with the
- familiarity which Bingley’s good humour encouraged, yet, whenever
- she _did_ speak, she must be vulgar. Nor was her respect for him,
- though it made her more quiet, at all likely to make her more
- elegant. Elizabeth did all she could to shield him from the
- frequent notice of either, and was ever anxious to keep him to
- herself, and to those of her family with whom he might converse
- without mortification; and though the uncomfortable feelings
- arising from all this took from the season of courtship much of
- its pleasure, it added to the hope of the future; and she looked
- forward with delight to the time when they should be removed from
- society so little pleasing to either, to all the comfort and
- elegance of their family party at Pemberley.
-
-
-
-
- Chapter 61
-
- Happy for all her maternal feelings was the day on which Mrs.
- Bennet got rid of her two most deserving daughters. With what
- delighted pride she afterwards visited Mrs. Bingley, and talked
- of Mrs. Darcy, may be guessed. I wish I could say, for the sake
- of her family, that the accomplishment of her earnest desire in
- the establishment of so many of her children produced so happy an
- effect as to make her a sensible, amiable, well-informed woman
- for the rest of her life; though perhaps it was lucky for her
- husband, who might not have relished domestic felicity in so
- unusual a form, that she still was occasionally nervous and
- invariably silly.
-
- Mr. Bennet missed his second daughter exceedingly; his affection
- for her drew him oftener from home than anything else could do.
- He delighted in going to Pemberley, especially when he was least
- expected.
-
- Mr. Bingley and Jane remained at Netherfield only a twelvemonth.
- So near a vicinity to her mother and Meryton relations was not
- desirable even to _his_ easy temper, or _her_ affectionate heart.
- The darling wish of his sisters was then gratified; he bought an
- estate in a neighbouring county to Derbyshire, and Jane and
- Elizabeth, in addition to every other source of happiness, were
- within thirty miles of each other.
-
- Kitty, to her very material advantage, spent the chief of her
- time with her two elder sisters. In society so superior to what
- she had generally known, her improvement was great. She was not
- of so ungovernable a temper as Lydia; and, removed from the
- influence of Lydia’s example, she became, by proper attention and
- management, less irritable, less ignorant, and less insipid. From
- the further disadvantage of Lydia’s society she was of course
- carefully kept, and though Mrs. Wickham frequently invited her to
- come and stay with her, with the promise of balls and young men,
- her father would never consent to her going.
-
- Mary was the only daughter who remained at home; and she was
- necessarily drawn from the pursuit of accomplishments by Mrs.
- Bennet’s being quite unable to sit alone. Mary was obliged to mix
- more with the world, but she could still moralize over every
- morning visit; and as she was no longer mortified by comparisons
- between her sisters’ beauty and her own, it was suspected by her
- father that she submitted to the change without much reluctance.
-
- As for Wickham and Lydia, their characters suffered no revolution
- from the marriage of her sisters. He bore with philosophy the
- conviction that Elizabeth must now become acquainted with
- whatever of his ingratitude and falsehood had before been unknown
- to her; and in spite of every thing, was not wholly without hope
- that Darcy might yet be prevailed on to make his fortune. The
- congratulatory letter which Elizabeth received from Lydia on her
- marriage, explained to her that, by his wife at least, if not by
- himself, such a hope was cherished. The letter was to this
- effect:
-
- “My dear Lizzy,
- “I wish you joy. If you love Mr. Darcy half as well as I do my
- dear Wickham, you must be very happy. It is a great comfort to
- have you so rich, and when you have nothing else to do, I hope
- you will think of us. I am sure Wickham would like a place at
- court very much, and I do not think we shall have quite money
- enough to live upon without some help. Any place would do, of
- about three or four hundred a year; but however, do not speak to
- Mr. Darcy about it, if you had rather not.
-
- “Yours, etc.”
-
- As it happened that Elizabeth had much rather not, she
- endeavoured in her answer to put an end to every entreaty and
- expectation of the kind. Such relief, however, as it was in her
- power to afford, by the practice of what might be called economy
- in her own private expences, she frequently sent them. It had
- always been evident to her that such an income as theirs, under
- the direction of two persons so extravagant in their wants, and
- heedless of the future, must be very insufficient to their
- support; and whenever they changed their quarters, either Jane or
- herself were sure of being applied to for some little assistance
- towards discharging their bills. Their manner of living, even
- when the restoration of peace dismissed them to a home, was
- unsettled in the extreme. They were always moving from place to
- place in quest of a cheap situation, and always spending more
- than they ought. His affection for her soon sunk into
- indifference; hers lasted a little longer; and in spite of her
- youth and her manners, she retained all the claims to reputation
- which her marriage had given her.
-
- Though Darcy could never receive _him_ at Pemberley, yet, for
- Elizabeth’s sake, he assisted him further in his profession.
- Lydia was occasionally a visitor there, when her husband was gone
- to enjoy himself in London or Bath; and with the Bingleys they
- both of them frequently staid so long, that even Bingley’s good
- humour was overcome, and he proceeded so far as to _talk_ of
- giving them a hint to be gone.
-
- Miss Bingley was very deeply mortified by Darcy’s marriage; but
- as she thought it advisable to retain the right of visiting at
- Pemberley, she dropt all her resentment; was fonder than ever of
- Georgiana, almost as attentive to Darcy as heretofore, and paid
- off every arrear of civility to Elizabeth.
-
- Pemberley was now Georgiana’s home; and the attachment of the
- sisters was exactly what Darcy had hoped to see. They were able
- to love each other even as well as they intended. Georgiana had
- the highest opinion in the world of Elizabeth; though at first
- she often listened with an astonishment bordering on alarm at her
- lively, sportive, manner of talking to her brother. He, who had
- always inspired in herself a respect which almost overcame her
- affection, she now saw the object of open pleasantry. Her mind
- received knowledge which had never before fallen in her way. By
- Elizabeth’s instructions, she began to comprehend that a woman
- may take liberties with her husband which a brother will not
- always allow in a sister more than ten years younger than
- himself.
-
- Lady Catherine was extremely indignant on the marriage of her
- nephew; and as she gave way to all the genuine frankness of her
- character in her reply to the letter which announced its
- arrangement, she sent him language so very abusive, especially of
- Elizabeth, that for some time all intercourse was at an end. But
- at length, by Elizabeth’s persuasion, he was prevailed on to
- overlook the offence, and seek a reconciliation; and, after a
- little further resistance on the part of his aunt, her resentment
- gave way, either to her affection for him, or her curiosity to
- see how his wife conducted herself; and she condescended to wait
- on them at Pemberley, in spite of that pollution which its woods
- had received, not merely from the presence of such a mistress,
- but the visits of her uncle and aunt from the city.
-
- With the Gardiners, they were always on the most intimate terms.
- Darcy, as well as Elizabeth, really loved them; and they were
- both ever sensible of the warmest gratitude towards the persons
- who, by bringing her into Derbyshire, had been the means of
- uniting them.
-
-
-
-
-End of the Project Gutenberg EBook of Pride and Prejudice, by Jane Austen
-
-*** END OF THIS PROJECT GUTENBERG EBOOK PRIDE AND PREJUDICE ***
-
-***** This file should be named 1342-0.txt or 1342-0.zip *****
-This and all associated files of various formats will be found in:
- http://www.gutenberg.org/1/3/4/1342/
-
-Produced by Anonymous Volunteers, and David Widger
-
-Updated editions will replace the previous one--the old editions
-will be renamed.
-
-Creating the works from public domain print editions means that no
-one owns a United States copyright in these works, so the Foundation
-(and you!) can copy and distribute it in the United States without
-permission and without paying copyright royalties. Special rules,
-set forth in the General Terms of Use part of this license, apply to
-copying and distributing Project Gutenberg-tm electronic works to
-protect the PROJECT GUTENBERG-tm concept and trademark. Project
-Gutenberg is a registered trademark, and may not be used if you
-charge for the eBooks, unless you receive specific permission. If you
-do not charge anything for copies of this eBook, complying with the
-rules is very easy. You may use this eBook for nearly any purpose
-such as creation of derivative works, reports, performances and
-research. They may be modified and printed and given away--you may do
-practically ANYTHING with public domain eBooks. Redistribution is
-subject to the trademark license, especially commercial
-redistribution.
-
-
-
-*** START: FULL LICENSE ***
-
-THE FULL PROJECT GUTENBERG LICENSE
-PLEASE READ THIS BEFORE YOU DISTRIBUTE OR USE THIS WORK
-
-To protect the Project Gutenberg-tm mission of promoting the free
-distribution of electronic works, by using or distributing this work
-(or any other work associated in any way with the phrase “Project
-Gutenberg”), you agree to comply with all the terms of the Full Project
-Gutenberg-tm License (available with this file or online at
-http://gutenberg.org/license).
-
-
-Section 1. General Terms of Use and Redistributing Project Gutenberg-tm
-electronic works
-
-1.A. By reading or using any part of this Project Gutenberg-tm
-electronic work, you indicate that you have read, understand, agree to
-and accept all the terms of this license and intellectual property
-(trademark/copyright) agreement. If you do not agree to abide by all
-the terms of this agreement, you must cease using and return or destroy
-all copies of Project Gutenberg-tm electronic works in your possession.
-If you paid a fee for obtaining a copy of or access to a Project
-Gutenberg-tm electronic work and you do not agree to be bound by the
-terms of this agreement, you may obtain a refund from the person or
-entity to whom you paid the fee as set forth in paragraph 1.E.8.
-
-1.B. “Project Gutenberg” is a registered trademark. It may only be
-used on or associated in any way with an electronic work by people who
-agree to be bound by the terms of this agreement. There are a few
-things that you can do with most Project Gutenberg-tm electronic works
-even without complying with the full terms of this agreement. See
-paragraph 1.C below. There are a lot of things you can do with Project
-Gutenberg-tm electronic works if you follow the terms of this agreement
-and help preserve free future access to Project Gutenberg-tm electronic
-works. See paragraph 1.E below.
-
-1.C. The Project Gutenberg Literary Archive Foundation (“the Foundation”
- or PGLAF), owns a compilation copyright in the collection of Project
-Gutenberg-tm electronic works. Nearly all the individual works in the
-collection are in the public domain in the United States. If an
-individual work is in the public domain in the United States and you are
-located in the United States, we do not claim a right to prevent you from
-copying, distributing, performing, displaying or creating derivative
-works based on the work as long as all references to Project Gutenberg
-are removed. Of course, we hope that you will support the Project
-Gutenberg-tm mission of promoting free access to electronic works by
-freely sharing Project Gutenberg-tm works in compliance with the terms of
-this agreement for keeping the Project Gutenberg-tm name associated with
-the work. You can easily comply with the terms of this agreement by
-keeping this work in the same format with its attached full Project
-Gutenberg-tm License when you share it without charge with others.
-
-1.D. The copyright laws of the place where you are located also govern
-what you can do with this work. Copyright laws in most countries are in
-a constant state of change. If you are outside the United States, check
-the laws of your country in addition to the terms of this agreement
-before downloading, copying, displaying, performing, distributing or
-creating derivative works based on this work or any other Project
-Gutenberg-tm work. The Foundation makes no representations concerning
-the copyright status of any work in any country outside the United
-States.
-
-1.E. Unless you have removed all references to Project Gutenberg:
-
-1.E.1. The following sentence, with active links to, or other immediate
-access to, the full Project Gutenberg-tm License must appear prominently
-whenever any copy of a Project Gutenberg-tm work (any work on which the
-phrase “Project Gutenberg” appears, or with which the phrase “Project
-Gutenberg” is associated) is accessed, displayed, performed, viewed,
-copied or distributed:
-
-This eBook is for the use of anyone anywhere at no cost and with
-almost no restrictions whatsoever. You may copy it, give it away or
-re-use it under the terms of the Project Gutenberg License included
-with this eBook or online at www.gutenberg.org
-
-1.E.2. If an individual Project Gutenberg-tm electronic work is derived
-from the public domain (does not contain a notice indicating that it is
-posted with permission of the copyright holder), the work can be copied
-and distributed to anyone in the United States without paying any fees
-or charges. If you are redistributing or providing access to a work
-with the phrase “Project Gutenberg” associated with or appearing on the
-work, you must comply either with the requirements of paragraphs 1.E.1
-through 1.E.7 or obtain permission for the use of the work and the
-Project Gutenberg-tm trademark as set forth in paragraphs 1.E.8 or
-1.E.9.
-
-1.E.3. If an individual Project Gutenberg-tm electronic work is posted
-with the permission of the copyright holder, your use and distribution
-must comply with both paragraphs 1.E.1 through 1.E.7 and any additional
-terms imposed by the copyright holder. Additional terms will be linked
-to the Project Gutenberg-tm License for all works posted with the
-permission of the copyright holder found at the beginning of this work.
-
-1.E.4. Do not unlink or detach or remove the full Project Gutenberg-tm
-License terms from this work, or any files containing a part of this
-work or any other work associated with Project Gutenberg-tm.
-
-1.E.5. Do not copy, display, perform, distribute or redistribute this
-electronic work, or any part of this electronic work, without
-prominently displaying the sentence set forth in paragraph 1.E.1 with
-active links or immediate access to the full terms of the Project
-Gutenberg-tm License.
-
-1.E.6. You may convert to and distribute this work in any binary,
-compressed, marked up, nonproprietary or proprietary form, including any
-word processing or hypertext form. However, if you provide access to or
-distribute copies of a Project Gutenberg-tm work in a format other than
-“Plain Vanilla ASCII” or other format used in the official version
-posted on the official Project Gutenberg-tm web site (www.gutenberg.org),
-you must, at no additional cost, fee or expense to the user, provide a
-copy, a means of exporting a copy, or a means of obtaining a copy upon
-request, of the work in its original “Plain Vanilla ASCII” or other
-form. Any alternate format must include the full Project Gutenberg-tm
-License as specified in paragraph 1.E.1.
-
-1.E.7. Do not charge a fee for access to, viewing, displaying,
-performing, copying or distributing any Project Gutenberg-tm works
-unless you comply with paragraph 1.E.8 or 1.E.9.
-
-1.E.8. You may charge a reasonable fee for copies of or providing
-access to or distributing Project Gutenberg-tm electronic works provided
-that
-
-- You pay a royalty fee of 20% of the gross profits you derive from
- the use of Project Gutenberg-tm works calculated using the method
- you already use to calculate your applicable taxes. The fee is
- owed to the owner of the Project Gutenberg-tm trademark, but he
- has agreed to donate royalties under this paragraph to the
- Project Gutenberg Literary Archive Foundation. Royalty payments
- must be paid within 60 days following each date on which you
- prepare (or are legally required to prepare) your periodic tax
- returns. Royalty payments should be clearly marked as such and
- sent to the Project Gutenberg Literary Archive Foundation at the
- address specified in Section 4, “Information about donations to
- the Project Gutenberg Literary Archive Foundation.”
-
-- You provide a full refund of any money paid by a user who notifies
- you in writing (or by e-mail) within 30 days of receipt that s/he
- does not agree to the terms of the full Project Gutenberg-tm
- License. You must require such a user to return or
- destroy all copies of the works possessed in a physical medium
- and discontinue all use of and all access to other copies of
- Project Gutenberg-tm works.
-
-- You provide, in accordance with paragraph 1.F.3, a full refund of any
- money paid for a work or a replacement copy, if a defect in the
- electronic work is discovered and reported to you within 90 days
- of receipt of the work.
-
-- You comply with all other terms of this agreement for free
- distribution of Project Gutenberg-tm works.
-
-1.E.9. If you wish to charge a fee or distribute a Project Gutenberg-tm
-electronic work or group of works on different terms than are set
-forth in this agreement, you must obtain permission in writing from
-both the Project Gutenberg Literary Archive Foundation and Michael
-Hart, the owner of the Project Gutenberg-tm trademark. Contact the
-Foundation as set forth in Section 3 below.
-
-1.F.
-
-1.F.1. Project Gutenberg volunteers and employees expend considerable
-effort to identify, do copyright research on, transcribe and proofread
-public domain works in creating the Project Gutenberg-tm
-collection. Despite these efforts, Project Gutenberg-tm electronic
-works, and the medium on which they may be stored, may contain
-“Defects,” such as, but not limited to, incomplete, inaccurate or
-corrupt data, transcription errors, a copyright or other intellectual
-property infringement, a defective or damaged disk or other medium, a
-computer virus, or computer codes that damage or cannot be read by
-your equipment.
-
-1.F.2. LIMITED WARRANTY, DISCLAIMER OF DAMAGES - Except for the “Right
-of Replacement or Refund” described in paragraph 1.F.3, the Project
-Gutenberg Literary Archive Foundation, the owner of the Project
-Gutenberg-tm trademark, and any other party distributing a Project
-Gutenberg-tm electronic work under this agreement, disclaim all
-liability to you for damages, costs and expenses, including legal
-fees. YOU AGREE THAT YOU HAVE NO REMEDIES FOR NEGLIGENCE, STRICT
-LIABILITY, BREACH OF WARRANTY OR BREACH OF CONTRACT EXCEPT THOSE
-PROVIDED IN PARAGRAPH F3. YOU AGREE THAT THE FOUNDATION, THE
-TRADEMARK OWNER, AND ANY DISTRIBUTOR UNDER THIS AGREEMENT WILL NOT BE
-LIABLE TO YOU FOR ACTUAL, DIRECT, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
-INCIDENTAL DAMAGES EVEN IF YOU GIVE NOTICE OF THE POSSIBILITY OF SUCH
-DAMAGE.
-
-1.F.3. LIMITED RIGHT OF REPLACEMENT OR REFUND - If you discover a
-defect in this electronic work within 90 days of receiving it, you can
-receive a refund of the money (if any) you paid for it by sending a
-written explanation to the person you received the work from. If you
-received the work on a physical medium, you must return the medium with
-your written explanation. The person or entity that provided you with
-the defective work may elect to provide a replacement copy in lieu of a
-refund. If you received the work electronically, the person or entity
-providing it to you may choose to give you a second opportunity to
-receive the work electronically in lieu of a refund. If the second copy
-is also defective, you may demand a refund in writing without further
-opportunities to fix the problem.
-
-1.F.4. Except for the limited right of replacement or refund set forth
-in paragraph 1.F.3, this work is provided to you 'AS-IS' WITH NO OTHER
-WARRANTIES OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO
-WARRANTIES OF MERCHANTIBILITY OR FITNESS FOR ANY PURPOSE.
-
-1.F.5. Some states do not allow disclaimers of certain implied
-warranties or the exclusion or limitation of certain types of damages.
-If any disclaimer or limitation set forth in this agreement violates the
-law of the state applicable to this agreement, the agreement shall be
-interpreted to make the maximum disclaimer or limitation permitted by
-the applicable state law. The invalidity or unenforceability of any
-provision of this agreement shall not void the remaining provisions.
-
-1.F.6. INDEMNITY - You agree to indemnify and hold the Foundation, the
-trademark owner, any agent or employee of the Foundation, anyone
-providing copies of Project Gutenberg-tm electronic works in accordance
-with this agreement, and any volunteers associated with the production,
-promotion and distribution of Project Gutenberg-tm electronic works,
-harmless from all liability, costs and expenses, including legal fees,
-that arise directly or indirectly from any of the following which you do
-or cause to occur: (a) distribution of this or any Project Gutenberg-tm
-work, (b) alteration, modification, or additions or deletions to any
-Project Gutenberg-tm work, and (c) any Defect you cause.
-
-
-Section 2. Information about the Mission of Project Gutenberg-tm
-
-Project Gutenberg-tm is synonymous with the free distribution of
-electronic works in formats readable by the widest variety of computers
-including obsolete, old, middle-aged and new computers. It exists
-because of the efforts of hundreds of volunteers and donations from
-people in all walks of life.
-
-Volunteers and financial support to provide volunteers with the
-assistance they need, are critical to reaching Project Gutenberg-tm’s
-goals and ensuring that the Project Gutenberg-tm collection will
-remain freely available for generations to come. In 2001, the Project
-Gutenberg Literary Archive Foundation was created to provide a secure
-and permanent future for Project Gutenberg-tm and future generations.
-To learn more about the Project Gutenberg Literary Archive Foundation
-and how your efforts and donations can help, see Sections 3 and 4
-and the Foundation web page at http://www.pglaf.org.
-
-
-Section 3. Information about the Project Gutenberg Literary Archive
-Foundation
-
-The Project Gutenberg Literary Archive Foundation is a non profit
-501(c)(3) educational corporation organized under the laws of the
-state of Mississippi and granted tax exempt status by the Internal
-Revenue Service. The Foundation’s EIN or federal tax identification
-number is 64-6221541. Its 501(c)(3) letter is posted at
-http://pglaf.org/fundraising. Contributions to the Project Gutenberg
-Literary Archive Foundation are tax deductible to the full extent
-permitted by U.S. federal laws and your state’s laws.
-
-The Foundation’s principal office is located at 4557 Melan Dr. S.
-Fairbanks, AK, 99712., but its volunteers and employees are scattered
-throughout numerous locations. Its business office is located at
-809 North 1500 West, Salt Lake City, UT 84116, (801) 596-1887, email
-business@pglaf.org. Email contact links and up to date contact
-information can be found at the Foundation’s web site and official
-page at http://pglaf.org
-
-For additional contact information:
- Dr. Gregory B. Newby
- Chief Executive and Director
- gbnewby@pglaf.org
-
-
-Section 4. Information about Donations to the Project Gutenberg
-Literary Archive Foundation
-
-Project Gutenberg-tm depends upon and cannot survive without wide
-spread public support and donations to carry out its mission of
-increasing the number of public domain and licensed works that can be
-freely distributed in machine readable form accessible by the widest
-array of equipment including outdated equipment. Many small donations
-($1 to $5,000) are particularly important to maintaining tax exempt
-status with the IRS.
-
-The Foundation is committed to complying with the laws regulating
-charities and charitable donations in all 50 states of the United
-States. Compliance requirements are not uniform and it takes a
-considerable effort, much paperwork and many fees to meet and keep up
-with these requirements. We do not solicit donations in locations
-where we have not received written confirmation of compliance. To
-SEND DONATIONS or determine the status of compliance for any
-particular state visit http://pglaf.org
-
-While we cannot and do not solicit contributions from states where we
-have not met the solicitation requirements, we know of no prohibition
-against accepting unsolicited donations from donors in such states who
-approach us with offers to donate.
-
-International donations are gratefully accepted, but we cannot make
-any statements concerning tax treatment of donations received from
-outside the United States. U.S. laws alone swamp our small staff.
-
-Please check the Project Gutenberg Web pages for current donation
-methods and addresses. Donations are accepted in a number of other
-ways including checks, online payments and credit card donations.
-To donate, please visit: http://pglaf.org/donate
-
-
-Section 5. General Information About Project Gutenberg-tm electronic
-works.
-
-Professor Michael S. Hart is the originator of the Project Gutenberg-tm
-concept of a library of electronic works that could be freely shared
-with anyone. For thirty years, he produced and distributed Project
-Gutenberg-tm eBooks with only a loose network of volunteer support.
-
-
-Project Gutenberg-tm eBooks are often created from several printed
-editions, all of which are confirmed as Public Domain in the U.S.
-unless a copyright notice is included. Thus, we do not necessarily
-keep eBooks in compliance with any particular paper edition.
-
-
-Most people start at our Web site which has the main PG search facility:
-
- http://www.gutenberg.org
-
-This Web site includes information about Project Gutenberg-tm,
-including how to make donations to the Project Gutenberg Literary
-Archive Foundation, how to help produce our new eBooks, and how to
-subscribe to our email newsletter to hear about new eBooks.
-`
diff --git a/toolkit/stringutils/highlight.go b/toolkit/stringutils/highlight.go
deleted file mode 100644
index c609cf2d..00000000
--- a/toolkit/stringutils/highlight.go
+++ /dev/null
@@ -1,80 +0,0 @@
-// SPDX-License-Identifier: MIT OR Unlicense
-
-package stringutils
-
-import (
- "strings"
-)
-
-// HighlightString takes in some content and locations and then inserts in/out
-// strings which can be used for highlighting around matching terms. For example
-// you could pass in "test" and have it return "test"
-// locations accepts output from regex.FindAllIndex IndexAllIgnoreCase or IndexAll
-func HighlightString(content string, locations [][]int, in string, out string) string {
- var str strings.Builder
-
- end := -1
- var found bool
-
- // Profiles show that most time is spent looking against the locations
- // so we generated a cache quickly to speed that process up
- highlightCache := map[int]bool{}
- for _, val := range locations {
- highlightCache[val[0]] = true
- }
-
- // Range over str which is rune aware so even if we get invalid
- // locations we should hopefully ignore them as the byte offset wont
- // match
- for i, x := range content {
- found = false
-
- _, ok := highlightCache[i]
- // Find which of the locations match
- // and if so write the start str
- if ok {
- for _, val := range locations {
-
- // We have a match where the outer index matches
- // against the val[0] which contains the location of the match
- if i == val[0] {
- // We only write the found str once per match and
- // only if we are not in the middle of one
- if !found && end <= 0 {
- str.WriteString(in)
- found = true
- }
-
- // Determine the expected end location for this match
- // and only if its further than the expected end do we
- // change to deal with overlaps if say we are trying to match
- // on t and tes against test where we want tes as the longest
- // match to be the end that's written
- y := val[1] - 1 // val[1] in this case is the length of the match
- if y > end {
- end = y
- }
- }
- }
- }
-
- // This deals with characters that are multi-byte and as such we never range over
- // the rest and as such we need to remember to close them off if we have gone past
- // their end. As such this needs to come before we write the current byte
- if end > 0 && i > end {
- str.WriteString(out)
- end = 0
- }
-
- str.WriteString(string(x))
-
- // If at the end, and its not -1 meaning the first char
- // which should never happen (I hope!) then write the end str
- if i == end && end != -1 {
- str.WriteString(out)
- end = 0
- }
- }
-
- return str.String()
-}
diff --git a/toolkit/stringutils/highlight_test.go b/toolkit/stringutils/highlight_test.go
deleted file mode 100644
index 283c3336..00000000
--- a/toolkit/stringutils/highlight_test.go
+++ /dev/null
@@ -1,169 +0,0 @@
-// SPDX-License-Identifier: MIT OR Unlicense
-
-package stringutils
-
-import (
- "regexp"
- "testing"
-)
-
-func TestHighlightStringSimple(t *testing.T) {
- loc := [][]int{}
- loc = append(loc, []int{0, 4})
-
- got := HighlightString("this", loc, "[in]", "[out]")
-
- expected := "[in]this[out]"
- if got != expected {
- t.Error("Expected", expected, "got", got)
- }
-}
-
-func TestHighlightStringCheckInOut(t *testing.T) {
- loc := [][]int{}
- loc = append(loc, []int{0, 4})
-
- got := HighlightString("this", loc, "__", "__")
-
- expected := "__this__"
- if got != expected {
- t.Error("Expected", expected, "got", got)
- }
-}
-
-func TestHighlightStringCheck2(t *testing.T) {
- loc := [][]int{}
- loc = append(loc, []int{0, 4})
-
- got := HighlightString("bing", loc, "__", "__")
-
- expected := "__bing__"
- if got != expected {
- t.Error("Expected", expected, "got", got)
- }
-}
-
-func TestHighlightStringCheckTwoWords(t *testing.T) {
- loc := [][]int{}
- loc = append(loc, []int{0, 4})
- loc = append(loc, []int{5, 9})
-
- got := HighlightString("this this", loc, "__", "__")
-
- expected := "__this__ __this__"
- if got != expected {
- t.Error("Expected", expected, "got", got)
- }
-}
-
-func TestHighlightStringCheckMixedWords(t *testing.T) {
- loc := [][]int{}
- loc = append(loc, []int{0, 4})
- loc = append(loc, []int{5, 9})
- loc = append(loc, []int{10, 19})
-
- got := HighlightString("this this something", loc, "__", "__")
-
- expected := "__this__ __this__ __something__"
- if got != expected {
- t.Error("Expected", expected, "got", got)
- }
-}
-
-func TestHighlightStringOverlapStart(t *testing.T) {
- loc := [][]int{}
- loc = append(loc, []int{0, 1})
- loc = append(loc, []int{0, 4})
-
- got := HighlightString("THIS", loc, "__", "__")
-
- expected := "__THIS__"
- if got != expected {
- t.Error("Expected", expected, "got", got)
- }
-}
-
-func TestHighlightStringOverlapMiddle(t *testing.T) {
- loc := [][]int{}
- loc = append(loc, []int{0, 4})
- loc = append(loc, []int{1, 2})
-
- got := HighlightString("this", loc, "__", "__")
-
- expected := "__this__"
- if got != expected {
- t.Error("Expected", expected, "got", got)
- }
-}
-
-func TestHighlightStringNoOverlapMiddleNextSame(t *testing.T) {
- loc := [][]int{}
- loc = append(loc, []int{0, 1})
- loc = append(loc, []int{1, 2})
-
- got := HighlightString("this", loc, "__", "__")
-
- expected := "__t____h__is"
- if got != expected {
- t.Error("Expected", expected, "got", got)
- }
-}
-
-func TestHighlightStringOverlapMiddleLonger(t *testing.T) {
- loc := [][]int{}
- loc = append(loc, []int{0, 2})
- loc = append(loc, []int{1, 4})
-
- got := HighlightString("this", loc, "__", "__")
-
- expected := "__this__"
- if got != expected {
- t.Error("Expected", expected, "got", got)
- }
-}
-
-func TestBugOne(t *testing.T) {
- loc := [][]int{}
- loc = append(loc, []int{10, 18})
-
- got := HighlightString("this is unexpected", loc, "__", "__")
-
- expected := "this is un__expected__"
- if got != expected {
- t.Error("Expected", expected, "got", got)
- }
-}
-
-func TestIntegrationRegex(t *testing.T) {
- r := regexp.MustCompile(`1`)
- haystack := "111"
-
- loc := r.FindAllIndex([]byte(haystack), -1)
- got := HighlightString(haystack, loc, "__", "__")
-
- if got != "__1____1____1__" {
- t.Error("Expected", "__1____1____1__", "got", got)
- }
-}
-
-func TestIntegrationIndexAll(t *testing.T) {
- haystack := "111"
-
- loc := IndexAll(haystack, "1", -1)
- got := HighlightString(haystack, loc, "__", "__")
-
- if got != "__1____1____1__" {
- t.Error("Expected", "__1____1____1__", "got", got)
- }
-}
-
-func TestIntegrationIndexAllIgnoreCaseUnicode(t *testing.T) {
- haystack := "111"
-
- loc := IndexAllIgnoreCase(haystack, "1", -1)
- got := HighlightString(haystack, loc, "__", "__")
-
- if got != "__1____1____1__" {
- t.Error("Expected", "__1____1____1__", "got", got)
- }
-}
diff --git a/toolkit/stringutils/index.go b/toolkit/stringutils/index.go
deleted file mode 100644
index 29ac8e32..00000000
--- a/toolkit/stringutils/index.go
+++ /dev/null
@@ -1,318 +0,0 @@
-// SPDX-License-Identifier: MIT OR Unlicense
-
-package stringutils
-
-import (
- "math"
- "sort"
- "strings"
- "sync"
- "unicode/utf8"
-)
-
-// IndexAll extracts all of the locations of a string inside another string
-// up-to the defined limit and does so without regular expressions
-// which makes it faster than FindAllIndex in most situations while
-// not being any slower. It performs worst when working against random
-// data.
-//
-// Some benchmark results to illustrate the point (find more in index_benchmark_test.go)
-//
-// BenchmarkFindAllIndex-8 2458844 480.0 ns/op
-// BenchmarkIndexAll-8 14819680 79.6 ns/op
-//
-// For pure literal searches IE no regular expression logic this method
-// is a drop in replacement for re.FindAllIndex but generally much faster.
-//
-// Similar to how FindAllIndex the limit option can be passed -1
-// to get all matches.
-//
-// Note that this method is explicitly case sensitive in its matching.
-// A return value of nil indicates no match.
-func IndexAll(haystack string, needle string, limit int) [][]int {
- // The below needed to avoid timeout crash found using go-fuzz
- if len(haystack) == 0 || len(needle) == 0 {
- return nil
- }
-
- // Return contains a slice of slices where index 0 is the location of the match in bytes
- // and index 1 contains the end location in bytes of the match
- var locs [][]int
-
- // Perform the first search outside the main loop to make the method
- // easier to understand
- searchText := haystack
- offSet := 0
- loc := strings.Index(searchText, needle)
-
- if limit <= -1 {
- // Similar to how regex FindAllString works
- // if we have -1 as the limit set to max to
- // try to get everything
- limit = math.MaxInt32
- } else {
- // Increment by one because we do count++ at the start of the loop
- // and as such there is a off by 1 error in the return otherwise
- limit++
- }
-
- var count int
- for loc != -1 {
- count++
-
- if count == limit {
- break
- }
-
- // trim off the portion we already searched, and look from there
- searchText = searchText[loc+len(needle):]
- locs = append(locs, []int{loc + offSet, loc + offSet + len(needle)})
-
- // We need to keep the offset of the match so we continue searching
- offSet += loc + len(needle)
-
- // strings.Index does checks of if the string is empty so we don't need
- // to explicitly do it ourselves
- loc = strings.Index(searchText, needle)
- }
-
- // Retain compatibility with FindAllIndex method
- if len(locs) == 0 {
- return nil
- }
-
- return locs
-}
-
-// if the IndexAllIgnoreCase method is called frequently with the same patterns
-// (which is a common case) this is here to speed up the case permutations
-// it is limited to a size of 10 so it never gets that large but really
-// allows things to run faster
-var _permuteCache = map[string][]string{}
-var _permuteCacheLock = sync.Mutex{}
-
-// CacheSize this is public so it can be modified depending on project needs
-// you can increase this value to cache more of the case permutations which
-// can improve performance if doing the same searches over and over
-var CacheSize = 10
-
-// IndexAllIgnoreCase extracts all of the locations of a string inside another string
-// up-to the defined limit. It is designed to be faster than uses of FindAllIndex with
-// case insensitive matching enabled, by looking for string literals first and then
-// checking for exact matches. It also does so in a unicode aware way such that a search
-// for S will search for S s and ſ which a simple strings.ToLower over the haystack
-// and the needle will not.
-//
-// The result is the ability to search for literals without hitting the regex engine
-// which can at times be horribly slow. This by contrast is much faster. See
-// index_ignorecase_benchmark_test.go for some head to head results. Generally
-// so long as we aren't dealing with random data this method should be considerably
-// faster (in some cases thousands of times) or just as fast. Of course it cannot
-// do regular expressions, but that's fine.
-//
-// For pure literal searches IE no regular expression logic this method
-// is a drop in replacement for re.FindAllIndex but generally much faster.
-func IndexAllIgnoreCase(haystack string, needle string, limit int) [][]int {
- // The below needed to avoid timeout crash found using go-fuzz
- if len(haystack) == 0 || len(needle) == 0 {
- return nil
- }
-
- // One of the problems with finding locations ignoring case is that
- // the different case representations can have different byte counts
- // which means the locations using strings or bytes Index can be off
- // if you apply strings.ToLower to your haystack then use strings.Index.
- //
- // This can be overcome using regular expressions but suffers the penalty
- // of hitting the regex engine and paying the price of case
- // insensitive match there.
- //
- // This method tries something else which is used by some regex engines
- // such as the one in Rust where given a str literal if you get
- // all the case options of that such as turning foo into foo Foo fOo FOo foO FoO fOO FOO
- // and then use Boyer-Moore or some such for those. Of course using something
- // like Aho-Corasick or Rabin-Karp to get multi match would be a better idea so you
- // can match all of the input in one pass.
- //
- // If the needle is over some amount of characters long you chop off the first few
- // and then search for those. However this means you are not finding actual matches and as such
- // you the need to validate a potential match after you have found one.
- // The confirmation match is done in a loop because for some literals regular expression
- // is still to slow, although for most its a valid option.
- var locs [][]int
-
- // Char limit is the cut-off where we switch from all case permutations
- // to just the first 3 and then check for an actual match
- // in my tests 3 speeds things up the most against test data
- // of many famous books concatenated together and large
- // amounts of data from /dev/urandom
- var charLimit = 3
-
- if utf8.RuneCountInString(needle) <= charLimit {
- // We are below the limit we set, so get all the search
- // terms and search for that
-
- // Generally speaking I am against caches inside libraries but in this case...
- // when the IndexAllIgnoreCase method is called repeatedly it quite often
- // ends up performing case folding on the same thing over and over again which
- // can become the most expensive operation. So we keep a VERY small cache
- // to avoid that being an issue.
- _permuteCacheLock.Lock()
- searchTerms, ok := _permuteCache[needle]
- if !ok {
- if len(_permuteCache) > CacheSize {
- _permuteCache = map[string][]string{}
- }
- searchTerms = PermuteCaseFolding(needle)
- _permuteCache[needle] = searchTerms
- }
- _permuteCacheLock.Unlock()
-
- // This is using IndexAll in a loop which was faster than
- // any implementation of Aho-Corasick or Boyer-Moore I tried
- // but in theory Aho-Corasick / Rabin-Karp or even a modified
- // version of Boyer-Moore should be faster than this.
- // Especially since they should be able to do multiple comparisons
- // at the same time.
- // However after some investigation it turns out that this turns
- // into a fancy vector instruction on AMD64 (which is all we care about)
- // and as such its pretty hard to beat.
- for _, term := range searchTerms {
- locs = append(locs, IndexAll(haystack, term, limit)...)
- }
-
- // if the limit is not -1 we need to sort and return the first X results so we maintain compatibility with how
- // FindAllIndex would work
- if limit > 0 && len(locs) > limit {
-
- // now sort the results to we can get the first X results
- // Now rank based on which ones are the best and sort them on that rank
- // then get the top amount and the surrounding lines
- sort.Slice(locs, func(i, j int) bool {
- return locs[i][0] < locs[j][0]
- })
-
- return locs[:limit]
- }
- } else {
- // Over the character limit so look for potential matches and only then check to find real ones
-
- // Note that we have to use runes here to avoid cutting bytes off so
- // cast things around to ensure it works
- needleRune := []rune(needle)
-
- // Generally speaking I am against caches inside libraries but in this case...
- // when the IndexAllIgnoreCase method is called repeatedly it quite often
- // ends up performing case folding on the same thing over and over again which
- // can become the most expensive operation. So we keep a VERY small cache
- // to avoid that being an issue.
- _permuteCacheLock.Lock()
- searchTerms, ok := _permuteCache[string(needleRune[:charLimit])]
- if !ok {
- if len(_permuteCache) > CacheSize {
- _permuteCache = map[string][]string{}
- }
- searchTerms = PermuteCaseFolding(string(needleRune[:charLimit]))
- _permuteCache[string(needleRune[:charLimit])] = searchTerms
- }
- _permuteCacheLock.Unlock()
-
- // This is using IndexAll in a loop which was faster than
- // any implementation of Aho-Corasick or Boyer-Moore I tried
- // but in theory Aho-Corasick / Rabin-Karp or even a modified
- // version of Boyer-Moore should be faster than this.
- // Especially since they should be able to do multiple comparisons
- // at the same time.
- // However after some investigation it turns out that this turns
- // into a fancy vector instruction on AMD64 (which is all we care about)
- // and as such its pretty hard to beat.
- haystackRune := []rune(haystack)
-
- for _, term := range searchTerms {
- potentialMatches := IndexAll(haystack, term, -1)
-
- for _, match := range potentialMatches {
- // We have a potential match, so now see if it actually matches
- // by getting the actual value out of our haystack
- if len(haystackRune) < match[0]+len(needleRune) {
- continue
- }
-
- // Because the length of the needle might be different to what we just found as a match
- // based on byte size we add enough extra on the end to deal with the difference
- e := len(needle) + len(needle) - 1
- for match[0]+e > len(haystack) {
- e--
- }
-
- // Cut off the number at the end to the number we need which is the length of the needle runes
- toMatchRune := []rune(haystack[match[0] : match[0]+e])
- toMatchEnd := len(needleRune)
- if len(toMatchRune) < len(needleRune) {
- toMatchEnd = len(toMatchRune)
- }
-
- toMatch := toMatchRune[:toMatchEnd]
-
- // old logic here
- //toMatch = []rune(haystack[match[0] : match[0]+e])[:len(needleRune)]
-
- // what we need to do is iterate the runes of the haystack portion we are trying to
- // match and confirm that the same rune position is a actual match or case fold match
- // if they are keep looking, if they are not bail out as its not a real match
- isMatch := false
- for i := 0; i < len(toMatch); i++ {
- isMatch = false
-
- // Check against the actual term and if that's a match we can avoid folding
- // and doing those comparisons to hopefully save some CPU time
- if toMatch[i] == needleRune[i] {
- isMatch = true
- } else {
- // Not a match so case fold to actually check
- for _, j := range AllSimpleFold(toMatch[i]) {
- if j == needleRune[i] {
- isMatch = true
- }
- }
- }
-
- // Bail out as there is no point to continue checking at this point
- // as we found no match and there is no point burning more CPU checking
- if !isMatch {
- break
- }
- }
-
- if isMatch {
- // When we have confirmed a match we add it to our total
- // but adjust the positions to the match and the length of the
- // needle to ensure the byte count lines up
- locs = append(locs, []int{match[0], match[0] + len(string(toMatch))})
- }
-
- }
- }
-
- // if the limit is not -1 we need to sort and return the first X results so we maintain compatibility with how
- // FindAllIndex would work
- if limit > 0 && len(locs) > limit {
-
- // now sort the results to we can get the first X results
- // Now rank based on which ones are the best and sort them on that rank
- // then get the top amount and the surrounding lines
- sort.Slice(locs, func(i, j int) bool {
- return locs[i][0] < locs[j][0]
- })
-
- return locs[:limit]
- }
- }
-
- // Retain compatibility with FindAllIndex method
- if len(locs) == 0 {
- return nil
- }
-
- return locs
-}
diff --git a/toolkit/stringutils/index_benchmark_test.go b/toolkit/stringutils/index_benchmark_test.go
deleted file mode 100644
index 61a6c631..00000000
--- a/toolkit/stringutils/index_benchmark_test.go
+++ /dev/null
@@ -1,216 +0,0 @@
-// SPDX-License-Identifier: MIT OR Unlicense
-
-package stringutils
-
-import (
- "regexp"
- "testing"
-)
-
-var testMatchEndCase = `1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 1test`
-var testUnicodeMatchEndCase = `ȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺ Ⱥtest`
-
-var testMatchEndCaseLarge = `1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 1test`
-var testUnicodeMatchEndCaseLarge = `ȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺ Ⱥtest`
-
-func BenchmarkFindAllIndex(b *testing.B) {
- r := regexp.MustCompile(`test`)
- haystack := []byte(testMatchEndCase)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkIndexAll(b *testing.B) {
- for i := 0; i < b.N; i++ {
- matches := IndexAll(testMatchEndCase, "test", -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkFindAllIndexLarge(b *testing.B) {
- r := regexp.MustCompile(`test`)
- haystack := []byte(testMatchEndCaseLarge)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkIndexAllLarge(b *testing.B) {
- for i := 0; i < b.N; i++ {
- matches := IndexAll(testMatchEndCaseLarge, "test", -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkFindAllIndexUnicode(b *testing.B) {
- r := regexp.MustCompile(`test`)
- haystack := []byte(testUnicodeMatchEndCase)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkIndexAllUnicode(b *testing.B) {
- for i := 0; i < b.N; i++ {
- matches := IndexAll(testUnicodeMatchEndCase, "test", -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkFindAllIndexUnicodeLarge(b *testing.B) {
- r := regexp.MustCompile(`test`)
- haystack := []byte(testUnicodeMatchEndCaseLarge)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkIndexAllUnicodeLarge(b *testing.B) {
- for i := 0; i < b.N; i++ {
- matches := IndexAll(testUnicodeMatchEndCaseLarge, "test", -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-// This benchmark simulates a bad case of there being many
-// partial matches where the first character in the needle
-// can be found throughout the haystack
-func BenchmarkFindAllIndexManyPartialMatches(b *testing.B) {
- r := regexp.MustCompile(`1test`)
- haystack := []byte(testMatchEndCase)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-// This benchmark simulates a bad case of there being many
-// partial matches where the first character in the needle
-// can be found throughout the haystack
-func BenchmarkIndexAllManyPartialMatches(b *testing.B) {
- for i := 0; i < b.N; i++ {
- matches := IndexAll(testMatchEndCase, "1test", -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-// This benchmark simulates a bad case of there being many
-// partial matches where the first character in the needle
-// can be found throughout the haystack
-func BenchmarkFindAllIndexUnicodeManyPartialMatches(b *testing.B) {
- r := regexp.MustCompile(`Ⱥtest`)
- haystack := []byte(testUnicodeMatchEndCase)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-// This benchmark simulates a bad case of there being many
-// partial matches where the first character in the needle
-// can be found throughout the haystack
-func BenchmarkIndexAllUnicodeManyPartialMatches(b *testing.B) {
- for i := 0; i < b.N; i++ {
- matches := IndexAll(testUnicodeMatchEndCase, "Ⱥtest", -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkFindAllIndexUnicodeManyPartialMatchesVeryLarge(b *testing.B) {
- r := regexp.MustCompile(`Ⱥtest`)
-
- var large string
- for i := 0; i <= 100; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 101 {
- b.Error("Expected 101 match got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexAllUnicodeManyPartialMatchesVeryLarge(b *testing.B) {
- var large string
- for i := 0; i <= 100; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAll(large, "Ⱥtest", -1)
- if len(matches) != 101 {
- b.Error("Expected 101 match got", len(matches))
- }
- }
-}
-
-func BenchmarkFindAllIndexUnicodeManyPartialMatchesSuperLarge(b *testing.B) {
- r := regexp.MustCompile(`Ⱥtest`)
-
- var large string
- for i := 0; i <= 500; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 501 {
- b.Error("Expected 501 match got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexAllUnicodeManyPartialMatchesSuperLarge(b *testing.B) {
- var large string
- for i := 0; i <= 500; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAll(large, "Ⱥtest", -1)
- if len(matches) != 501 {
- b.Error("Expected 501 match got", len(matches))
- }
- }
-}
diff --git a/toolkit/stringutils/index_bug_test.go b/toolkit/stringutils/index_bug_test.go
deleted file mode 100644
index ce109861..00000000
--- a/toolkit/stringutils/index_bug_test.go
+++ /dev/null
@@ -1,39 +0,0 @@
-package stringutils
-
-import (
- "strings"
- "testing"
-)
-
-var broken = `list1=[0,1,2,3,4,5,6,7,8,9]#制作一个0-9的列表
-list1.reverse()#reverse()函数直接对列表中的元素践行反向
-print(list1)
-
-# the following line is where it is breaking
-list2=[str(i) for i in list1]#将列表中的每一个数字转换成字符串
-print(list2)
-
-str1="".join(list2)#通过join()函数,将列表中的单个字符串拼接成一整个字符串
-print(str1)
-
-str2=str1[2:8]#对字符串中的第三到第八字符进行切片
-print(str2)
-
-str3=str2[::-1]#通过右边第一个开始对整个字符串开始切片,以实现其翻转
-print(str3)
-
-i=int(str3)#int()函数试讲字符串转换为整数
-print(i)#这里输出的结果虽然与print(str3)相同,但是性质是不同的
-
-#转换成二进制、八进制、十六进制
-print('转换成二进制:',bin(i),'转换成八进制:',oct(i), '转换成十六进制:',hex(i))
-#二进制、八进制、十六进制这几个进制相互转换的时候,都要先转换为十进制int()`
-
-func TestIndexAllUnicodeOffset(t *testing.T) {
- lines := strings.Split(strings.Replace(broken, "\r\n", "\n", -1), "\n")
-
- // this has an exception
- for _, l := range lines {
- IndexAllIgnoreCase(l, "list1=[0,1,2,3,4,5,6,7,8,9]#制作一个0", -1)
- }
-}
diff --git a/toolkit/stringutils/index_fuzz_test.go b/toolkit/stringutils/index_fuzz_test.go
deleted file mode 100644
index db666949..00000000
--- a/toolkit/stringutils/index_fuzz_test.go
+++ /dev/null
@@ -1,88 +0,0 @@
-package stringutils
-
-import (
- "testing"
-)
-
-func FuzzIndexAllIgnoreCase(f *testing.F) {
- type caseType struct {
- Haystack string
- Needle string
- Count int
- }
-
- testcases := []caseType{{
- Haystack: testUnicodeMatchEndCaseLarge,
- Needle: testUnicodeMatchEndCaseLarge[:5],
- Count: 9,
- },
- {
- Haystack: testMatchEndCaseLarge,
- Needle: testUnicodeMatchEndCaseLarge[:9],
- Count: 10,
- },
- {
- Haystack: `list1=[0,1,2,3,4,5,6,7,8,9]#制作一个0-9的列表
-list1.reverse()#reverse()函数直接对列表中的元素践行反向
-print(list1)
-
-# the following line is where it is breaking
-list2=[str(i) for i in list1]#将列表中的每一个数字转换成字符串
-print(list2)
-
-str1="".join(list2)#通过join()函数,将列表中的单个字符串拼接成一整个字符串
-print(str1)
-
-str2=str1[2:8]#对字符串中的第三到第八字符进行切片
-print(str2)
-
-str3=str2[::-1]#通过右边第一个开始对整个字符串开始切片,以实现其翻转
-print(str3)
-
-i=int(str3)#int()函数试讲字符串转换为整数
-print(i)#这里输出的结果虽然与print(str3)相同,但是性质是不同的
-
-#转换成二进制、八进制、十六进制
-print('转换成二进制:',bin(i),'转换成八进制:',oct(i), '转换成十六进制:',hex(i))
-#二进制、八进制、十六进制这几个进制相互转换的时候,都要先转换为十进制int()`,
- Count: -1,
- },
- {
- Haystack: `func MultipleInputs(a, b int, name string) {
- // ... fancy code goes here
-}
-
-func FuzzMultipleInputs(f *testing.F) {
- // We can add Multiple Seeds, but it has to be the same order as the input parameters for MultipleInputs
- f.Add(10,20,"John the Ripper")
- f.Fuzz(func(t *testing.T,a int,b int,name string){
- MultipleInputs(a,b,name)
- })
-}`,
- Needle: `jp morgan प्रीपेड`,
- Count: 10,
- },
- {
- Haystack: `func MultipleInputs(a, b int, name string) {
- // ... fancy code goes here
-}
-
-func FuzzMultipleInputs(f *testing.F) {
- // We can add Multiple Seeds, but it has to be the same order as the input parameters for MultipleInputs
- f.Add(10,20,"John the Ripper")
- f.Fuzz(func(t *testing.T,a int,b int,name string){
- MultipleInputs(a,b,name)
- })
-}`,
- Needle: `tes`,
- Count: -1,
- },
- }
- for _, tc := range testcases {
- f.Add(tc.Haystack, tc.Needle, tc.Count) // Use f.Add to provide a seed corpus
- }
-
- f.Fuzz(func(t *testing.T, h string, n string, c int) {
- _ = IndexAllIgnoreCase(h, n, c)
- })
-}
diff --git a/toolkit/stringutils/index_ignorecase_benchmark_test.go b/toolkit/stringutils/index_ignorecase_benchmark_test.go
deleted file mode 100644
index ac497a18..00000000
--- a/toolkit/stringutils/index_ignorecase_benchmark_test.go
+++ /dev/null
@@ -1,513 +0,0 @@
-// SPDX-License-Identifier: MIT OR Unlicense
-
-package stringutils
-
-import (
- "regexp"
- "testing"
-)
-
-func BenchmarkFindAllIndexCaseInsensitive(b *testing.B) {
- r := regexp.MustCompile(`(?i)test`)
- haystack := []byte(testMatchEndCase)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseCaseInsensitive(b *testing.B) {
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(testMatchEndCase, "test", -1)
-
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkFindAllIndexLargeCaseInsensitive(b *testing.B) {
- r := regexp.MustCompile(`(?i)test`)
- haystack := []byte(testMatchEndCaseLarge)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseLargeCaseInsensitive(b *testing.B) {
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(testMatchEndCaseLarge, "test", -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkFindAllIndexUnicodeCaseInsensitive(b *testing.B) {
- r := regexp.MustCompile(`(?i)test`)
- haystack := []byte(testUnicodeMatchEndCase)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseUnicodeCaseInsensitive(b *testing.B) {
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(testUnicodeMatchEndCase, "test", -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkFindAllIndexUnicodeLargeCaseInsensitive(b *testing.B) {
- r := regexp.MustCompile(`(?i)test`)
- haystack := []byte(testUnicodeMatchEndCaseLarge)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseUnicodeLargeCaseInsensitive(b *testing.B) {
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(testUnicodeMatchEndCaseLarge, "test", -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-// This benchmark simulates a bad case of there being many
-// partial matches where the first character in the needle
-// can be found throughout the haystack
-func BenchmarkFindAllIndexManyPartialMatchesCaseInsensitive(b *testing.B) {
- r := regexp.MustCompile(`(?i)1test`)
- haystack := []byte(testMatchEndCase)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseManyPartialMatchesCaseInsensitive(b *testing.B) {
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(testMatchEndCase, "1test", -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-// This benchmark simulates a bad case of there being many
-// partial matches where the first character in the needle
-// can be found throughout the haystack
-func BenchmarkFindAllIndexUnicodeManyPartialMatchesCaseInsensitive(b *testing.B) {
- r := regexp.MustCompile(`(?i)Ⱥtest`)
- haystack := []byte(testUnicodeMatchEndCase)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseUnicodeManyPartialMatchesCaseInsensitive(b *testing.B) {
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(testUnicodeMatchEndCase, "Ⱥtest", -1)
- if len(matches) != 1 {
- b.Error("Expected single match")
- }
- }
-}
-
-func BenchmarkFindAllIndexUnicodeCaseInsensitiveVeryLarge(b *testing.B) {
- var large string
- for i := 0; i <= 100; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- r := regexp.MustCompile(`(?i)Ⱥtest`)
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 101 {
- b.Error("Expected single match got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseUnicodeCaseInsensitiveVeryLarge(b *testing.B) {
- var large string
- for i := 0; i <= 100; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(large, "Ⱥtest", -1)
- if len(matches) != 101 {
- b.Error("Expected single match got", len(matches))
- }
- }
-}
-
-func BenchmarkFindAllIndexFoldingCaseInsensitiveVeryLarge(b *testing.B) {
- var large string
- for i := 0; i <= 100; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- r := regexp.MustCompile(`(?i)ſ`)
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 101 {
- b.Error("Expected single match got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseFoldingCaseInsensitiveVeryLarge(b *testing.B) {
- var large string
- for i := 0; i <= 100; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(large, "ſ", -1)
- if len(matches) != 101 {
- b.Error("Expected single match got", len(matches))
- }
- }
-}
-
-func BenchmarkFindAllIndexFoldingCaseInsensitiveNeedle1(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- r := regexp.MustCompile(`(?i)a`)
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseFoldingCaseInsensitiveNeedle1(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(large, "a", -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkFindAllIndexFoldingCaseInsensitiveNeedle2(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- r := regexp.MustCompile(`(?i)aa`)
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseFoldingCaseInsensitiveNeedle2(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(large, "aa", -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkFindAllIndexFoldingCaseInsensitiveNeedle3(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- r := regexp.MustCompile(`(?i)aaa`)
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseFoldingCaseInsensitiveNeedle3(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(large, "aaa", -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkFindAllIndexFoldingCaseInsensitiveNeedle4(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- r := regexp.MustCompile(`(?i)aaaa`)
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseFoldingCaseInsensitiveNeedle4(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(large, "aaaa", -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkFindAllIndexFoldingCaseInsensitiveNeedle5(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- r := regexp.MustCompile(`(?i)aaaaa`)
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseFoldingCaseInsensitiveNeedle5(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(large, "aaaaa", -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkFindAllIndexFoldingCaseInsensitiveNeedle6(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- r := regexp.MustCompile(`(?i)aaaaaa`)
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseFoldingCaseInsensitiveNeedle6(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(large, "aaaaaa", -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkFindAllIndexFoldingCaseInsensitiveNeedle7(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- r := regexp.MustCompile(`(?i)aaaaaaa`)
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseFoldingCaseInsensitiveNeedle7(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(large, "aaaaaaa", -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkFindAllIndexFoldingCaseInsensitiveNeedle8(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- r := regexp.MustCompile(`(?i)aaaaaaaa`)
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseFoldingCaseInsensitiveNeedle8(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(large, "aaaaaaaa", -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkFindAllIndexFoldingCaseInsensitiveNeedle9(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- r := regexp.MustCompile(`(?i)aaaaaaaaa`)
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseFoldingCaseInsensitiveNeedle9(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(large, "aaaaaaaaa", -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkFindAllIndexFoldingCaseInsensitiveNeedle10(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- r := regexp.MustCompile(`(?i)aaaaaaaaaa`)
- haystack := []byte(large)
-
- for i := 0; i < b.N; i++ {
- matches := r.FindAllIndex(haystack, -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
-
-func BenchmarkIndexesAllIgnoreCaseFoldingCaseInsensitiveNeedle10(b *testing.B) {
- var large string
- for i := 0; i <= 10; i++ {
- large += testUnicodeMatchEndCaseLarge
- }
-
- for i := 0; i < b.N; i++ {
- matches := IndexAllIgnoreCase(large, "aaaaaaaaaa", -1)
- if len(matches) != 0 {
- b.Error("Expected no matches got", len(matches))
- }
- }
-}
diff --git a/toolkit/stringutils/index_test.go b/toolkit/stringutils/index_test.go
deleted file mode 100644
index 6d2bd0ec..00000000
--- a/toolkit/stringutils/index_test.go
+++ /dev/null
@@ -1,301 +0,0 @@
-// SPDX-License-Identifier: MIT OR Unlicense
-
-package stringutils
-
-import (
- "math"
- "regexp"
- "strings"
- "testing"
- "time"
-)
-
-func TestExtractLocations(t *testing.T) {
- locations := IndexAll("test that this returns a match", "test", math.MaxInt64)
-
- if locations[0][0] != 0 {
- t.Error("Expected to find location 0")
- }
-}
-
-func TestExtractLocationsLarge(t *testing.T) {
- locations := IndexAll("1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 test that this returns a match", "test", math.MaxInt64)
-
- if locations[0][0] != 101 {
- t.Error("Expected to find location 101")
- }
-}
-
-func TestExtractLocationsLimit(t *testing.T) {
- locations := IndexAll("test test", "test", 1)
-
- if len(locations) != 1 {
- t.Error("Expected to find a single location")
- }
-}
-
-func TestExtractLocationsLimitTwo(t *testing.T) {
- locations := IndexAll("test test test", "test", 2)
-
- if len(locations) != 2 {
- t.Error("Expected to find two locations")
- }
-}
-
-func TestExtractLocationsLimitThree(t *testing.T) {
- locations := IndexAll("test test test", "test", 3)
-
- if len(locations) != 3 {
- t.Error("Expected to find three locations")
- }
-}
-
-func TestExtractLocationsNegativeLimit(t *testing.T) {
- locations := IndexAll("test test test", "test", -1)
-
- if len(locations) != 3 {
- t.Error("Expected to find three locations")
- }
-}
-
-func TestDropInReplacement(t *testing.T) {
- r := regexp.MustCompile(`test`)
-
- matches1 := r.FindAllIndex([]byte(testMatchEndCase), -1)
- matches2 := IndexAll(testMatchEndCase, "test", -1)
-
- for i := 0; i < len(matches1); i++ {
- if matches1[i][0] != matches2[i][0] || matches1[i][1] != matches2[i][1] {
- t.Error("Expect results to match", i)
- }
- }
-}
-
-func TestDropInReplacementNil(t *testing.T) {
- r := regexp.MustCompile(`test`)
-
- matches1 := r.FindAllIndex([]byte(`aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`), -1)
- matches2 := IndexAll(`aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa`, "test", -1)
-
- if matches1 != nil || matches2 != nil {
- t.Error("Expect results to be nil")
- }
-}
-
-func TestDropInReplacementMultiple(t *testing.T) {
- r := regexp.MustCompile(`1`)
-
- matches1 := r.FindAllIndex([]byte(`111`), -1)
- matches2 := IndexAll(`111`, "1", -1)
-
- for i := 0; i < len(matches1); i++ {
- if matches1[i][0] != matches2[i][0] || matches1[i][1] != matches2[i][1] {
- t.Error("Expect results to match", i)
- }
- }
-}
-
-func TestIndexAllIgnoreCaseUnicodeEmpty(t *testing.T) {
- matches := IndexAllIgnoreCase("", "2", -1)
-
- if matches != nil {
- t.Error("Expected no matches")
- }
-}
-
-func TestIndexAllIgnoreCaseUnicodeLongNeedleNoMatch(t *testing.T) {
- matches := IndexAllIgnoreCase("aaaaabbbbb", "aaaaaa", -1)
-
- if matches != nil {
- t.Error("Expected no matches")
- }
-}
-
-func TestIndexAllIgnoreCaseUnicodeLongNeedleSingleMatch(t *testing.T) {
- matches := IndexAllIgnoreCase("aaaaaabbbbb", "aaaaaa", -1)
-
- if len(matches) != 1 {
- t.Error("Expected single matches")
- }
-}
-
-func TestIndexAllIgnoreCaseUnicodeSingleMatch(t *testing.T) {
- matches := IndexAllIgnoreCase("aaaa", "a", 1)
-
- if len(matches) != 1 {
- t.Error("Expected single match")
- }
-}
-
-func TestIndexAllIgnoreCaseUnicodeTwoMatch(t *testing.T) {
- matches := IndexAllIgnoreCase("aaaa", "a", 2)
-
- if len(matches) != 2 {
- t.Error("Expected two matches")
- }
-}
-
-func TestIndexAllIgnoreCaseUnicodeNegativeLimit(t *testing.T) {
- matches := IndexAllIgnoreCase("aaaa", "a", -1)
-
- if len(matches) != 4 {
- t.Error("Expected four matches")
- }
-}
-
-func TestIndexAllIgnoreCaseUnicodeOutOfRange(t *testing.T) {
- matches := IndexAllIgnoreCase("veryuni", "unique", -1)
-
- if len(matches) != 0 {
- t.Error("Expected zero matches")
- }
-}
-
-func TestIndexAllIgnoreCaseUnicodeOutOfRange2(t *testing.T) {
- matches := IndexAllIgnoreCase("veryuni", "uniq", -1)
-
- if len(matches) != 0 {
- t.Error("Expected zero matches")
- }
-}
-
-func TestIndexAllIgnoreCaseUnicodeOutOfRange3(t *testing.T) {
- matches := IndexAllIgnoreCase("ve", "ee", -1)
-
- if len(matches) != 0 {
- t.Error("Expected zero matches")
- }
-}
-
-func TestIndexAllIgnoreCaseUnicodeCheck(t *testing.T) {
- matches := IndexAllIgnoreCase("a secret a", "ſecret", -1)
-
- if matches[0][0] != 2 || matches[0][1] != 8 {
- t.Error("Expected 2 and 8 got", matches[0][0], "and", matches[0][1])
- }
-
- if "a secret a"[matches[0][0]:matches[0][1]] != "secret" {
- t.Error("Expected secret")
- }
-}
-
-func TestIndexAllIgnoreCaseUnicodeCheckEnd(t *testing.T) {
- matches := IndexAllIgnoreCase("a ſecret a", "secret", -1)
-
- if matches[0][0] != 2 || matches[0][1] != 9 {
- t.Error("Expected 2 and 9 got", matches[0][0], "and", matches[0][1])
- }
-
- if "a ſecret a"[matches[0][0]:matches[0][1]] != "ſecret" {
- t.Errorf("Expected ſecret got '%s'", "a ſecret a"[matches[0][0]:matches[0][1]])
- }
-}
-
-func TestDropInReplacementMultipleIndexAllIgnoreCaseUnicode(t *testing.T) {
- r := regexp.MustCompile(`1`)
-
- matches1 := r.FindAllIndex([]byte(`111`), -1)
- matches2 := IndexAllIgnoreCase(`111`, "1", -1)
-
- for i := 0; i < len(matches1); i++ {
- if matches1[i][0] != matches2[i][0] || matches1[i][1] != matches2[i][1] {
- t.Error("Expect results to match", i)
- }
- }
-}
-
-func TestIndexAllIgnoreCaseUnicodeSpace(t *testing.T) {
- matches := IndexAllIgnoreCase(prideAndPrejudice, "ten thousand a year", -1)
- m := IndexAll(strings.ToLower(prideAndPrejudice), "ten thousand a year", -1)
-
- r := regexp.MustCompile(`(?i)ten thousand a year`)
- index := r.FindAllIndex([]byte(prideAndPrejudice), -1)
-
- if len(matches) != len(m) || len(matches) != len(index) {
- t.Error("Expected 2 got", len(matches))
- }
-}
-
-func TestIndexAllIgnoreCaseAtEnd(t *testing.T) {
- matches := IndexAllIgnoreCase(`testjava`, "java", -1)
-
- r := regexp.MustCompile(`java`)
- index := r.FindAllIndex([]byte(`testjava`), -1)
-
- if len(matches) != len(index) || matches[0][0] != 4 || matches[0][1] != 8 {
- t.Error("Expected 1 got", len(matches))
- }
-}
-
-func TestIndexAllIgnoreCaseStrange(t *testing.T) {
- matches := IndexAllIgnoreCase(`func AllSimpleFold(input rune) []rune {
- res := []rune{}
-`, "rune{}", -1)
-
- r := regexp.MustCompile(`rune{}`)
- index := r.FindAllIndex([]byte(`func AllSimpleFold(input rune) []rune {
- res := []rune{}
-`), -1)
-
- if len(matches) != len(index) || matches[0][0] != 57 || matches[0][1] != 63 {
- t.Error("Expected 1 got", len(matches))
- }
-}
-
-func TestIndexAllIgnoreCaseStrangeTwo(t *testing.T) {
- matches := IndexAllIgnoreCase(`this is my cs ß haystack`, `ß`, -1)
- r := regexp.MustCompile(`ß`)
- index := r.FindAllIndex([]byte(`this is my cs ß haystack`), -1)
-
- if len(matches) != len(index) || matches[0][0] != 14 || matches[0][1] != 16 {
- t.Error("Expected 1 got", len(matches))
- }
-}
-
-// This test is here because using regex to validate matches is horribly slow for this
-// case. It should run faster than the value here for most machines
-func TestIndexAllIgnoreCasePerformanceCase(t *testing.T) {
- start := time.Now().UnixNano() / int64(time.Millisecond)
- IndexAllIgnoreCase(`ȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺ`, `ȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺȺ`, -1)
- end := time.Now().UnixNano() / int64(time.Millisecond)
-
- if end-start > 1000 {
- t.Error("Should be faster than this... don't think you are, know you are. Ran in (ms)", end-start)
- }
-}
-
-// When we limit we want the searches to be in order in order to match what FindAllIndex would do
-// as closely as possible
-func TestIndexAllIgnoreCaseLimitSmallNeedle(t *testing.T) {
- matches := IndexAllIgnoreCase("Test TEST test tEST", "te", 2)
-
- if len(matches) != 2 {
- t.Error("Expected two matches")
- }
-
- if matches[0][0] != 0 {
- t.Error("expected 0 got", matches[0][0])
- }
-
- if matches[1][0] != 5 {
- t.Error("expected 5 got", matches[1][0])
- }
-}
-
-// When we limit we want the searches to be in order in order to match what FindAllIndex would do
-// as closely as possible
-func TestIndexAllIgnoreCaseLimitLargeNeedle(t *testing.T) {
- matches := IndexAllIgnoreCase("Test TEST test tEST", "test", 2)
-
- if len(matches) != 2 {
- t.Error("Expected two matches")
- }
-
- if matches[0][0] != 0 {
- t.Error("expected 0 got", matches[0][0])
- }
-
- if matches[1][0] != 5 {
- t.Error("expected 5 got", matches[1][0])
- }
-}
diff --git a/toolkit/stringutils/stringutils.go b/toolkit/stringutils/stringutils.go
deleted file mode 100644
index 8116bd5f..00000000
--- a/toolkit/stringutils/stringutils.go
+++ /dev/null
@@ -1,83 +0,0 @@
-package stringutils
-
-import (
- "regexp"
- "strings"
- "unicode"
-)
-
-// IsEmpty asserts s is empty
-func IsEmpty(s string) bool {
- return strings.TrimSpace(s) == "" || strings.TrimSpace(s) == ""
-}
-
-// IsNotEmpty asserts s is not empty
-func IsNotEmpty(s string) bool {
- return !IsEmpty(s)
-}
-
-// ContainsI assert s contains substr ignore case
-func ContainsI(s string, substr string) bool {
- re := regexp.MustCompile(`(?i)` + substr)
- return re.MatchString(s)
-}
-
-// HasPrefixI assert s has prefix prefix ignore case
-func HasPrefixI(s, prefix string) bool {
- re := regexp.MustCompile(`(?i)^` + prefix)
- return re.MatchString(s)
-}
-
-func ToTitle(s string) string {
- str := []rune(s)
- return strings.ToUpper(string(str[0])) + string(str[1:])
-}
-
-var symbolre = regexp.MustCompile("[。?!,、;:“”‘’()《》【】~!\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~\\s]")
-
-func ToCamel(s string) string {
- parts := symbolre.Split(s, -1)
- var convertedParts []string
- for _, v := range parts {
- if IsNotEmpty(v) {
- convertedParts = append(convertedParts, ToTitle(v))
- }
- }
- ret := strings.Join(convertedParts, "")
- if !unicode.IsUpper(rune(ret[0])) {
- ret = "A" + ret
- }
- return ret
-}
-
-func ReplaceAtRuneIndex(in string, r rune, i int) string {
- out := []rune(in)
- out[i] = r
- return string(out)
-}
-
-func ReplaceStringAtByteIndex(in string, replace string, start int, end int) string {
- out := []byte(in)
- r := []byte(replace)
- result := make([]byte, len(out[:start]))
- copy(result, out[:start])
- result = append(result, r...)
- result = append(result, out[end:]...)
- return string(result)
-}
-
-func ReplaceStringAtByteIndexBatch(in string, args []string, locs [][]int) string {
- out := []byte(in)
- result := make([]byte, 0)
- end := 0
- for i, loc := range locs {
- arg := args[i]
- r := []byte(arg)
- start := loc[0]
- result = append(result, out[end:start]...)
- result = append(result, r...)
- end = loc[1]
- }
- result = append(result, out[end:]...)
- return string(result)
-}
diff --git a/toolkit/stringutils/stringutils_test.go b/toolkit/stringutils/stringutils_test.go
deleted file mode 100644
index ed4e4296..00000000
--- a/toolkit/stringutils/stringutils_test.go
+++ /dev/null
@@ -1,318 +0,0 @@
-package stringutils
-
-import (
- "testing"
-)
-
-func TestContains(t *testing.T) {
- type args struct {
- s string
- substr string
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {
- name: "",
- args: args{
- s: "filedownloadUser",
- substr: "Download",
- },
- want: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := ContainsI(tt.args.s, tt.args.substr); got != tt.want {
- t.Errorf("Contains() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestHasPrefixI(t *testing.T) {
- type args struct {
- s string
- prefix string
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {
- name: "1",
- args: args{
- s: "VARCHAR(255)",
- prefix: "var",
- },
- want: true,
- },
- {
- name: "2",
- args: args{
- s: "VARCHAR(255)",
- prefix: "VAR",
- },
- want: true,
- },
- {
- name: "3",
- args: args{
- s: "VARCHAR(255)",
- prefix: "CHA",
- },
- want: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := HasPrefixI(tt.args.s, tt.args.prefix); got != tt.want {
- t.Errorf("HasPrefixI() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestIsEmpty(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {
- name: "1",
- args: args{
- s: " abc ",
- },
- want: false,
- },
- {
- name: "2",
- args: args{
- s: " ",
- },
- want: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := IsEmpty(tt.args.s); got != tt.want {
- t.Errorf("IsEmpty() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestIsNotEmpty(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want bool
- }{
- {
- name: "",
- args: args{
- s: " abc ",
- },
- want: true,
- },
- {
- name: "",
- args: args{
- s: " ",
- },
- want: false,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := IsNotEmpty(tt.args.s); got != tt.want {
- t.Errorf("IsNotEmpty() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestToCamel(t *testing.T) {
- type args struct {
- s string
- }
- tests := []struct {
- name string
- args args
- want string
- }{
- {
- name: "",
- args: args{
- s: "abc武斌123",
- },
- want: "Abc武斌123",
- },
- {
- name: "",
- args: args{
- s: "word",
- },
- want: "Word",
- },
- {
- name: "",
- args: args{
- s: "snake_word",
- },
- want: "SnakeWord",
- },
- {
- name: "",
- args: args{
- s: "Commonresultarraylistcom.hundsun.hcreator.pojo.hcbizmodelview",
- },
- want: "CommonresultarraylistcomHundsunHcreatorPojoHcbizmodelview",
- },
- {
- name: "",
- args: args{
- s: "Commonresultarraylistcom,hundsun,hcreator,pojo,hcbizmodelview",
- },
- want: "CommonresultarraylistcomHundsunHcreatorPojoHcbizmodelview",
- },
- {
- name: "",
- args: args{
- s: "Commonresultarraylistcom hundsun hcreator",
- },
- want: "CommonresultarraylistcomHundsunHcreator",
- },
- {
- name: "",
- args: args{
- s: "Commonresultarraylistcom,hundsun,hcreator",
- },
- want: "CommonresultarraylistcomHundsunHcreator",
- },
- {
- name: "",
- args: args{
- s: "台湾是中国的省",
- },
- want: "A台湾是中国的省",
- },
- {
- name: "",
- args: args{
- s: "dataDecimal",
- },
- want: "DataDecimal",
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := ToCamel(tt.args.s); got != tt.want {
- t.Errorf("ToCamel() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestReplaceStringAtIndex(t *testing.T) {
- in := `INSERT INTO user ("name", "age") VALUES (?, ?);`
- loc := IndexAll(in, "?", -1)
-
- in2 := `我爱北京天安门,啦啦啦`
- loc2 := IndexAll(in2, "天安门", -1)
-
- type args struct {
- in string
- replace string
- start int
- end int
- }
- tests := []struct {
- name string
- args args
- want string
- }{
- {
- name: "",
- args: args{
- in: in,
- replace: "18",
- start: loc[1][0],
- end: loc[1][1],
- },
- want: `INSERT INTO user ("name", "age") VALUES (?, 18);`,
- },
- {
- name: "",
- args: args{
- in: in2,
- replace: "颐和园",
- start: loc2[0][0],
- end: loc2[0][1],
- },
- want: `我爱北京颐和园,啦啦啦`,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := ReplaceStringAtByteIndex(tt.args.in, tt.args.replace, tt.args.start, tt.args.end); got != tt.want {
- t.Errorf("ReplaceStringAtIndex() = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestReplaceStringAtByteIndexBatch(t *testing.T) {
- in := `INSERT INTO user ("name", "age") VALUES (?, ?);`
- loc := IndexAll(in, "?", -1)
-
- in2 := `我爱北京天安门,啦啦啦`
- loc2 := IndexAll(in2, "天安门", -1)
-
- type args struct {
- in string
- args []string
- locs [][]int
- }
- tests := []struct {
- name string
- args args
- want string
- }{
- {
- name: "",
- args: args{
- in: in,
- args: []string{"'wubin'", "18"},
- locs: loc,
- },
- want: `INSERT INTO user ("name", "age") VALUES ('wubin', 18);`,
- },
- {
- name: "",
- args: args{
- in: in2,
- args: []string{"颐和园"},
- locs: loc2,
- },
- want: `我爱北京颐和园,啦啦啦`,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- if got := ReplaceStringAtByteIndexBatch(tt.args.in, tt.args.args, tt.args.locs); got != tt.want {
- t.Errorf("ReplaceStringAtByteIndexBatch() = %v, want %v", got, tt.want)
- }
- })
- }
-}
diff --git a/toolkit/templateutils/funcs.go b/toolkit/templateutils/funcs.go
deleted file mode 100644
index 20f14407..00000000
--- a/toolkit/templateutils/funcs.go
+++ /dev/null
@@ -1,37 +0,0 @@
-package templateutils
-
-import (
- "bytes"
- "strings"
- "text/template"
- "time"
-
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
-)
-
-func formatTime(t time.Time) string {
- return t.Format(constants.FORMAT)
-}
-
-func boolToInt(b bool) int {
- if b {
- return 1
- }
- return 0
-}
-
-func Eval(t *template.Template) func(string, interface{}) (string, error) {
- return func(name string, v interface{}) (string, error) {
- var buf bytes.Buffer
- err := t.ExecuteTemplate(&buf, name, v)
- return buf.String(), err
- }
-}
-
-func trimSuffix(suffix, v string) string {
- return strings.TrimSuffix(strings.TrimSpace(v), suffix)
-}
-
-func hasPrefix(v, prefix string) bool {
- return strings.HasPrefix(strings.TrimSpace(v), prefix)
-}
diff --git a/toolkit/templateutils/templateutils.go b/toolkit/templateutils/templateutils.go
deleted file mode 100644
index 1463f5d1..00000000
--- a/toolkit/templateutils/templateutils.go
+++ /dev/null
@@ -1,92 +0,0 @@
-package templateutils
-
-import (
- "bytes"
- "path/filepath"
- "strings"
- "text/template"
-
- "github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/caller"
-)
-
-// String return result of calling template Execute as string
-func String(tmplname, tmpl string, data interface{}) (string, error) {
- var (
- sqlBuf bytes.Buffer
- err error
- tpl *template.Template
- )
- funcs := map[string]any{
- "contains": strings.Contains,
- "hasPrefix": strings.HasPrefix,
- "hasSuffix": strings.HasSuffix,
- }
- tpl = template.Must(template.New(tmplname).Funcs(funcs).Parse(tmpl))
- if err = tpl.Execute(&sqlBuf, data); err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- return strings.TrimSpace(sqlBuf.String()), nil
-}
-
-// StringBlock return result of calling template Execute as string
-func StringBlock(tmplname, tmpl string, block string, data interface{}) (string, error) {
- var (
- sqlBuf bytes.Buffer
- err error
- tpl *template.Template
- )
- tpl = template.Must(template.New(tmplname).Parse(tmpl))
- if err = tpl.ExecuteTemplate(&sqlBuf, block, data); err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- return strings.TrimSpace(sqlBuf.String()), nil
-}
-
-// StringBlockMysql return result of calling template Execute as string from template file
-func StringBlockMysql(tmpl string, block string, data interface{}) (string, error) {
- var (
- sqlBuf bytes.Buffer
- err error
- tpl *template.Template
- funcMap map[string]interface{}
- )
- tpl = template.New(filepath.Base(tmpl))
- funcMap = make(map[string]interface{})
- funcMap["FormatTime"] = formatTime
- funcMap["BoolToInt"] = boolToInt
- funcMap["Eval"] = Eval(tpl)
- funcMap["TrimSuffix"] = trimSuffix
- funcMap["isNil"] = func(t interface{}) bool {
- return t == nil
- }
- tpl = template.Must(tpl.Funcs(funcMap).ParseFiles(tmpl))
- if err = tpl.ExecuteTemplate(&sqlBuf, block, data); err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- return strings.TrimSpace(sqlBuf.String()), nil
-}
-
-// BlockMysql return result of calling template Execute as string from template file
-func BlockMysql(tmplname, tmpl string, block string, data interface{}) (string, error) {
- var (
- sqlBuf bytes.Buffer
- err error
- tpl *template.Template
- funcMap map[string]interface{}
- )
- tpl = template.New(tmplname)
- funcMap = make(map[string]interface{})
- funcMap["FormatTime"] = formatTime
- funcMap["BoolToInt"] = boolToInt
- funcMap["Eval"] = Eval(tpl)
- funcMap["TrimSuffix"] = trimSuffix
- funcMap["isNil"] = func(t interface{}) bool {
- return t == nil
- }
- tpl = template.Must(tpl.Funcs(funcMap).Parse(tmpl))
- if err = tpl.ExecuteTemplate(&sqlBuf, block, data); err != nil {
- return "", errors.Wrap(err, caller.NewCaller().String())
- }
- return strings.TrimSpace(sqlBuf.String()), nil
-}
diff --git a/toolkit/timeutils/timeutils.go b/toolkit/timeutils/timeutils.go
deleted file mode 100644
index 695a7d2e..00000000
--- a/toolkit/timeutils/timeutils.go
+++ /dev/null
@@ -1,40 +0,0 @@
-package timeutils
-
-import (
- "context"
- "time"
-
- "github.com/hyperjumptech/jiffy"
- "github.com/pkg/errors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/stringutils"
-)
-
-// Parse parses string to time.Duration
-func Parse(t string, defaultDur time.Duration) (time.Duration, error) {
- var (
- dur time.Duration
- err error
- )
- if stringutils.IsNotEmpty(t) {
- if dur, err = jiffy.DurationOf(t); err != nil {
- err = errors.Wrapf(err, "parse %s from config file fail, use default 15s instead", t)
- }
- }
- if dur <= 0 {
- dur = defaultDur
- }
- return dur, err
-}
-
-func CallWithCtx(ctx context.Context, fn func() struct{}) error {
- result := make(chan struct{}, 1)
- go func() {
- result <- fn()
- }()
- select {
- case <-ctx.Done():
- return ctx.Err()
- case <-result:
- return nil
- }
-}
diff --git a/toolkit/timeutils/timeutils_test.go b/toolkit/timeutils/timeutils_test.go
deleted file mode 100644
index a6243108..00000000
--- a/toolkit/timeutils/timeutils_test.go
+++ /dev/null
@@ -1,67 +0,0 @@
-package timeutils
-
-import (
- "context"
- "fmt"
- "testing"
- "time"
-)
-
-func TestParse(t *testing.T) {
- type args struct {
- t string
- defaultDur time.Duration
- }
- tests := []struct {
- name string
- args args
- want time.Duration
- wantErr bool
- }{
- {
- name: "1",
- args: args{
- t: "9h1m30s",
- defaultDur: 15 * time.Second,
- },
- want: 32490000000000,
- wantErr: false,
- },
- {
- name: "2",
- args: args{
- t: "wrongdurationstr",
- defaultDur: 15 * time.Second,
- },
- want: 15000000000,
- wantErr: true,
- },
- }
- for _, tt := range tests {
- t.Run(tt.name, func(t *testing.T) {
- got, err := Parse(tt.args.t, tt.args.defaultDur)
- if (err != nil) != tt.wantErr {
- t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
- return
- }
- if got != tt.want {
- t.Errorf("Parse() got = %v, want %v", got, tt.want)
- }
- })
- }
-}
-
-func TestCallWithCtx(t *testing.T) {
- ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
- defer cancel()
- err := CallWithCtx(ctx, func() struct{} {
- time.Sleep(2 * time.Second)
- fmt.Println("Job Done")
- return struct{}{}
- })
- if err != nil {
- fmt.Println(err)
- return
- }
- fmt.Println("OK")
-}
diff --git a/toolkit/utils/utils.go b/toolkit/utils/utils.go
deleted file mode 100644
index 93eb7635..00000000
--- a/toolkit/utils/utils.go
+++ /dev/null
@@ -1,11 +0,0 @@
-package utils
-
-import (
- "github.com/sirupsen/logrus"
- "time"
-)
-
-func TimeTrack(start time.Time, name string, log logrus.StdLogger) {
- elapsed := time.Since(start)
- log.Printf("%s took %s", name, elapsed)
-}
diff --git a/toolkit/yaml/testdata/.env b/toolkit/yaml/testdata/.env
deleted file mode 100644
index 891c52be..00000000
--- a/toolkit/yaml/testdata/.env
+++ /dev/null
@@ -1,2 +0,0 @@
-GDD_PORT=6060
-GDD_ROUTE_ROOT_PATH=/api
\ No newline at end of file
diff --git a/toolkit/yaml/testdata/app-dev-local.yaml b/toolkit/yaml/testdata/app-dev-local.yaml
deleted file mode 100644
index a10d2716..00000000
--- a/toolkit/yaml/testdata/app-dev-local.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-gdd:
- nacos:
- server:
- addr: "http://localhost:8848/nacos"
\ No newline at end of file
diff --git a/toolkit/yaml/testdata/app-dev.yml b/toolkit/yaml/testdata/app-dev.yml
deleted file mode 100644
index f3ca1ea1..00000000
--- a/toolkit/yaml/testdata/app-dev.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-gdd:
- port: 8080
\ No newline at end of file
diff --git a/toolkit/yaml/testdata/app-local.yaml b/toolkit/yaml/testdata/app-local.yaml
deleted file mode 100644
index bfd73998..00000000
--- a/toolkit/yaml/testdata/app-local.yaml
+++ /dev/null
@@ -1,5 +0,0 @@
-gdd:
- nacos:
- server:
- addr: "http://localhost:8848/nacos"
- param-with-hyphen: 1
\ No newline at end of file
diff --git a/toolkit/yaml/testdata/app-prod.yml b/toolkit/yaml/testdata/app-prod.yml
deleted file mode 100644
index 891c52be..00000000
--- a/toolkit/yaml/testdata/app-prod.yml
+++ /dev/null
@@ -1,2 +0,0 @@
-GDD_PORT=6060
-GDD_ROUTE_ROOT_PATH=/api
\ No newline at end of file
diff --git a/toolkit/yaml/testdata/app-test.yml b/toolkit/yaml/testdata/app-test.yml
deleted file mode 100644
index ff7da91b..00000000
--- a/toolkit/yaml/testdata/app-test.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-gdd:
- route:
- root:
- path: /api
diff --git a/toolkit/yaml/testdata/app.yml b/toolkit/yaml/testdata/app.yml
deleted file mode 100644
index 26c05273..00000000
--- a/toolkit/yaml/testdata/app.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-gdd:
- port: 6060
- tracing:
- metrics:
- root: "go-doudou"
\ No newline at end of file
diff --git a/toolkit/yaml/yaml.go b/toolkit/yaml/yaml.go
deleted file mode 100644
index 583eb6ff..00000000
--- a/toolkit/yaml/yaml.go
+++ /dev/null
@@ -1,99 +0,0 @@
-package yaml
-
-import (
- "fmt"
- "io"
- "io/ioutil"
- "os"
- "path/filepath"
- "strings"
-
- "github.com/ghodss/yaml"
- "github.com/jeremywohl/flatten"
- "github.com/spf13/cast"
-)
-
-func load(data []byte) error {
- config := make(map[string]interface{})
- err := yaml.Unmarshal(data, &config)
- if err != nil {
- return err
- }
- flat, _ := flatten.Flatten(config, "", flatten.UnderscoreStyle)
- currentEnv := map[string]bool{}
- rawEnv := os.Environ()
- for _, rawEnvLine := range rawEnv {
- key := strings.Split(rawEnvLine, "=")[0]
- currentEnv[key] = true
- }
- for k, v := range flat {
- upperK := strings.ToUpper(strings.ReplaceAll(k, "-", ""))
- if !currentEnv[upperK] {
- _ = os.Setenv(upperK, cast.ToString(v))
- }
- }
- return nil
-}
-
-func LoadReader(reader io.Reader) error {
- data, err := ioutil.ReadAll(reader)
- if err != nil {
- return err
- }
- return load(data)
-}
-
-func LoadFile(file string) {
- data, _ := ioutil.ReadFile(file)
- err := load(data)
- if err != nil {
- panic(err)
- }
-}
-
-func Load(env string) {
- wd, _ := os.Getwd()
- matches, _ := filepath.Glob(filepath.Join(wd, fmt.Sprintf("app-%s-local.%s", env, "y*ml")))
- for _, item := range matches {
- LoadFile(item)
- }
- if "test" != env {
- matches, _ = filepath.Glob(filepath.Join(wd, fmt.Sprintf("app-local.%s", "y*ml")))
- for _, item := range matches {
- LoadFile(item)
- }
- }
- matches, _ = filepath.Glob(filepath.Join(wd, fmt.Sprintf("app-%s.%s", env, "y*ml")))
- for _, item := range matches {
- LoadFile(item)
- }
- matches, _ = filepath.Glob(filepath.Join(wd, fmt.Sprintf("app.%s", "y*ml")))
- for _, item := range matches {
- LoadFile(item)
- }
-}
-
-func LoadReaderAsMap(reader io.Reader) (map[string]interface{}, error) {
- data, err := ioutil.ReadAll(reader)
- if err != nil {
- return nil, err
- }
- return loadAsMap(data)
-}
-
-func LoadFileAsMap(file string) (map[string]interface{}, error) {
- data, err := ioutil.ReadFile(file)
- if err != nil {
- return nil, err
- }
- return loadAsMap(data)
-}
-
-func loadAsMap(data []byte) (map[string]interface{}, error) {
- config := make(map[string]interface{})
- err := yaml.Unmarshal(data, &config)
- if err != nil {
- return nil, err
- }
- return flatten.Flatten(config, "", flatten.DotStyle)
-}
diff --git a/toolkit/yaml/yaml_test.go b/toolkit/yaml/yaml_test.go
deleted file mode 100644
index 3b8b2a8e..00000000
--- a/toolkit/yaml/yaml_test.go
+++ /dev/null
@@ -1,140 +0,0 @@
-package yaml_test
-
-import (
- "bytes"
- "io"
- "io/ioutil"
- "os"
- "strings"
- "testing"
-
- "github.com/pkg/errors"
- "github.com/stretchr/testify/require"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/yaml"
-)
-
-func TestLoad_dev(t *testing.T) {
- defer os.Clearenv()
- _ = os.Chdir("testdata")
- yaml.Load("dev")
- port := os.Getenv("GDD_PORT")
- require.Equal(t, "8080", port)
-
- nacosServer := os.Getenv("GDD_NACOS_SERVER_ADDR")
- require.Equal(t, "http://localhost:8848/nacos", nacosServer)
-}
-
-func TestLoad_test(t *testing.T) {
- defer os.Clearenv()
- _ = os.Chdir("testdata")
- yaml.Load("test")
- port := os.Getenv("GDD_PORT")
- require.Equal(t, "6060", port)
-
- nacosServer := os.Getenv("GDD_NACOS_SERVER_ADDR")
- require.Equal(t, "", nacosServer)
-
- rootPath := os.Getenv("GDD_ROUTE_ROOT_PATH")
- require.Equal(t, "/api", rootPath)
-}
-
-func TestLoad_error(t *testing.T) {
- defer os.Clearenv()
- _ = os.Chdir("testdata")
- require.Panics(t, func() {
- yaml.Load("prod")
- })
-}
-
-func TestLoadReaderAsMap(t *testing.T) {
- defer os.Clearenv()
- _ = os.Chdir("testdata")
- data, err := ioutil.ReadFile("app.yml")
- require.NoError(t, err)
- result, err := yaml.LoadReaderAsMap(strings.NewReader(string(data)))
- require.NoError(t, err)
- require.Equal(t, float64(6060), result["gdd.port"])
- require.Equal(t, "go-doudou", result["gdd.tracing.metrics.root"])
-}
-
-func TestLoadFileAsMap(t *testing.T) {
- defer os.Clearenv()
- _ = os.Chdir("testdata")
- result, err := yaml.LoadFileAsMap("app.yml")
- require.NoError(t, err)
- require.Equal(t, float64(6060), result["gdd.port"])
- require.Equal(t, "go-doudou", result["gdd.tracing.metrics.root"])
-}
-
-func TestLoadReaderAsMapError(t *testing.T) {
- defer os.Clearenv()
- _, err := yaml.LoadReaderAsMap(ErrReader(errors.New("test error")))
- require.Error(t, err)
-}
-
-func TestLoadFileAsMapDotenvError(t *testing.T) {
- defer os.Clearenv()
- _ = os.Chdir("testdata")
- _, err := yaml.LoadFileAsMap(".env")
- require.Error(t, err)
-}
-
-func TestLoadReaderAsMapDotenvError(t *testing.T) {
- defer os.Clearenv()
- _ = os.Chdir("testdata")
- data, err := ioutil.ReadFile(".env")
- require.NoError(t, err)
- _, err = yaml.LoadReaderAsMap(strings.NewReader(string(data)))
- require.Error(t, err)
-}
-
-func TestLoadFileAsMapError(t *testing.T) {
- defer os.Clearenv()
- _, err := yaml.LoadFileAsMap("not_exist_file")
- require.Error(t, err)
-}
-
-func TestLoadReaderAsMapFromString(t *testing.T) {
- defer os.Clearenv()
- data := []byte("gdd:\n port: 6060\n tracing:\n metrics:\n root: \"go-doudou\"")
- result, err := yaml.LoadReaderAsMap(strings.NewReader(string(data)))
- require.NoError(t, err)
- require.Equal(t, float64(6060), result["gdd.port"])
- require.Equal(t, "go-doudou", result["gdd.tracing.metrics.root"])
-}
-
-func TestLoadReader(t *testing.T) {
- defer os.Clearenv()
- _ = os.Chdir("testdata")
- data, err := ioutil.ReadFile("app.yml")
- require.NoError(t, err)
- err = yaml.LoadReader(bytes.NewReader(data))
- require.NoError(t, err)
- require.Equal(t, "6060", os.Getenv("GDD_PORT"))
- require.Equal(t, "go-doudou", os.Getenv("GDD_TRACING_METRICS_ROOT"))
-}
-
-func ErrReader(err error) io.Reader {
- return &errReader{err: err}
-}
-
-type errReader struct {
- err error
-}
-
-func (r *errReader) Read(p []byte) (int, error) {
- return 0, r.err
-}
-
-func TestLoadReaderError(t *testing.T) {
- err := yaml.LoadReader(ErrReader(errors.New("test error")))
- require.Error(t, err)
-}
-
-func TestLoadReaderDotenvError(t *testing.T) {
- _ = os.Chdir("testdata")
- data, err := ioutil.ReadFile(".env")
- require.NoError(t, err)
- err = yaml.LoadReader(bytes.NewReader(data))
- require.Error(t, err)
-}
diff --git a/toolkit/zlogger/entry.go b/toolkit/zlogger/entry.go
deleted file mode 100644
index cf187a81..00000000
--- a/toolkit/zlogger/entry.go
+++ /dev/null
@@ -1,210 +0,0 @@
-package zlogger
-
-import (
- "context"
- "fmt"
- "io"
- "os"
- "strconv"
-
- "github.com/rs/zerolog"
- "github.com/rs/zerolog/pkgerrors"
- "github.com/unionj-cloud/go-doudou/v2/toolkit/constants"
-)
-
-var Logger = zerolog.New(os.Stderr).With().Timestamp().Logger()
-
-type LoggerConfig struct {
- Dev bool
- Caller bool
- Discard bool
- Pid bool
-
- Writer io.Writer
- Level zerolog.Level
-}
-
-type LoggerConfigOption func(*LoggerConfig)
-
-func WithDev(dev bool) LoggerConfigOption {
- return func(lc *LoggerConfig) {
- lc.Dev = dev
- }
-}
-
-func WithCaller(caller bool) LoggerConfigOption {
- return func(lc *LoggerConfig) {
- lc.Caller = caller
- }
-}
-
-func WithDiscard(discard bool) LoggerConfigOption {
- return func(lc *LoggerConfig) {
- lc.Discard = discard
- }
-}
-
-func WithWriter(writer io.Writer) LoggerConfigOption {
- return func(lc *LoggerConfig) {
- lc.Writer = writer
- }
-}
-
-func WithZeroLogLevel(level zerolog.Level) LoggerConfigOption {
- return func(lc *LoggerConfig) {
- lc.Level = level
- }
-}
-
-func NewLoggerConfig(opts ...LoggerConfigOption) LoggerConfig {
- lc := LoggerConfig{}
- for _, item := range opts {
- item(&lc)
- }
- return lc
-}
-
-func InitEntry(lc LoggerConfig) {
- var output io.Writer
- if !lc.Discard {
- if lc.Writer != nil {
- output = lc.Writer
- } else if lc.Dev {
- output = zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: constants.FORMAT, NoColor: true}
- } else {
- output = os.Stdout
- }
- }
- zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
- zeroCtx := zerolog.New(output).Level(lc.Level).With().Timestamp().Stack()
- if lc.Caller {
- zeroCtx = zeroCtx.Caller()
- }
- if lc.Pid {
- zeroCtx = zeroCtx.Str("__pid", strconv.Itoa(os.Getpid()))
- }
- Logger = zeroCtx.Logger()
-}
-
-// SetOutput duplicates the global logger and sets w as its output,
-// then assign to zlogger package level zerolog.Logger
-func SetOutput(w io.Writer) {
- Logger = Logger.Output(w)
-}
-
-// Output duplicates the global logger and sets w as its output.
-func Output(w io.Writer) zerolog.Logger {
- return Logger.Output(w)
-}
-
-// With creates a child logger with the field added to its context.
-func With() zerolog.Context {
- return Logger.With()
-}
-
-// Level creates a child logger with the minimum accepted level set to level.
-func Level(level zerolog.Level) zerolog.Logger {
- return Logger.Level(level)
-}
-
-// Sample returns a logger with the s sampler.
-func Sample(s zerolog.Sampler) zerolog.Logger {
- return Logger.Sample(s)
-}
-
-// Hook returns a logger with the h Hook.
-func Hook(h zerolog.Hook) zerolog.Logger {
- return Logger.Hook(h)
-}
-
-// Err starts a new message with error level with err as a field if not nil or
-// with info level if err is nil.
-//
-// You must call Msg on the returned event in order to send the event.
-func Err(err error) *zerolog.Event {
- return Logger.Err(err)
-}
-
-// Trace starts a new message with trace level.
-//
-// You must call Msg on the returned event in order to send the event.
-func Trace() *zerolog.Event {
- return Logger.Trace()
-}
-
-// Debug starts a new message with debug level.
-//
-// You must call Msg on the returned event in order to send the event.
-func Debug() *zerolog.Event {
- return Logger.Debug()
-}
-
-// Info starts a new message with info level.
-//
-// You must call Msg on the returned event in order to send the event.
-func Info() *zerolog.Event {
- return Logger.Info()
-}
-
-// Warn starts a new message with warn level.
-//
-// You must call Msg on the returned event in order to send the event.
-func Warn() *zerolog.Event {
- return Logger.Warn()
-}
-
-// Error starts a new message with error level.
-//
-// You must call Msg on the returned event in order to send the event.
-func Error() *zerolog.Event {
- return Logger.Error()
-}
-
-// Fatal starts a new message with fatal level. The os.Exit(1) function
-// is called by the Msg method.
-//
-// You must call Msg on the returned event in order to send the event.
-func Fatal() *zerolog.Event {
- return Logger.Fatal()
-}
-
-// Panic starts a new message with panic level. The message is also sent
-// to the panic function.
-//
-// You must call Msg on the returned event in order to send the event.
-func Panic() *zerolog.Event {
- return Logger.Panic()
-}
-
-// WithLevel starts a new message with level.
-//
-// You must call Msg on the returned event in order to send the event.
-func WithLevel(level zerolog.Level) *zerolog.Event {
- return Logger.WithLevel(level)
-}
-
-// Log starts a new message with no level. Setting zerolog.GlobalLevel to
-// zerolog.Disabled will still disable events produced by this method.
-//
-// You must call Msg on the returned event in order to send the event.
-func Log() *zerolog.Event {
- return Logger.Log()
-}
-
-// Print sends a log event using debug level and no extra field.
-// Arguments are handled in the manner of fmt.Print.
-func Print(v ...interface{}) {
- Logger.Debug().CallerSkipFrame(1).Msg(fmt.Sprint(v...))
-}
-
-// Printf sends a log event using debug level and no extra field.
-// Arguments are handled in the manner of fmt.Printf.
-func Printf(format string, v ...interface{}) {
- Logger.Debug().CallerSkipFrame(1).Msgf(format, v...)
-}
-
-// Ctx returns the Logger associated with the ctx. If no logger
-// is associated, a disabled logger is returned.
-func Ctx(ctx context.Context) *zerolog.Logger {
- return zerolog.Ctx(ctx)
-}
diff --git a/version/version.go b/version/version.go
index a3e54bfb..b7c960c3 100644
--- a/version/version.go
+++ b/version/version.go
@@ -1,3 +1,3 @@
package version
-const Release = "v2.4.2"
+const Release = "v2.5.1"