Compare commits
14 Commits
0fd5f3b417
...
v0.0.266
Author | SHA1 | Date | |
---|---|---|---|
cd9406900a | |||
6c81f7f6bc | |||
d56a0235af | |||
de2ca763c1 | |||
da52bb5c90
|
|||
3d4afe7b25
|
|||
f5766d639c
|
|||
cdf2a6e76b
|
|||
6d7cfb86f8 | |||
1e9d663ffe | |||
5b8d7ebf87 | |||
11dc6d2640 | |||
29a3f73f15 | |||
98105642fc |
12
TODO.md
12
TODO.md
@@ -2,12 +2,6 @@
|
||||
|
||||
- cronext
|
||||
|
||||
- cursortoken
|
||||
|
||||
- typed/geenric mongo wrapper
|
||||
|
||||
- error package
|
||||
|
||||
- rfctime.DateOnly
|
||||
- rfctime.HMSTimeOnly
|
||||
- rfctime.NanoTimeOnly
|
||||
- rfctime.DateOnly
|
||||
- rfctime.HMSTimeOnly
|
||||
- rfctime.NanoTimeOnly
|
@@ -31,13 +31,13 @@ type EnumDef struct {
|
||||
Values []EnumDefVal
|
||||
}
|
||||
|
||||
var rexPackage = rext.W(regexp.MustCompile(`^package\s+(?P<name>[A-Za-z0-9_]+)\s*$`))
|
||||
var rexEnumPackage = rext.W(regexp.MustCompile(`^package\s+(?P<name>[A-Za-z0-9_]+)\s*$`))
|
||||
|
||||
var rexEnumDef = rext.W(regexp.MustCompile(`^\s*type\s+(?P<name>[A-Za-z0-9_]+)\s+(?P<type>[A-Za-z0-9_]+)\s*//\s*(@enum:type).*$`))
|
||||
|
||||
var rexValueDef = rext.W(regexp.MustCompile(`^\s*(?P<name>[A-Za-z0-9_]+)\s+(?P<type>[A-Za-z0-9_]+)\s*=\s*(?P<value>("[A-Za-z0-9_:]+"|[0-9]+))\s*(//(?P<descr>.*))?.*$`))
|
||||
var rexEnumValueDef = rext.W(regexp.MustCompile(`^\s*(?P<name>[A-Za-z0-9_]+)\s+(?P<type>[A-Za-z0-9_]+)\s*=\s*(?P<value>("[A-Za-z0-9_:]+"|[0-9]+))\s*(//(?P<descr>.*))?.*$`))
|
||||
|
||||
var rexChecksumConst = rext.W(regexp.MustCompile(`const ChecksumGenerator = "(?P<cs>[A-Za-z0-9_]*)"`))
|
||||
var rexEnumChecksumConst = rext.W(regexp.MustCompile(`const ChecksumEnumGenerator = "(?P<cs>[A-Za-z0-9_]*)"`))
|
||||
|
||||
func GenerateEnumSpecs(sourceDir string, destFile string) error {
|
||||
|
||||
@@ -52,13 +52,14 @@ func GenerateEnumSpecs(sourceDir string, destFile string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m, ok := rexChecksumConst.MatchFirst(string(content)); ok {
|
||||
if m, ok := rexEnumChecksumConst.MatchFirst(string(content)); ok {
|
||||
oldChecksum = m.GroupByName("cs").Value()
|
||||
}
|
||||
}
|
||||
|
||||
files = langext.ArrFilter(files, func(v os.DirEntry) bool { return v.Name() != path.Base(destFile) })
|
||||
files = langext.ArrFilter(files, func(v os.DirEntry) bool { return strings.HasSuffix(v.Name(), ".go") })
|
||||
files = langext.ArrFilter(files, func(v os.DirEntry) bool { return !strings.HasSuffix(v.Name(), "_gen.go") })
|
||||
langext.SortBy(files, func(v os.DirEntry) string { return v.Name() })
|
||||
|
||||
newChecksumStr := goext.GoextVersion
|
||||
@@ -85,7 +86,7 @@ func GenerateEnumSpecs(sourceDir string, destFile string) error {
|
||||
|
||||
for _, f := range files {
|
||||
fmt.Printf("========= %s =========\n\n", f.Name())
|
||||
fileEnums, pn, err := processFile(sourceDir, path.Join(sourceDir, f.Name()))
|
||||
fileEnums, pn, err := processEnumFile(sourceDir, path.Join(sourceDir, f.Name()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -103,7 +104,7 @@ func GenerateEnumSpecs(sourceDir string, destFile string) error {
|
||||
return errors.New("no package name found in any file")
|
||||
}
|
||||
|
||||
err = os.WriteFile(destFile, []byte(fmtOutput(newChecksum, allEnums, pkgname)), 0o755)
|
||||
err = os.WriteFile(destFile, []byte(fmtEnumOutput(newChecksum, allEnums, pkgname)), 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -125,7 +126,7 @@ func GenerateEnumSpecs(sourceDir string, destFile string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func processFile(basedir string, fn string) ([]EnumDef, string, error) {
|
||||
func processEnumFile(basedir string, fn string) ([]EnumDef, string, error) {
|
||||
file, err := os.Open(fn)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
@@ -149,7 +150,7 @@ func processFile(basedir string, fn string) ([]EnumDef, string, error) {
|
||||
break
|
||||
}
|
||||
|
||||
if match, ok := rexPackage.MatchFirst(line); i == 0 && ok {
|
||||
if match, ok := rexEnumPackage.MatchFirst(line); i == 0 && ok {
|
||||
pkgname = match.GroupByName("name").Value()
|
||||
continue
|
||||
}
|
||||
@@ -172,7 +173,7 @@ func processFile(basedir string, fn string) ([]EnumDef, string, error) {
|
||||
fmt.Printf("Found enum definition { '%s' -> '%s' }\n", def.EnumTypeName, def.Type)
|
||||
}
|
||||
|
||||
if match, ok := rexValueDef.MatchFirst(line); ok {
|
||||
if match, ok := rexEnumValueDef.MatchFirst(line); ok {
|
||||
typename := match.GroupByName("type").Value()
|
||||
def := EnumDefVal{
|
||||
VarName: match.GroupByName("name").Value(),
|
||||
@@ -202,7 +203,7 @@ func processFile(basedir string, fn string) ([]EnumDef, string, error) {
|
||||
return enums, pkgname, nil
|
||||
}
|
||||
|
||||
func fmtOutput(cs string, enums []EnumDef, pkgname string) string {
|
||||
func fmtEnumOutput(cs string, enums []EnumDef, pkgname string) string {
|
||||
str := "// Code generated by enum-generate.go DO NOT EDIT.\n"
|
||||
str += "\n"
|
||||
str += "package " + pkgname + "\n"
|
||||
@@ -212,7 +213,7 @@ func fmtOutput(cs string, enums []EnumDef, pkgname string) string {
|
||||
str += "import \"gogs.mikescher.com/BlackForestBytes/goext/enums\"" + "\n"
|
||||
str += "\n"
|
||||
|
||||
str += "const ChecksumGenerator = \"" + cs + "\" // GoExtVersion: " + goext.GoextVersion + "\n"
|
||||
str += "const ChecksumEnumGenerator = \"" + cs + "\" // GoExtVersion: " + goext.GoextVersion + "\n"
|
||||
str += "\n"
|
||||
|
||||
for _, enumdef := range enums {
|
||||
|
236
bfcodegen/id-generate.go
Normal file
236
bfcodegen/id-generate.go
Normal file
@@ -0,0 +1,236 @@
|
||||
package bfcodegen
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/cmdext"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/cryptext"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||
"gogs.mikescher.com/BlackForestBytes/goext/rext"
|
||||
"io"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type IDDef struct {
|
||||
File string
|
||||
FileRelative string
|
||||
Name string
|
||||
}
|
||||
|
||||
var rexIDPackage = rext.W(regexp.MustCompile(`^package\s+(?P<name>[A-Za-z0-9_]+)\s*$`))
|
||||
|
||||
var rexIDDef = rext.W(regexp.MustCompile(`^\s*type\s+(?P<name>[A-Za-z0-9_]+)\s+string\s*//\s*(@id:type).*$`))
|
||||
|
||||
var rexIDChecksumConst = rext.W(regexp.MustCompile(`const ChecksumIDGenerator = "(?P<cs>[A-Za-z0-9_]*)"`))
|
||||
|
||||
func GenerateIDSpecs(sourceDir string, destFile string) error {
|
||||
|
||||
files, err := os.ReadDir(sourceDir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
oldChecksum := "N/A"
|
||||
if _, err := os.Stat(destFile); !os.IsNotExist(err) {
|
||||
content, err := os.ReadFile(destFile)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if m, ok := rexIDChecksumConst.MatchFirst(string(content)); ok {
|
||||
oldChecksum = m.GroupByName("cs").Value()
|
||||
}
|
||||
}
|
||||
|
||||
files = langext.ArrFilter(files, func(v os.DirEntry) bool { return v.Name() != path.Base(destFile) })
|
||||
files = langext.ArrFilter(files, func(v os.DirEntry) bool { return strings.HasSuffix(v.Name(), ".go") })
|
||||
files = langext.ArrFilter(files, func(v os.DirEntry) bool { return !strings.HasSuffix(v.Name(), "_gen.go") })
|
||||
langext.SortBy(files, func(v os.DirEntry) string { return v.Name() })
|
||||
|
||||
newChecksumStr := goext.GoextVersion
|
||||
for _, f := range files {
|
||||
content, err := os.ReadFile(path.Join(sourceDir, f.Name()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
newChecksumStr += "\n" + f.Name() + "\t" + cryptext.BytesSha256(content)
|
||||
}
|
||||
|
||||
newChecksum := cryptext.BytesSha256([]byte(newChecksumStr))
|
||||
|
||||
if newChecksum != oldChecksum {
|
||||
fmt.Printf("[IDGenerate] Checksum has changed ( %s -> %s ), will generate new file\n\n", oldChecksum, newChecksum)
|
||||
} else {
|
||||
fmt.Printf("[IDGenerate] Checksum unchanged ( %s ), nothing to do\n", oldChecksum)
|
||||
return nil
|
||||
}
|
||||
|
||||
allIDs := make([]IDDef, 0)
|
||||
|
||||
pkgname := ""
|
||||
|
||||
for _, f := range files {
|
||||
fmt.Printf("========= %s =========\n\n", f.Name())
|
||||
fileIDs, pn, err := processIDFile(sourceDir, path.Join(sourceDir, f.Name()))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Printf("\n")
|
||||
|
||||
allIDs = append(allIDs, fileIDs...)
|
||||
|
||||
if pn != "" {
|
||||
pkgname = pn
|
||||
}
|
||||
}
|
||||
|
||||
if pkgname == "" {
|
||||
return errors.New("no package name found in any file")
|
||||
}
|
||||
|
||||
err = os.WriteFile(destFile, []byte(fmtIDOutput(newChecksum, allIDs, pkgname)), 0o755)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
res, err := cmdext.RunCommand("go", []string{"fmt", destFile}, langext.Ptr(2*time.Second))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if res.CommandTimedOut {
|
||||
fmt.Println(res.StdCombined)
|
||||
return errors.New("go fmt timed out")
|
||||
}
|
||||
if res.ExitCode != 0 {
|
||||
fmt.Println(res.StdCombined)
|
||||
return errors.New("go fmt did not succeed")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func processIDFile(basedir string, fn string) ([]IDDef, string, error) {
|
||||
file, err := os.Open(fn)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
defer func() { _ = file.Close() }()
|
||||
|
||||
bin, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
lines := strings.Split(string(bin), "\n")
|
||||
|
||||
ids := make([]IDDef, 0)
|
||||
|
||||
pkgname := ""
|
||||
|
||||
for i, line := range lines {
|
||||
if i == 0 && strings.HasPrefix(line, "// Code generated by") {
|
||||
break
|
||||
}
|
||||
|
||||
if match, ok := rexIDPackage.MatchFirst(line); i == 0 && ok {
|
||||
pkgname = match.GroupByName("name").Value()
|
||||
continue
|
||||
}
|
||||
|
||||
if match, ok := rexIDDef.MatchFirst(line); ok {
|
||||
|
||||
rfp, err := filepath.Rel(basedir, fn)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
|
||||
def := IDDef{
|
||||
File: fn,
|
||||
FileRelative: rfp,
|
||||
Name: match.GroupByName("name").Value(),
|
||||
}
|
||||
fmt.Printf("Found ID definition { '%s' }\n", def.Name)
|
||||
ids = append(ids, def)
|
||||
}
|
||||
}
|
||||
|
||||
return ids, pkgname, nil
|
||||
}
|
||||
|
||||
func fmtIDOutput(cs string, ids []IDDef, pkgname string) string {
|
||||
str := "// Code generated by id-generate.go DO NOT EDIT.\n"
|
||||
str += "\n"
|
||||
str += "package " + pkgname + "\n"
|
||||
str += "\n"
|
||||
|
||||
str += "import \"go.mongodb.org/mongo-driver/bson\"" + "\n"
|
||||
str += "import \"go.mongodb.org/mongo-driver/bson/bsontype\"" + "\n"
|
||||
str += "import \"go.mongodb.org/mongo-driver/bson/primitive\"" + "\n"
|
||||
str += "import \"gogs.mikescher.com/BlackForestBytes/goext/exerr\"" + "\n"
|
||||
str += "\n"
|
||||
|
||||
str += "const ChecksumIDGenerator = \"" + cs + "\" // GoExtVersion: " + goext.GoextVersion + "\n"
|
||||
str += "\n"
|
||||
|
||||
anyDef := langext.ArrFirstOrNil(ids, func(def IDDef) bool { return def.Name == "AnyID" || def.Name == "AnyId" })
|
||||
|
||||
for _, iddef := range ids {
|
||||
|
||||
str += "// ================================ " + iddef.Name + " (" + iddef.FileRelative + ") ================================" + "\n"
|
||||
str += "" + "\n"
|
||||
|
||||
str += "func (i " + iddef.Name + ") MarshalBSONValue() (bsontype.Type, []byte, error) {" + "\n"
|
||||
str += " if objId, err := primitive.ObjectIDFromHex(string(i)); err == nil {" + "\n"
|
||||
str += " return bson.MarshalValue(objId)" + "\n"
|
||||
str += " } else {" + "\n"
|
||||
str += " return 0, nil, exerr.New(exerr.TypeMarshalEntityID, \"Failed to marshal " + iddef.Name + "(\"+i.String()+\") to ObjectId\").Str(\"value\", string(i)).Type(\"type\", i).Build()" + "\n"
|
||||
str += " }" + "\n"
|
||||
str += "}" + "\n"
|
||||
|
||||
str += "" + "\n"
|
||||
|
||||
str += "func (i " + iddef.Name + ") String() string {" + "\n"
|
||||
str += " return string(i)" + "\n"
|
||||
str += "}" + "\n"
|
||||
|
||||
str += "" + "\n"
|
||||
|
||||
str += "func (i " + iddef.Name + ") ObjID() (primitive.ObjectID, error) {" + "\n"
|
||||
str += " return primitive.ObjectIDFromHex(string(i))" + "\n"
|
||||
str += "}" + "\n"
|
||||
|
||||
str += "" + "\n"
|
||||
|
||||
str += "func (i " + iddef.Name + ") Valid() bool {" + "\n"
|
||||
str += " _, err := primitive.ObjectIDFromHex(string(i))" + "\n"
|
||||
str += " return err == nil" + "\n"
|
||||
str += "}" + "\n"
|
||||
|
||||
str += "" + "\n"
|
||||
|
||||
if anyDef != nil {
|
||||
str += "func (i " + iddef.Name + ") AsAny() " + anyDef.Name + " {" + "\n"
|
||||
str += " return " + anyDef.Name + "(i)" + "\n"
|
||||
str += "}" + "\n"
|
||||
|
||||
str += "" + "\n"
|
||||
}
|
||||
|
||||
str += "func New" + iddef.Name + "() " + iddef.Name + " {" + "\n"
|
||||
str += " return " + iddef.Name + "(primitive.NewObjectID().Hex())" + "\n"
|
||||
str += "}" + "\n"
|
||||
|
||||
str += "" + "\n"
|
||||
|
||||
}
|
||||
|
||||
return str
|
||||
}
|
@@ -55,6 +55,8 @@ var (
|
||||
TypeBindFailFormData = NewType("BINDFAIL_FORMDATA", langext.Ptr(400))
|
||||
TypeBindFailHeader = NewType("BINDFAIL_HEADER", langext.Ptr(400))
|
||||
|
||||
TypeMarshalEntityID = NewType("MARSHAL_ENTITY_ID", langext.Ptr(400))
|
||||
|
||||
TypeUnauthorized = NewType("UNAUTHORIZED", langext.Ptr(401))
|
||||
TypeAuthFailed = NewType("AUTH_FAILED", langext.Ptr(401))
|
||||
|
||||
|
10
go.mod
10
go.mod
@@ -8,24 +8,24 @@ require (
|
||||
github.com/rs/xid v1.5.0
|
||||
github.com/rs/zerolog v1.30.0
|
||||
go.mongodb.org/mongo-driver v1.12.1
|
||||
golang.org/x/crypto v0.12.0
|
||||
golang.org/x/crypto v0.13.0
|
||||
golang.org/x/sys v0.12.0
|
||||
golang.org/x/term v0.12.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/bytedance/sonic v1.10.0 // indirect
|
||||
github.com/bytedance/sonic v1.10.1 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||
github.com/chenzhuoyu/iasm v0.9.0 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-playground/validator/v10 v10.15.3 // indirect
|
||||
github.com/go-playground/validator/v10 v10.15.4 // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.16.7 // indirect
|
||||
github.com/klauspost/compress v1.17.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/leodido/go-urn v1.2.4 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
@@ -42,7 +42,7 @@ require (
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect
|
||||
golang.org/x/arch v0.5.0 // indirect
|
||||
golang.org/x/net v0.14.0 // indirect
|
||||
golang.org/x/net v0.15.0 // indirect
|
||||
golang.org/x/sync v0.3.0 // indirect
|
||||
golang.org/x/text v0.13.0 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
|
10
go.sum
10
go.sum
@@ -6,6 +6,8 @@ github.com/bytedance/sonic v1.10.0-rc3 h1:uNSnscRapXTwUgTyOF0GVljYD08p9X/Lbr9Mwe
|
||||
github.com/bytedance/sonic v1.10.0-rc3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||
github.com/bytedance/sonic v1.10.0 h1:qtNZduETEIWJVIyDl01BeNxur2rW9OwTQ/yBqFRkKEk=
|
||||
github.com/bytedance/sonic v1.10.0/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||
github.com/bytedance/sonic v1.10.1 h1:7a1wuFXL1cMy7a3f7/VFcEtriuXQnUBhtoVfOZiaysc=
|
||||
github.com/bytedance/sonic v1.10.1/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d h1:77cEq6EriyTZ0g/qfRdp61a3Uu/AWrgIq2s0ClJV1g0=
|
||||
@@ -34,6 +36,8 @@ github.com/go-playground/validator/v10 v10.15.1 h1:BSe8uhN+xQ4r5guV/ywQI4gO59C2r
|
||||
github.com/go-playground/validator/v10 v10.15.1/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-playground/validator/v10 v10.15.3 h1:S+sSpunYjNPDuXkWbK+x+bA7iXiW296KG4dL3X7xUZo=
|
||||
github.com/go-playground/validator/v10 v10.15.3/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-playground/validator/v10 v10.15.4 h1:zMXza4EpOdooxPel5xDqXEdXG5r+WggpvnAKMsalBjs=
|
||||
github.com/go-playground/validator/v10 v10.15.4/go.mod h1:9iXMNT7sEkjXb0I+enO7QXmzG6QCsPWY4zveKFVRSyU=
|
||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU=
|
||||
github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I=
|
||||
@@ -54,6 +58,8 @@ github.com/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQ
|
||||
github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
|
||||
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/compress v1.17.0 h1:Rnbp4K9EjcDuVuHtd0dgA4qNuv9yKDYKK1ulpJwgrqM=
|
||||
github.com/klauspost/compress v1.17.0/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 h1:0E5MSMDEoAulmXNFquVs//DdoomxaoTY1kUhbc/qbZg=
|
||||
github.com/klauspost/cpuid/v2 v2.2.5/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
@@ -151,6 +157,8 @@ golang.org/x/crypto v0.11.0 h1:6Ewdq3tDic1mg5xRO4milcWCfMVQhI4NkqWWvqejpuA=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.12.0 h1:tFM/ta59kqch6LlvYnPa0yx5a83cL2nHflFhYKvv9Yk=
|
||||
golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw=
|
||||
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
|
||||
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
@@ -163,6 +171,8 @@ golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY=
|
||||
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.14.0 h1:BONx9s002vGdD9umnlX1Po8vOZmrgH34qlHcD1MfK14=
|
||||
golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI=
|
||||
golang.org/x/net v0.15.0 h1:ugBLEUaxABaB5AJqW9enI0ACdci2RUd4eP51NTBvuJ8=
|
||||
golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
|
@@ -1,5 +1,5 @@
|
||||
package goext
|
||||
|
||||
const GoextVersion = "0.0.257"
|
||||
const GoextVersion = "0.0.266"
|
||||
|
||||
const GoextVersionTimestamp = "2023-09-05T15:01:55+0200"
|
||||
const GoextVersionTimestamp = "2023-09-21T13:08:13+0200"
|
||||
|
@@ -2,23 +2,41 @@ package tst
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"reflect"
|
||||
"runtime/debug"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func AssertEqual[T comparable](t *testing.T, actual T, expected T) {
|
||||
t.Helper()
|
||||
if actual != expected {
|
||||
t.Errorf("values differ: Actual: '%v', Expected: '%v'", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertNotEqual[T comparable](t *testing.T, actual T, expected T) {
|
||||
t.Helper()
|
||||
if actual == expected {
|
||||
t.Errorf("values do not differ: Actual: '%v', Expected: '%v'", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertDeepEqual[T comparable](t *testing.T, actual T, expected T) {
|
||||
t.Helper()
|
||||
if reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("values differ: Actual: '%v', Expected: '%v'", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertNotDeepEqual[T comparable](t *testing.T, actual T, expected T) {
|
||||
t.Helper()
|
||||
if !reflect.DeepEqual(actual, expected) {
|
||||
t.Errorf("values do not differ: Actual: '%v', Expected: '%v'", actual, expected)
|
||||
}
|
||||
}
|
||||
|
||||
func AssertDeRefEqual[T comparable](t *testing.T, actual *T, expected T) {
|
||||
t.Helper()
|
||||
if actual == nil {
|
||||
t.Errorf("values differ: Actual: NIL, Expected: '%v'", expected)
|
||||
}
|
||||
@@ -28,6 +46,7 @@ func AssertDeRefEqual[T comparable](t *testing.T, actual *T, expected T) {
|
||||
}
|
||||
|
||||
func AssertPtrEqual[T comparable](t *testing.T, actual *T, expected *T) {
|
||||
t.Helper()
|
||||
if actual == nil && expected == nil {
|
||||
return
|
||||
}
|
||||
@@ -47,6 +66,7 @@ func AssertPtrEqual[T comparable](t *testing.T, actual *T, expected *T) {
|
||||
}
|
||||
|
||||
func AssertHexEqual(t *testing.T, expected string, actual []byte) {
|
||||
t.Helper()
|
||||
actualStr := hex.EncodeToString(actual)
|
||||
if actualStr != expected {
|
||||
t.Errorf("values differ: Actual: '%v', Expected: '%v'", actualStr, expected)
|
||||
@@ -54,18 +74,21 @@ func AssertHexEqual(t *testing.T, expected string, actual []byte) {
|
||||
}
|
||||
|
||||
func AssertTrue(t *testing.T, value bool) {
|
||||
t.Helper()
|
||||
if !value {
|
||||
t.Error("value should be true\n" + string(debug.Stack()))
|
||||
}
|
||||
}
|
||||
|
||||
func AssertFalse(t *testing.T, value bool) {
|
||||
t.Helper()
|
||||
if value {
|
||||
t.Error("value should be false\n" + string(debug.Stack()))
|
||||
}
|
||||
}
|
||||
|
||||
func AssertNoErr(t *testing.T, anerr error) {
|
||||
t.Helper()
|
||||
if anerr != nil {
|
||||
t.Error("Function returned an error: " + anerr.Error() + "\n" + string(debug.Stack()))
|
||||
}
|
||||
|
@@ -21,19 +21,7 @@ func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int,
|
||||
|
||||
if filter != nil {
|
||||
pipeline = filter.FilterQuery()
|
||||
fpf1, fpd1, fpf2, fpd2 := filter.Pagination()
|
||||
if fpf1 != "" {
|
||||
pf1 = fpf1
|
||||
}
|
||||
if fpd1 != "" {
|
||||
pd1 = fpd1
|
||||
}
|
||||
if fpf2 != "" {
|
||||
pf2 = fpf2
|
||||
}
|
||||
if fpd2 != "" {
|
||||
pd2 = fpd2
|
||||
}
|
||||
pf1, pd1, pf2, pd2 = filter.Pagination()
|
||||
}
|
||||
|
||||
sortPrimary := pf1
|
||||
@@ -128,3 +116,17 @@ func (c *Coll[TData]) Count(ctx context.Context, filter ct.Filter) (int64, error
|
||||
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (c *Coll[TData]) ListWithCount(ctx context.Context, filter ct.Filter, pageSize *int, inTok ct.CursorToken) ([]TData, ct.CursorToken, int64, error) {
|
||||
// NOTE: Possible optimization: Cache count in CursorToken, then fetch count only on first page.
|
||||
count, err := c.Count(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, ct.CursorToken{}, 0, err
|
||||
}
|
||||
|
||||
data, token, err := c.List(ctx, filter, pageSize, inTok)
|
||||
if err != nil {
|
||||
return nil, ct.CursorToken{}, 0, err
|
||||
}
|
||||
return data, token, count, nil
|
||||
}
|
||||
|
@@ -25,7 +25,7 @@ func (c *Coll[TData]) EnsureInitializedReflection(v TData) {
|
||||
|
||||
m := make(map[string]fullTypeRef)
|
||||
|
||||
c.initFields("", rval, m, make([]int, 0))
|
||||
c.initFields("", rval.Type(), m, make([]int, 0), make([]reflect.Type, 0))
|
||||
|
||||
c.implDataTypeMap[rval.Type()] = m
|
||||
}
|
||||
@@ -50,20 +50,16 @@ func (c *Coll[TData]) init() {
|
||||
c.implDataTypeMap = make(map[reflect.Type]map[string]fullTypeRef)
|
||||
|
||||
v := reflect.ValueOf(example)
|
||||
c.initFields("", v, c.dataTypeMap, make([]int, 0))
|
||||
c.initFields("", v.Type(), c.dataTypeMap, make([]int, 0), make([]reflect.Type, 0))
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, m map[string]fullTypeRef, idxarr []int) {
|
||||
|
||||
rtyp := rval.Type()
|
||||
|
||||
func (c *Coll[TData]) initFields(prefix string, rtyp reflect.Type, m map[string]fullTypeRef, idxarr []int, typesInPath []reflect.Type) {
|
||||
for i := 0; i < rtyp.NumField(); i++ {
|
||||
|
||||
rsfield := rtyp.Field(i)
|
||||
rvfield := rval.Field(i)
|
||||
|
||||
if !rsfield.IsExported() {
|
||||
continue
|
||||
@@ -91,21 +87,21 @@ func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, m map[string
|
||||
newIdxArr := langext.ArrCopy(idxarr)
|
||||
newIdxArr = append(newIdxArr, i)
|
||||
|
||||
if langext.InArray("inline", bsontags) && rvfield.Kind() == reflect.Struct {
|
||||
if langext.InArray("inline", bsontags) && rsfield.Type.Kind() == reflect.Struct {
|
||||
|
||||
// pass-through field
|
||||
c.initFields(prefix, rvfield, m, newIdxArr)
|
||||
c.initFields(prefix, rsfield.Type, m, newIdxArr, typesInPath)
|
||||
|
||||
} else {
|
||||
|
||||
if rvfield.Type().Kind() == reflect.Pointer {
|
||||
if rsfield.Type.Kind() == reflect.Pointer {
|
||||
|
||||
m[fullKey] = fullTypeRef{
|
||||
IsPointer: true,
|
||||
RealType: rvfield.Type(),
|
||||
Kind: rvfield.Type().Elem().Kind(),
|
||||
Type: rvfield.Type().Elem(),
|
||||
UnderlyingType: reflectext.Underlying(rvfield.Type().Elem()),
|
||||
RealType: rsfield.Type,
|
||||
Kind: rsfield.Type.Elem().Kind(),
|
||||
Type: rsfield.Type.Elem(),
|
||||
UnderlyingType: reflectext.Underlying(rsfield.Type.Elem()),
|
||||
Name: rsfield.Name,
|
||||
Index: newIdxArr,
|
||||
}
|
||||
@@ -114,20 +110,37 @@ func (c *Coll[TData]) initFields(prefix string, rval reflect.Value, m map[string
|
||||
|
||||
m[fullKey] = fullTypeRef{
|
||||
IsPointer: false,
|
||||
RealType: rvfield.Type(),
|
||||
Kind: rvfield.Type().Kind(),
|
||||
Type: rvfield.Type(),
|
||||
UnderlyingType: reflectext.Underlying(rvfield.Type()),
|
||||
RealType: rsfield.Type,
|
||||
Kind: rsfield.Type.Kind(),
|
||||
Type: rsfield.Type,
|
||||
UnderlyingType: reflectext.Underlying(rsfield.Type),
|
||||
Name: rsfield.Name,
|
||||
Index: newIdxArr,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if rvfield.Kind() == reflect.Struct {
|
||||
c.initFields(fullKey+".", rvfield, m, newIdxArr)
|
||||
if rsfield.Type.Kind() == reflect.Struct {
|
||||
c.initFields(fullKey+".", rsfield.Type, m, newIdxArr, typesInPath)
|
||||
}
|
||||
|
||||
if rsfield.Type.Kind() == reflect.Pointer && rsfield.Type.Elem().Kind() == reflect.Struct {
|
||||
innerType := rsfield.Type.Elem()
|
||||
|
||||
// check if there is recursion
|
||||
recursion := false
|
||||
for _, typ := range typesInPath {
|
||||
recursion = recursion || (typ == innerType)
|
||||
}
|
||||
|
||||
if !recursion {
|
||||
// Store all seen types before that deref a pointer to prevent endless recursion
|
||||
newTypesInPath := make([]reflect.Type, len(typesInPath))
|
||||
copy(newTypesInPath, typesInPath)
|
||||
newTypesInPath = append(newTypesInPath, rtyp)
|
||||
c.initFields(fullKey+".", innerType, m, newIdxArr, newTypesInPath)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -23,6 +23,9 @@ func TestReflectionGetFieldType(t *testing.T) {
|
||||
Sub struct {
|
||||
A string `bson:"a"`
|
||||
} `bson:"sub"`
|
||||
SubPtr *struct {
|
||||
A string `bson:"a"`
|
||||
} `bson:"subPtr"`
|
||||
Str string `bson:"str"`
|
||||
Ptr *int `bson:"ptr"`
|
||||
MDate rfctime.RFC3339NanoTime `bson:"mdate"`
|
||||
@@ -43,6 +46,11 @@ func TestReflectionGetFieldType(t *testing.T) {
|
||||
}{
|
||||
A: "2",
|
||||
},
|
||||
SubPtr: &struct {
|
||||
A string `bson:"a"`
|
||||
}{
|
||||
A: "4",
|
||||
},
|
||||
Str: "3",
|
||||
Ptr: langext.Ptr(4),
|
||||
MDate: t1,
|
||||
@@ -82,6 +90,12 @@ func TestReflectionGetFieldType(t *testing.T) {
|
||||
tst.AssertEqual(t, gft("sub.a").IsPointer, false)
|
||||
tst.AssertEqual(t, gfv("sub.a").(string), "2")
|
||||
|
||||
tst.AssertEqual(t, gft("subPtr.a").Kind.String(), "string")
|
||||
tst.AssertEqual(t, gft("subPtr.a").Type.String(), "string")
|
||||
tst.AssertEqual(t, gft("subPtr.a").Name, "A")
|
||||
tst.AssertEqual(t, gft("subPtr.a").IsPointer, false)
|
||||
tst.AssertEqual(t, gfv("subPtr.a").(string), "4")
|
||||
|
||||
tst.AssertEqual(t, gft("str").Kind.String(), "string")
|
||||
tst.AssertEqual(t, gft("str").Type.String(), "string")
|
||||
tst.AssertEqual(t, gft("str").Name, "Str")
|
||||
@@ -99,16 +113,25 @@ func TestReflectionGetTokenValueAsMongoType(t *testing.T) {
|
||||
|
||||
type IDType string
|
||||
|
||||
type RecurseiveType struct {
|
||||
Other int `bson:"other"`
|
||||
Inner *RecurseiveType `bson:"inner"`
|
||||
}
|
||||
|
||||
type TestData struct {
|
||||
ID IDType `bson:"_id"`
|
||||
CDate time.Time `bson:"cdate"`
|
||||
Sub struct {
|
||||
A string `bson:"a"`
|
||||
} `bson:"sub"`
|
||||
SubPtr *struct {
|
||||
A string `bson:"a"`
|
||||
} `bson:"subPtr"`
|
||||
Str string `bson:"str"`
|
||||
Ptr *int `bson:"ptr"`
|
||||
Num int `bson:"num"`
|
||||
MDate rfctime.RFC3339NanoTime `bson:"mdate"`
|
||||
Rec RecurseiveType `bson:"rec"`
|
||||
}
|
||||
|
||||
coll := W[TestData](&mongo.Collection{})
|
||||
@@ -130,6 +153,9 @@ func TestReflectionGetTokenValueAsMongoType(t *testing.T) {
|
||||
}
|
||||
|
||||
tst.AssertEqual(t, gtvasmt("hello", "str").(string), "hello")
|
||||
tst.AssertEqual(t, gtvasmt("hello", "sub.a").(string), "hello")
|
||||
tst.AssertEqual(t, gtvasmt("hello", "subPtr.a").(string), "hello")
|
||||
tst.AssertEqual(t, gtvasmt("4", "rec.other").(int), 4)
|
||||
tst.AssertEqual(t, gtvasmt("4", "num").(int), 4)
|
||||
tst.AssertEqual(t, gtvasmt("asdf", "_id").(IDType), "asdf")
|
||||
tst.AssertEqual(t, gtvasmt("", "ptr").(*int), nil)
|
||||
|
Reference in New Issue
Block a user