Compare commits
6 Commits
Author | SHA1 | Date | |
---|---|---|---|
1fbae343a4
|
|||
31418bf0e6
|
|||
6d45f6f667
|
|||
f610a2202c
|
|||
2807299d46
|
|||
e872dbccec
|
4
Makefile
4
Makefile
@@ -3,7 +3,9 @@ run:
|
|||||||
echo "This is a library - can't be run" && false
|
echo "This is a library - can't be run" && false
|
||||||
|
|
||||||
test:
|
test:
|
||||||
go test ./...
|
# go test ./...
|
||||||
|
which gotestsum || go install gotest.tools/gotestsum@latest
|
||||||
|
gotestsum --format "testname" -- -tags="timetzdata sqlite_fts5 sqlite_foreign_keys" "./test"
|
||||||
|
|
||||||
version:
|
version:
|
||||||
_data/version.sh
|
_data/version.sh
|
318
bfcodegen/enum-generate.go
Normal file
318
bfcodegen/enum-generate.go
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
package bfcodegen
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/cmdext"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/rext"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"regexp"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type EnumDefVal struct {
|
||||||
|
VarName string
|
||||||
|
Value string
|
||||||
|
Description *string
|
||||||
|
}
|
||||||
|
|
||||||
|
type EnumDef struct {
|
||||||
|
File string
|
||||||
|
EnumTypeName string
|
||||||
|
Type string
|
||||||
|
Values []EnumDefVal
|
||||||
|
}
|
||||||
|
|
||||||
|
var rexPackage = 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>.*))?.*$"))
|
||||||
|
|
||||||
|
func GenerateEnumSpecs(sourceDir string, destFile string) error {
|
||||||
|
|
||||||
|
files, err := os.ReadDir(sourceDir)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
allEnums := make([]EnumDef, 0)
|
||||||
|
|
||||||
|
pkgname := ""
|
||||||
|
|
||||||
|
for _, f := range files {
|
||||||
|
if !strings.HasSuffix(f.Name(), ".go") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("========= %s =========\n\n", f.Name())
|
||||||
|
fileEnums, pn, err := processFile(f.Name())
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Printf("\n")
|
||||||
|
|
||||||
|
allEnums = append(allEnums, fileEnums...)
|
||||||
|
|
||||||
|
if pn != "" {
|
||||||
|
pkgname = pn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if pkgname == "" {
|
||||||
|
return errors.New("no package name found in any file")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = os.WriteFile(destFile, []byte(fmtOutput(allEnums, 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 processFile(fn string) ([]EnumDef, 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")
|
||||||
|
|
||||||
|
enums := make([]EnumDef, 0)
|
||||||
|
|
||||||
|
pkgname := ""
|
||||||
|
|
||||||
|
for i, line := range lines {
|
||||||
|
if i == 0 && strings.HasPrefix(line, "// Code generated by") {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
if match, ok := rexPackage.MatchFirst(line); i == 0 && ok {
|
||||||
|
pkgname = match.GroupByName("name").Value()
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if match, ok := rexEnumDef.MatchFirst(line); ok {
|
||||||
|
def := EnumDef{
|
||||||
|
File: fn,
|
||||||
|
EnumTypeName: match.GroupByName("name").Value(),
|
||||||
|
Type: match.GroupByName("type").Value(),
|
||||||
|
Values: make([]EnumDefVal, 0),
|
||||||
|
}
|
||||||
|
enums = append(enums, def)
|
||||||
|
fmt.Printf("Found enum definition { '%s' -> '%s' }\n", def.EnumTypeName, def.Type)
|
||||||
|
}
|
||||||
|
|
||||||
|
if match, ok := rexValueDef.MatchFirst(line); ok {
|
||||||
|
typename := match.GroupByName("type").Value()
|
||||||
|
def := EnumDefVal{
|
||||||
|
VarName: match.GroupByName("name").Value(),
|
||||||
|
Value: match.GroupByName("value").Value(),
|
||||||
|
Description: match.GroupByNameOrEmpty("descr").ValueOrNil(),
|
||||||
|
}
|
||||||
|
|
||||||
|
found := false
|
||||||
|
for i, v := range enums {
|
||||||
|
if v.EnumTypeName == typename {
|
||||||
|
enums[i].Values = append(enums[i].Values, def)
|
||||||
|
found = true
|
||||||
|
if def.Description != nil {
|
||||||
|
fmt.Printf("Found enum value [%s] for '%s' ('%s')\n", def.Value, def.VarName, *def.Description)
|
||||||
|
} else {
|
||||||
|
fmt.Printf("Found enum value [%s] for '%s'\n", def.Value, def.VarName)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !found {
|
||||||
|
fmt.Printf("Found non-enum value [%s] for '%s' ( looks like enum value, but no matching @enum:type )\n", def.Value, def.VarName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return enums, pkgname, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func fmtOutput(enums []EnumDef, pkgname string) string {
|
||||||
|
str := "// Code generated by permissions_gen.sh DO NOT EDIT.\n"
|
||||||
|
str += "\n"
|
||||||
|
str += "package " + pkgname + "\n"
|
||||||
|
str += "\n"
|
||||||
|
|
||||||
|
str += "import \"gogs.mikescher.com/BlackForestBytes/goext/langext\"" + "\n"
|
||||||
|
str += "\n"
|
||||||
|
|
||||||
|
str += "type Enum interface {" + "\n"
|
||||||
|
str += " Valid() bool" + "\n"
|
||||||
|
str += " ValuesAny() []any" + "\n"
|
||||||
|
str += " ValuesMeta() []EnumMetaValue" + "\n"
|
||||||
|
str += " VarName() string" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "type StringEnum interface {" + "\n"
|
||||||
|
str += " Enum" + "\n"
|
||||||
|
str += " String() string" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "type DescriptionEnum interface {" + "\n"
|
||||||
|
str += " Enum" + "\n"
|
||||||
|
str += " Description() string" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "\n"
|
||||||
|
|
||||||
|
str += "type EnumMetaValue struct {" + "\n"
|
||||||
|
str += " VarName string `json:\"varName\"`" + "\n"
|
||||||
|
str += " Value any `json:\"value\"`" + "\n"
|
||||||
|
str += " Description *string `json:\"description\"`" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "\n"
|
||||||
|
|
||||||
|
for _, enumdef := range enums {
|
||||||
|
|
||||||
|
hasDescr := langext.ArrAll(enumdef.Values, func(val EnumDefVal) bool { return val.Description != nil })
|
||||||
|
hasStr := enumdef.Type == "string"
|
||||||
|
|
||||||
|
str += "// ================================ " + enumdef.EnumTypeName + " ================================" + "\n"
|
||||||
|
str += "//" + "\n"
|
||||||
|
str += "// File: " + enumdef.File + "\n"
|
||||||
|
str += "// StringEnum: " + langext.Conditional(hasStr, "true", "false") + "\n"
|
||||||
|
str += "// DescrEnum: " + langext.Conditional(hasDescr, "true", "false") + "\n"
|
||||||
|
str += "//" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "var __" + enumdef.EnumTypeName + "Values = []" + enumdef.EnumTypeName + "{" + "\n"
|
||||||
|
for _, v := range enumdef.Values {
|
||||||
|
str += " " + v.VarName + "," + "\n"
|
||||||
|
}
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
if hasDescr {
|
||||||
|
str += "var __" + enumdef.EnumTypeName + "Descriptions = map[" + enumdef.EnumTypeName + "]string{" + "\n"
|
||||||
|
for _, v := range enumdef.Values {
|
||||||
|
str += " " + v.VarName + ": \"" + strings.TrimSpace(*v.Description) + "\"," + "\n"
|
||||||
|
}
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
str += "var __" + enumdef.EnumTypeName + "Varnames = map[" + enumdef.EnumTypeName + "]string{" + "\n"
|
||||||
|
for _, v := range enumdef.Values {
|
||||||
|
str += " " + v.VarName + ": \"" + v.VarName + "\"," + "\n"
|
||||||
|
}
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") Valid() bool {" + "\n"
|
||||||
|
str += " return langext.InArray(e, __" + enumdef.EnumTypeName + "Values)" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") Values() []" + enumdef.EnumTypeName + " {" + "\n"
|
||||||
|
str += " return __" + enumdef.EnumTypeName + "Values" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") ValuesAny() []any {" + "\n"
|
||||||
|
str += " return langext.ArrCastToAny(__" + enumdef.EnumTypeName + "Values)" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") ValuesMeta() []EnumMetaValue {" + "\n"
|
||||||
|
str += " return []EnumMetaValue{" + "\n"
|
||||||
|
for _, v := range enumdef.Values {
|
||||||
|
if hasDescr {
|
||||||
|
str += " " + fmt.Sprintf("EnumMetaValue{VarName: \"%s\", Value: %s, Description: langext.Ptr(\"%s\")},", v.VarName, v.VarName, strings.TrimSpace(*v.Description)) + "\n"
|
||||||
|
} else {
|
||||||
|
str += " " + fmt.Sprintf("EnumMetaValue{VarName: \"%s\", Value: %s, Description: nil},", v.VarName, v.VarName) + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str += " }" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
if hasStr {
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") String() string {" + "\n"
|
||||||
|
str += " return string(e)" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if hasDescr {
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") Description() string {" + "\n"
|
||||||
|
str += " if d, ok := __" + enumdef.EnumTypeName + "Descriptions[e]; ok {" + "\n"
|
||||||
|
str += " return d" + "\n"
|
||||||
|
str += " }" + "\n"
|
||||||
|
str += " return \"\"" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
str += "func (e " + enumdef.EnumTypeName + ") VarName() string {" + "\n"
|
||||||
|
str += " if d, ok := __" + enumdef.EnumTypeName + "Varnames[e]; ok {" + "\n"
|
||||||
|
str += " return d" + "\n"
|
||||||
|
str += " }" + "\n"
|
||||||
|
str += " return \"\"" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func Parse" + enumdef.EnumTypeName + "(vv string) (" + enumdef.EnumTypeName + ", bool) {" + "\n"
|
||||||
|
str += " for _, ev := range __" + enumdef.EnumTypeName + "Values {" + "\n"
|
||||||
|
str += " if string(ev) == vv {" + "\n"
|
||||||
|
str += " return ev, true" + "\n"
|
||||||
|
str += " }" + "\n"
|
||||||
|
str += " }" + "\n"
|
||||||
|
str += " return \"\", false" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func " + enumdef.EnumTypeName + "Values() []" + enumdef.EnumTypeName + " {" + "\n"
|
||||||
|
str += " return __" + enumdef.EnumTypeName + "Values" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
str += "func " + enumdef.EnumTypeName + "ValuesMeta() []EnumMetaValue {" + "\n"
|
||||||
|
str += " return []EnumMetaValue{" + "\n"
|
||||||
|
for _, v := range enumdef.Values {
|
||||||
|
if hasDescr {
|
||||||
|
str += " " + fmt.Sprintf("EnumMetaValue{VarName: \"%s\", Value: %s, Description: langext.Ptr(\"%s\")},", v.VarName, v.VarName, strings.TrimSpace(*v.Description)) + "\n"
|
||||||
|
} else {
|
||||||
|
str += " " + fmt.Sprintf("EnumMetaValue{VarName: \"%s\", Value: %s, Description: nil},", v.VarName, v.VarName) + "\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str += " }" + "\n"
|
||||||
|
str += "}" + "\n"
|
||||||
|
str += "" + "\n"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return str
|
||||||
|
}
|
@@ -8,6 +8,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -172,6 +173,20 @@ func parseEnvToValue(envval string, fullEnvKey string, rvtype reflect.Type) (ref
|
|||||||
|
|
||||||
return envcvl, nil
|
return envcvl, nil
|
||||||
|
|
||||||
|
} else if rvtype.ConvertibleTo(reflect.TypeOf(false)) {
|
||||||
|
|
||||||
|
if strings.TrimSpace(strings.ToLower(envval)) == "true" {
|
||||||
|
return reflect.ValueOf(true).Convert(rvtype), nil
|
||||||
|
} else if strings.TrimSpace(strings.ToLower(envval)) == "false" {
|
||||||
|
return reflect.ValueOf(true).Convert(rvtype), nil
|
||||||
|
} else if strings.TrimSpace(strings.ToLower(envval)) == "1" {
|
||||||
|
return reflect.ValueOf(false).Convert(rvtype), nil
|
||||||
|
} else if strings.TrimSpace(strings.ToLower(envval)) == "0" {
|
||||||
|
return reflect.ValueOf(false).Convert(rvtype), nil
|
||||||
|
} else {
|
||||||
|
return reflect.Value{}, errors.New(fmt.Sprintf("Failed to parse env-config variable '%s' to <%s, ,bool> (value := '%s')", rvtype.Name(), fullEnvKey, envval))
|
||||||
|
}
|
||||||
|
|
||||||
} else if rvtype.ConvertibleTo(reflect.TypeOf("")) {
|
} else if rvtype.ConvertibleTo(reflect.TypeOf("")) {
|
||||||
|
|
||||||
envcvl := reflect.ValueOf(envval).Convert(rvtype)
|
envcvl := reflect.ValueOf(envval).Convert(rvtype)
|
||||||
|
@@ -68,6 +68,7 @@ func TestApplyEnvOverridesSimple(t *testing.T) {
|
|||||||
V7 aliasstring `env:"TEST_V7"`
|
V7 aliasstring `env:"TEST_V7"`
|
||||||
V8 time.Duration `env:"TEST_V8"`
|
V8 time.Duration `env:"TEST_V8"`
|
||||||
V9 time.Time `env:"TEST_V9"`
|
V9 time.Time `env:"TEST_V9"`
|
||||||
|
VA bool `env:"TEST_VA"`
|
||||||
}
|
}
|
||||||
|
|
||||||
data := testdata{
|
data := testdata{
|
||||||
@@ -82,6 +83,7 @@ func TestApplyEnvOverridesSimple(t *testing.T) {
|
|||||||
V7: "7",
|
V7: "7",
|
||||||
V8: 9,
|
V8: 9,
|
||||||
V9: time.Unix(1671102873, 0),
|
V9: time.Unix(1671102873, 0),
|
||||||
|
VA: false,
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Setenv("TEST_V1", "846")
|
t.Setenv("TEST_V1", "846")
|
||||||
@@ -93,6 +95,7 @@ func TestApplyEnvOverridesSimple(t *testing.T) {
|
|||||||
t.Setenv("TEST_V7", "AAAAAA")
|
t.Setenv("TEST_V7", "AAAAAA")
|
||||||
t.Setenv("TEST_V8", "1min4s")
|
t.Setenv("TEST_V8", "1min4s")
|
||||||
t.Setenv("TEST_V9", "2009-11-10T23:00:00Z")
|
t.Setenv("TEST_V9", "2009-11-10T23:00:00Z")
|
||||||
|
t.Setenv("TEST_VA", "true")
|
||||||
|
|
||||||
err := ApplyEnvOverrides("", &data, ".")
|
err := ApplyEnvOverrides("", &data, ".")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -109,6 +112,7 @@ func TestApplyEnvOverridesSimple(t *testing.T) {
|
|||||||
tst.AssertEqual(t, data.V7, "AAAAAA")
|
tst.AssertEqual(t, data.V7, "AAAAAA")
|
||||||
tst.AssertEqual(t, data.V8, time.Second*64)
|
tst.AssertEqual(t, data.V8, time.Second*64)
|
||||||
tst.AssertEqual(t, data.V9, time.Unix(1257894000, 0).UTC())
|
tst.AssertEqual(t, data.V9, time.Unix(1257894000, 0).UTC())
|
||||||
|
tst.AssertEqual(t, data.VA, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestApplyEnvOverridesRecursive(t *testing.T) {
|
func TestApplyEnvOverridesRecursive(t *testing.T) {
|
||||||
|
@@ -4,6 +4,12 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// PTrue := &true
|
||||||
|
var PTrue = Ptr(true)
|
||||||
|
|
||||||
|
// PFalse := &false
|
||||||
|
var PFalse = Ptr(false)
|
||||||
|
|
||||||
func Ptr[T any](v T) *T {
|
func Ptr[T any](v T) *T {
|
||||||
return &v
|
return &v
|
||||||
}
|
}
|
||||||
|
@@ -81,7 +81,7 @@ func (t *RFC3339Time) UnmarshalBSONValue(bt bsontype.Type, data []byte) error {
|
|||||||
return errors.New(fmt.Sprintf("cannot unmarshal %v into RFC3339Time", bt))
|
return errors.New(fmt.Sprintf("cannot unmarshal %v into RFC3339Time", bt))
|
||||||
}
|
}
|
||||||
var tt time.Time
|
var tt time.Time
|
||||||
err := bson.Unmarshal(data, &tt)
|
err := bson.RawValue{Type: bt, Value: data}.Unmarshal(&tt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -116,6 +116,12 @@ func (t RFC3339Time) DecodeValue(dc bsoncodec.DecodeContext, vr bsonrw.ValueRead
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if val.Kind() == reflect.Ptr {
|
||||||
|
val.Set(reflect.ValueOf(&t))
|
||||||
|
} else {
|
||||||
|
val.Set(reflect.ValueOf(t))
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
91
sq/converter.go
Normal file
91
sq/converter.go
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
package sq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
//TODO UNFINISHED
|
||||||
|
// this is not finished
|
||||||
|
// idea was that we can register converter in the database struct
|
||||||
|
// they get inherited from the transactions
|
||||||
|
// and when marshallingunmarshaling (sq.Query | sq.QueryAll)
|
||||||
|
// or marshaling (sq.InsertSingle)
|
||||||
|
// the types get converter automatically...
|
||||||
|
|
||||||
|
type DBTypeConverter interface {
|
||||||
|
ModelTypeString() string
|
||||||
|
DBTypeString() string
|
||||||
|
ModelToDB(v any) (any, error)
|
||||||
|
DBToModel(v any) (any, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
var ConverterBoolToBit = NewDBTypeConverter[bool, int](func(v bool) (int, error) {
|
||||||
|
return langext.Conditional(v, 1, 0), nil
|
||||||
|
}, func(v int) (bool, error) {
|
||||||
|
if v == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
if v == 1 {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return false, errors.New(fmt.Sprintf("invalid valud for boolean: '%d'", v))
|
||||||
|
})
|
||||||
|
|
||||||
|
var ConverterTimeToUnixMillis = NewDBTypeConverter[time.Time, int64](func(v time.Time) (int64, error) {
|
||||||
|
return v.UnixMilli(), nil
|
||||||
|
}, func(v int64) (time.Time, error) {
|
||||||
|
return time.UnixMilli(v), nil
|
||||||
|
})
|
||||||
|
|
||||||
|
var ConverterOptTimeToUnixMillis = NewDBTypeConverter[*time.Time, *int64](func(v *time.Time) (*int64, error) {
|
||||||
|
if v == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return langext.Ptr(v.UnixMilli()), nil
|
||||||
|
}, func(v *int64) (*time.Time, error) {
|
||||||
|
if v == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
return langext.Ptr(time.UnixMilli(*v)), nil
|
||||||
|
})
|
||||||
|
|
||||||
|
type dbTypeConverterImpl[TModelData any, TDBData any] struct {
|
||||||
|
dbTypeString string
|
||||||
|
modelTypeString string
|
||||||
|
todb func(v TModelData) (TDBData, error)
|
||||||
|
tomodel func(v TDBData) (TModelData, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *dbTypeConverterImpl[TModelData, TDBData]) ModelTypeString() string {
|
||||||
|
return t.modelTypeString
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *dbTypeConverterImpl[TModelData, TDBData]) DBTypeString() string {
|
||||||
|
return t.dbTypeString
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *dbTypeConverterImpl[TModelData, TDBData]) ModelToDB(v any) (any, error) {
|
||||||
|
if vv, ok := v.(TModelData); ok {
|
||||||
|
return t.todb(vv)
|
||||||
|
}
|
||||||
|
return nil, errors.New(fmt.Sprintf("Unexpected value in DBTypeConverter, expected '%s', found '%T'", t.modelTypeString, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *dbTypeConverterImpl[TModelData, TDBData]) DBToModel(v any) (any, error) {
|
||||||
|
if vv, ok := v.(TDBData); ok {
|
||||||
|
return t.tomodel(vv)
|
||||||
|
}
|
||||||
|
return nil, errors.New(fmt.Sprintf("Unexpected value in DBTypeConverter, expected '%s', found '%T'", t.dbTypeString, v))
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDBTypeConverter[TModelData any, TDBData any](todb func(v TModelData) (TDBData, error), tomodel func(v TDBData) (TModelData, error)) DBTypeConverter {
|
||||||
|
return &dbTypeConverterImpl[TModelData, TDBData]{
|
||||||
|
dbTypeString: fmt.Sprintf("%T", *new(TDBData)),
|
||||||
|
modelTypeString: fmt.Sprintf("%T", *new(TModelData)),
|
||||||
|
todb: todb,
|
||||||
|
tomodel: tomodel,
|
||||||
|
}
|
||||||
|
}
|
@@ -36,6 +36,10 @@ func HashSqliteSchema(ctx context.Context, schemaStr string) (string, error) {
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return HashSqliteDatabase(ctx, db)
|
||||||
|
}
|
||||||
|
|
||||||
|
func HashSqliteDatabase(ctx context.Context, db DB) (string, error) {
|
||||||
ss, err := CreateSqliteDatabaseSchemaString(ctx, db)
|
ss, err := CreateSqliteDatabaseSchemaString(ctx, db)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
|
Reference in New Issue
Block a user