Compare commits
	
		
			16 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 303bd04649 | |||
| 7bda674939 | |||
| 126d4fbd0b | |||
| fed8bccaab | |||
| 47b6a6b508 | |||
| 764ce79a71 | |||
| b876c64ba2 | |||
| 8d52b41f57 | |||
| f47e2a33fe | |||
| 9321938dad | |||
| 3828d601a2 | |||
| 2e713c808d | |||
| 6602f86b43 | |||
| 24d9f0fdc7 | |||
| 8446b2da22 | |||
| 758e5a67b5 | 
| @@ -441,9 +441,9 @@ func (b *Builder) Output(ctx context.Context, g *gin.Context) { | ||||
|  | ||||
| 	b.errorData.Output(g) | ||||
|  | ||||
| 	if b.errorData.Severity == SevErr || b.errorData.Severity == SevFatal { | ||||
| 	if (b.errorData.Severity == SevErr || b.errorData.Severity == SevFatal) && (pkgconfig.ZeroLogErrGinOutput || pkgconfig.ZeroLogAllGinOutput) { | ||||
| 		b.errorData.Log(stackSkipLogger.Error()) | ||||
| 	} else if b.errorData.Severity == SevWarn { | ||||
| 	} else if (b.errorData.Severity == SevWarn) && (pkgconfig.ZeroLogAllGinOutput) { | ||||
| 		b.errorData.Log(stackSkipLogger.Warn()) | ||||
| 	} | ||||
|  | ||||
|   | ||||
| @@ -181,7 +181,7 @@ func getReflectedMetaValues(value interface{}, remainingDepth int) map[string]Me | ||||
|  | ||||
| 	jsonval, err := json.Marshal(value) | ||||
| 	if err != nil { | ||||
| 		panic(err) // gets recovered later up | ||||
| 		return map[string]MetaValue{"": {DataType: MDTString, Value: fmt.Sprintf("Failed to Marshal %T:\n%+v", value, value)}} | ||||
| 	} | ||||
|  | ||||
| 	return map[string]MetaValue{"": {DataType: MDTString, Value: string(jsonval)}} | ||||
|   | ||||
| @@ -14,6 +14,8 @@ type ErrorPackageConfig struct { | ||||
| 	ExtendGinOutput        func(err *ExErr, json map[string]any)            // (Optionally) extend the gin output with more fields | ||||
| 	ExtendGinDataOutput    func(err *ExErr, depth int, json map[string]any) // (Optionally) extend the gin `__data` output with more fields | ||||
| 	DisableErrorWrapping   bool                                             // Disables the exerr.Wrap()...Build() function - will always return the original error | ||||
| 	ZeroLogErrGinOutput    bool                                             // autom print zerolog logs on ginext.Error() / .Output(gin)  (for SevErr and SevFatal) | ||||
| 	ZeroLogAllGinOutput    bool                                             // autom print zerolog logs on ginext.Error() / .Output(gin)  (for all Severities) | ||||
| } | ||||
|  | ||||
| type ErrorPackageConfigInit struct { | ||||
| @@ -25,6 +27,8 @@ type ErrorPackageConfigInit struct { | ||||
| 	ExtendGinOutput        func(err *ExErr, json map[string]any) | ||||
| 	ExtendGinDataOutput    func(err *ExErr, depth int, json map[string]any) | ||||
| 	DisableErrorWrapping   *bool | ||||
| 	ZeroLogErrGinOutput    *bool | ||||
| 	ZeroLogAllGinOutput    *bool | ||||
| } | ||||
|  | ||||
| var initialized = false | ||||
| @@ -38,6 +42,8 @@ var pkgconfig = ErrorPackageConfig{ | ||||
| 	ExtendGinOutput:        func(err *ExErr, json map[string]any) {}, | ||||
| 	ExtendGinDataOutput:    func(err *ExErr, depth int, json map[string]any) {}, | ||||
| 	DisableErrorWrapping:   false, | ||||
| 	ZeroLogErrGinOutput:    true, | ||||
| 	ZeroLogAllGinOutput:    false, | ||||
| } | ||||
|  | ||||
| // Init initializes the exerr packages | ||||
| @@ -67,6 +73,8 @@ func Init(cfg ErrorPackageConfigInit) { | ||||
| 		ExtendGinOutput:        ego, | ||||
| 		ExtendGinDataOutput:    egdo, | ||||
| 		DisableErrorWrapping:   langext.Coalesce(cfg.DisableErrorWrapping, pkgconfig.DisableErrorWrapping), | ||||
| 		ZeroLogAllGinOutput:    langext.Coalesce(cfg.ZeroLogAllGinOutput, pkgconfig.ZeroLogAllGinOutput), | ||||
| 		ZeroLogErrGinOutput:    langext.Coalesce(cfg.ZeroLogErrGinOutput, pkgconfig.ZeroLogErrGinOutput), | ||||
| 	} | ||||
|  | ||||
| 	initialized = true | ||||
|   | ||||
| @@ -15,10 +15,10 @@ func (ee *ExErr) toJson(depth int, applyExtendListener bool, outputMeta bool) la | ||||
| 		ginJson["id"] = ee.UniqueID | ||||
| 	} | ||||
| 	if ee.Category != CatWrap { | ||||
| 		ginJson["category"] = ee.Category | ||||
| 		ginJson["category"] = ee.Category.Category | ||||
| 	} | ||||
| 	if ee.Type != TypeWrap { | ||||
| 		ginJson["type"] = ee.Type | ||||
| 		ginJson["type"] = ee.Type.Key | ||||
| 	} | ||||
| 	if ee.StatusCode != nil { | ||||
| 		ginJson["statuscode"] = ee.StatusCode | ||||
| @@ -30,7 +30,7 @@ func (ee *ExErr) toJson(depth int, applyExtendListener bool, outputMeta bool) la | ||||
| 		ginJson["caller"] = ee.Caller | ||||
| 	} | ||||
| 	if ee.Severity != SevErr { | ||||
| 		ginJson["severity"] = ee.Severity | ||||
| 		ginJson["severity"] = ee.Severity.Severity | ||||
| 	} | ||||
| 	if ee.Timestamp != (time.Time{}) { | ||||
| 		ginJson["time"] = ee.Timestamp.Format(time.RFC3339) | ||||
|   | ||||
| @@ -19,6 +19,7 @@ type GinWrapper struct { | ||||
| 	engine          *gin.Engine | ||||
| 	suppressGinLogs bool | ||||
|  | ||||
| 	opt                   Options | ||||
| 	allowCors             bool | ||||
| 	ginDebug              bool | ||||
| 	bufferBody            bool | ||||
| @@ -37,13 +38,15 @@ type ginRouteSpec struct { | ||||
| } | ||||
|  | ||||
| type Options struct { | ||||
| 	AllowCors             *bool                                     // Add cors handler to allow all CORS requests on the default http methods | ||||
| 	GinDebug              *bool                                     // Set gin.debug to true (adds more logs) | ||||
| 	SuppressGinLogs       *bool                                     // Suppress our custom gin logs (even if GinDebug == true) | ||||
| 	BufferBody            *bool                                     // Buffers the input body stream, this way the ginext error handler can later include the whole request body | ||||
| 	Timeout               *time.Duration                            // The default handler timeout | ||||
| 	ListenerBeforeRequest []func(g *gin.Context)                    // Register listener that are called before the handler method | ||||
| 	ListenerAfterRequest  []func(g *gin.Context, resp HTTPResponse) // Register listener that are called after the handler method | ||||
| 	AllowCors                *bool                                     // Add cors handler to allow all CORS requests on the default http methods | ||||
| 	GinDebug                 *bool                                     // Set gin.debug to true (adds more logs) | ||||
| 	SuppressGinLogs          *bool                                     // Suppress our custom gin logs (even if GinDebug == true) | ||||
| 	BufferBody               *bool                                     // Buffers the input body stream, this way the ginext error handler can later include the whole request body | ||||
| 	Timeout                  *time.Duration                            // The default handler timeout | ||||
| 	ListenerBeforeRequest    []func(g *gin.Context)                    // Register listener that are called before the handler method | ||||
| 	ListenerAfterRequest     []func(g *gin.Context, resp HTTPResponse) // Register listener that are called after the handler method | ||||
| 	DebugTrimHandlerPrefixes []string                                  // Trim these prefixes from the handler names in the debug print | ||||
| 	DebugReplaceHandlerNames map[string]string                         // Replace handler names in debug output | ||||
| } | ||||
|  | ||||
| // NewEngine creates a new (wrapped) ginEngine | ||||
| @@ -52,6 +55,7 @@ func NewEngine(opt Options) *GinWrapper { | ||||
|  | ||||
| 	wrapper := &GinWrapper{ | ||||
| 		engine:                engine, | ||||
| 		opt:                   opt, | ||||
| 		suppressGinLogs:       langext.Coalesce(opt.SuppressGinLogs, false), | ||||
| 		allowCors:             langext.Coalesce(opt.AllowCors, false), | ||||
| 		ginDebug:              langext.Coalesce(opt.GinDebug, true), | ||||
| @@ -185,6 +189,18 @@ func (w *GinWrapper) cleanMiddlewareName(fname string) string { | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for _, pfx := range w.opt.DebugTrimHandlerPrefixes { | ||||
| 		if strings.HasPrefix(fname, pfx) { | ||||
| 			fname = fname[len(pfx):] | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for k, v := range langext.ForceMap(w.opt.DebugReplaceHandlerNames) { | ||||
| 		if strings.EqualFold(fname, k) { | ||||
| 			fname = v | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	return fname | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package ginext | ||||
|  | ||||
| import ( | ||||
| 	"context" | ||||
| 	"fmt" | ||||
| 	"github.com/gin-gonic/gin" | ||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/exerr" | ||||
| @@ -410,7 +411,8 @@ func (j jsonAPIErrResponse) Write(g *gin.Context) { | ||||
| 	for _, v := range j.cookies { | ||||
| 		g.SetCookie(v.name, v.value, v.maxAge, v.path, v.domain, v.secure, v.httpOnly) | ||||
| 	} | ||||
| 	j.err.Output(g) | ||||
|  | ||||
| 	exerr.Get(j.err).Output(context.Background(), g) | ||||
|  | ||||
| 	j.err.CallListener(exerr.MethodOutput) | ||||
| } | ||||
|   | ||||
							
								
								
									
										25
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										25
									
								
								go.mod
									
									
									
									
									
								
							| @@ -5,19 +5,22 @@ go 1.22 | ||||
| require ( | ||||
| 	github.com/gin-gonic/gin v1.9.1 | ||||
| 	github.com/glebarez/go-sqlite v1.22.0 // only needed for tests -.- | ||||
| 	github.com/jmoiron/sqlx v1.3.5 | ||||
| 	github.com/jmoiron/sqlx v1.4.0 | ||||
| 	github.com/rs/xid v1.5.0 | ||||
| 	github.com/rs/zerolog v1.32.0 | ||||
| 	go.mongodb.org/mongo-driver v1.14.0 | ||||
| 	golang.org/x/crypto v0.21.0 | ||||
| 	golang.org/x/sys v0.18.0 | ||||
| 	golang.org/x/term v0.18.0 | ||||
| 	go.mongodb.org/mongo-driver v1.15.0 | ||||
| 	golang.org/x/crypto v0.22.0 | ||||
| 	golang.org/x/sys v0.19.0 | ||||
| 	golang.org/x/term v0.19.0 | ||||
| ) | ||||
|  | ||||
| require ( | ||||
| 	github.com/bytedance/sonic v1.11.3 // indirect | ||||
| 	github.com/bytedance/sonic v1.11.6 // indirect | ||||
| 	github.com/bytedance/sonic/loader v0.1.1 // indirect | ||||
| 	github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect | ||||
| 	github.com/chenzhuoyu/iasm v0.9.1 // indirect | ||||
| 	github.com/cloudwego/base64x v0.1.4 // indirect | ||||
| 	github.com/cloudwego/iasm v0.2.0 // indirect | ||||
| 	github.com/dustin/go-humanize v1.0.1 // indirect | ||||
| 	github.com/gabriel-vasile/mimetype v1.4.3 // indirect | ||||
| 	github.com/gin-contrib/sse v0.1.0 // indirect | ||||
| @@ -28,7 +31,7 @@ require ( | ||||
| 	github.com/golang/snappy v0.0.4 // indirect | ||||
| 	github.com/google/uuid v1.5.0 // indirect | ||||
| 	github.com/json-iterator/go v1.1.12 // indirect | ||||
| 	github.com/klauspost/compress v1.17.7 // indirect | ||||
| 	github.com/klauspost/compress v1.17.8 // indirect | ||||
| 	github.com/klauspost/cpuid/v2 v2.2.7 // indirect | ||||
| 	github.com/leodido/go-urn v1.4.0 // indirect | ||||
| 	github.com/mattn/go-colorable v0.1.13 // indirect | ||||
| @@ -36,17 +39,17 @@ require ( | ||||
| 	github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect | ||||
| 	github.com/modern-go/reflect2 v1.0.2 // indirect | ||||
| 	github.com/montanaflynn/stats v0.7.1 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.2.0 // indirect | ||||
| 	github.com/pelletier/go-toml/v2 v2.2.1 // indirect | ||||
| 	github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect | ||||
| 	github.com/twitchyliquid64/golang-asm v0.15.1 // indirect | ||||
| 	github.com/ugorji/go/codec v1.2.12 // indirect | ||||
| 	github.com/xdg-go/pbkdf2 v1.0.0 // indirect | ||||
| 	github.com/xdg-go/scram v1.1.2 // indirect | ||||
| 	github.com/xdg-go/stringprep v1.0.4 // indirect | ||||
| 	github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a // indirect | ||||
| 	github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 // indirect | ||||
| 	golang.org/x/arch v0.7.0 // indirect | ||||
| 	golang.org/x/net v0.22.0 // indirect | ||||
| 	golang.org/x/sync v0.6.0 // indirect | ||||
| 	golang.org/x/net v0.24.0 // indirect | ||||
| 	golang.org/x/sync v0.7.0 // indirect | ||||
| 	golang.org/x/text v0.14.0 // indirect | ||||
| 	google.golang.org/protobuf v1.33.0 // indirect | ||||
| 	gopkg.in/yaml.v3 v3.0.1 // indirect | ||||
|   | ||||
							
								
								
									
										45
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								go.sum
									
									
									
									
									
								
							| @@ -1,3 +1,4 @@ | ||||
| filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= | ||||
| github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= | ||||
| github.com/bytedance/sonic v1.10.0-rc/go.mod h1:ElCzW+ufi8qKqNW0FY314xriJhyJhuoJ3gFZdAHF7NM= | ||||
| github.com/bytedance/sonic v1.10.2 h1:GQebETVBxYB7JGWJtLBi07OVzWwt+8dWA00gEVW2ZFE= | ||||
| @@ -10,6 +11,15 @@ github.com/bytedance/sonic v1.11.2 h1:ywfwo0a/3j9HR8wsYGWsIWl2mvRsI950HyoxiBERw5 | ||||
| github.com/bytedance/sonic v1.11.2/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= | ||||
| github.com/bytedance/sonic v1.11.3 h1:jRN+yEjakWh8aK5FzrciUHG8OFXK+4/KrAX/ysEtHAA= | ||||
| github.com/bytedance/sonic v1.11.3/go.mod h1:iZcSUejdk5aukTND/Eu/ivjQuEL0Cu9/rf50Hi0u/g4= | ||||
| github.com/bytedance/sonic v1.11.4 h1:8+OMLSSDDm2/qJc6ld5K5Sm62NK9VHcUKk0NzBoMAM4= | ||||
| github.com/bytedance/sonic v1.11.4/go.mod h1:YrWEqYtlBPS6LUA0vpuG79a1trsh4Ae41uWUWUreHhE= | ||||
| github.com/bytedance/sonic v1.11.5 h1:G00FYjjqll5iQ1PYXynbg/hyzqBqavH8Mo9/oTopd9k= | ||||
| github.com/bytedance/sonic v1.11.5/go.mod h1:X2PC2giUdj/Cv2lliWFLk6c/DUQok5rViJSemeB0wDw= | ||||
| github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0= | ||||
| github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4= | ||||
| github.com/bytedance/sonic/loader v0.1.0/go.mod h1:UmRT+IRTGKz/DAkzcEGzyVqQFJ7H9BqwBO3pm9H/+HY= | ||||
| github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM= | ||||
| github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= | ||||
| 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= | ||||
| @@ -17,6 +27,17 @@ github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d/go.mod h1:8EPpV | ||||
| github.com/chenzhuoyu/iasm v0.9.0/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= | ||||
| github.com/chenzhuoyu/iasm v0.9.1 h1:tUHQJXo3NhBqw6s33wkGn9SP3bvrWLdlVIJ3hQBL7P0= | ||||
| github.com/chenzhuoyu/iasm v0.9.1/go.mod h1:Xjy2NpN3h7aUqeqM+woSuuvxmIe6+DDsiNLIrkAmYog= | ||||
| github.com/cloudwego/base64x v0.1.0 h1:Tg5q9tq1khq9Y9UwfoC6zkHK0FypN2GLDvhqFceOL8U= | ||||
| github.com/cloudwego/base64x v0.1.0/go.mod h1:lM8nFiNbg74QgesNo6EAtv8N9tlRjBWExmHoNDa3PkU= | ||||
| github.com/cloudwego/base64x v0.1.3 h1:b5J/l8xolB7dyDTTmhJP2oTs5LdrjyrUFuNxdfq5hAg= | ||||
| github.com/cloudwego/base64x v0.1.3/go.mod h1:1+1K5BUHIQzyapgpF7LwvOGAEDicKtt1umPV+aN8pi8= | ||||
| github.com/cloudwego/base64x v0.1.4 h1:jwCgWpFanWmN8xoIUHa2rtzmkd5J2plF/dnLS6Xd/0Y= | ||||
| github.com/cloudwego/base64x v0.1.4/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= | ||||
| github.com/cloudwego/iasm v0.0.9/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= | ||||
| github.com/cloudwego/iasm v0.1.0 h1:q0OuhwWDMyi3nlrQ6kIr0Yx0c3FI6cq/OZWKodIDdz8= | ||||
| github.com/cloudwego/iasm v0.1.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= | ||||
| github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= | ||||
| github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= | ||||
| github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= | ||||
| 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= | ||||
| @@ -47,6 +68,8 @@ github.com/go-playground/validator/v10 v10.19.0 h1:ol+5Fu+cSq9JD7SoSqe04GMI92cbn | ||||
| github.com/go-playground/validator/v10 v10.19.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM= | ||||
| github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= | ||||
| github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= | ||||
| 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/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= | ||||
| github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= | ||||
| github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= | ||||
| @@ -64,6 +87,8 @@ github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= | ||||
| github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||
| github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g= | ||||
| github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ= | ||||
| github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= | ||||
| github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= | ||||
| github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= | ||||
| github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= | ||||
| github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= | ||||
| @@ -73,6 +98,8 @@ github.com/klauspost/compress v1.17.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2e | ||||
| github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM= | ||||
| github.com/klauspost/compress v1.17.7 h1:ehO88t2UGzQK66LMdE8tibEd1ErmzZjNEqWkjLAKQQg= | ||||
| github.com/klauspost/compress v1.17.7/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= | ||||
| github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU= | ||||
| github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= | ||||
| github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= | ||||
| github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc= | ||||
| github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws= | ||||
| @@ -85,6 +112,7 @@ github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= | ||||
| github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= | ||||
| github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0= | ||||
| github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= | ||||
| github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= | ||||
| github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= | ||||
| github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= | ||||
| github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= | ||||
| @@ -94,6 +122,7 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D | ||||
| github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= | ||||
| github.com/mattn/go-sqlite3 v1.14.19 h1:fhGleo2h1p8tVChob4I9HpmVFIAkKGpiukdrgQbWfGI= | ||||
| github.com/mattn/go-sqlite3 v1.14.19/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= | ||||
| github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= | ||||
| 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= | ||||
| @@ -106,6 +135,8 @@ github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOS | ||||
| github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc= | ||||
| github.com/pelletier/go-toml/v2 v2.2.0 h1:QLgLl2yMN7N+ruc31VynXs1vhMZa7CeHHejIeBAsoHo= | ||||
| github.com/pelletier/go-toml/v2 v2.2.0/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= | ||||
| github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtosvtEhg= | ||||
| github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs= | ||||
| github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= | ||||
| github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= | ||||
| github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= | ||||
| @@ -144,11 +175,15 @@ github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gi | ||||
| github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d/go.mod h1:rHwXgn7JulP+udvsHwJoVG1YGAP6VLg4y9I5dyZdqmA= | ||||
| github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a h1:fZHgsYlfvtyqToslyjUt3VOPF4J7aK/3MPcK7xp3PDk= | ||||
| github.com/youmark/pkcs8 v0.0.0-20201027041543-1326539a0a0a/go.mod h1:ul22v+Nro/R083muKhosV54bj5niojjWZvU8xrevuH4= | ||||
| github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76 h1:tBiBTKHnIjovYoLX/TPkcf+OjqqKGQrPtGT3Foz+Pgo= | ||||
| github.com/youmark/pkcs8 v0.0.0-20240424034433-3c2c7870ae76/go.mod h1:SQliXeA7Dhkt//vS29v3zpbEwoa+zb2Cn5xj5uO4K5U= | ||||
| github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= | ||||
| go.mongodb.org/mongo-driver v1.13.1 h1:YIc7HTYsKndGK4RFzJ3covLz1byri52x0IoMB0Pt/vk= | ||||
| go.mongodb.org/mongo-driver v1.13.1/go.mod h1:wcDf1JBCXy2mOW0bWHwO/IOYqdca1MPCwDtFu/Z9+eo= | ||||
| go.mongodb.org/mongo-driver v1.14.0 h1:P98w8egYRjYe3XDjxhYJagTokP/H6HzlsnojRgZRd80= | ||||
| go.mongodb.org/mongo-driver v1.14.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= | ||||
| go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUSLc= | ||||
| go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c= | ||||
| golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= | ||||
| golang.org/x/arch v0.7.0 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc= | ||||
| golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= | ||||
| @@ -166,6 +201,8 @@ golang.org/x/crypto v0.20.0 h1:jmAMJJZXr5KiCw05dfYK9QnqaqKLYXijU23lsEdcQqg= | ||||
| golang.org/x/crypto v0.20.0/go.mod h1:Xwo95rrVNIoSMx9wa1JroENMToLWn3RNVrTBpLHgZPQ= | ||||
| golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= | ||||
| golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= | ||||
| 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/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= | ||||
| @@ -180,10 +217,14 @@ golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= | ||||
| golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= | ||||
| golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc= | ||||
| golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= | ||||
| golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w= | ||||
| golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= | ||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= | ||||
| golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | ||||
| golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= | ||||
| golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= | ||||
| golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||
| golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= | ||||
| @@ -201,6 +242,8 @@ golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y= | ||||
| golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||
| golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= | ||||
| golang.org/x/sys v0.18.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/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.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE= | ||||
| @@ -209,6 +252,8 @@ golang.org/x/term v0.17.0 h1:mkTF7LCd6WGJNL3K1Ad7kwxNfYAW6a8a8QqtMblp/4U= | ||||
| golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= | ||||
| golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= | ||||
| golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= | ||||
| 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.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= | ||||
| golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
| golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| package goext | ||||
|  | ||||
| const GoextVersion = "0.0.426" | ||||
| const GoextVersion = "0.0.442" | ||||
|  | ||||
| const GoextVersionTimestamp = "2024-04-01T16:03:00+0200" | ||||
| const GoextVersionTimestamp = "2024-04-29T17:24:10+0200" | ||||
|   | ||||
| @@ -477,6 +477,10 @@ func ArrRemove[T comparable](arr []T, needle T) []T { | ||||
| 	return arr | ||||
| } | ||||
|  | ||||
| func ArrRemoveAt[T any](arr []T, idx int) []T { | ||||
| 	return append(arr[:idx], arr[idx+1:]...) | ||||
| } | ||||
|  | ||||
| func ArrExcept[T comparable](arr []T, needles ...T) []T { | ||||
| 	r := make([]T, 0, len(arr)) | ||||
| 	rmlist := ArrToSet(needles) | ||||
|   | ||||
| @@ -63,3 +63,51 @@ func PatchRemJson[JV string | []byte](rawjson JV, key string) (JV, error) { | ||||
|  | ||||
| 	return JV(newjson), nil | ||||
| } | ||||
|  | ||||
| func MarshalJsonOrPanic(v any) string { | ||||
| 	bin, err := json.Marshal(v) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return string(bin) | ||||
| } | ||||
|  | ||||
| func MarshalJsonOrDefault(v any, def string) string { | ||||
| 	bin, err := json.Marshal(v) | ||||
| 	if err != nil { | ||||
| 		return def | ||||
| 	} | ||||
| 	return string(bin) | ||||
| } | ||||
|  | ||||
| func MarshalJsonOrNil(v any) *string { | ||||
| 	bin, err := json.Marshal(v) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return Ptr(string(bin)) | ||||
| } | ||||
|  | ||||
| func MarshalJsonIndentOrPanic(v any, prefix, indent string) string { | ||||
| 	bin, err := json.MarshalIndent(v, prefix, indent) | ||||
| 	if err != nil { | ||||
| 		panic(err) | ||||
| 	} | ||||
| 	return string(bin) | ||||
| } | ||||
|  | ||||
| func MarshalJsonIndentOrDefault(v any, prefix, indent string, def string) string { | ||||
| 	bin, err := json.MarshalIndent(v, prefix, indent) | ||||
| 	if err != nil { | ||||
| 		return def | ||||
| 	} | ||||
| 	return string(bin) | ||||
| } | ||||
|  | ||||
| func MarshalJsonIndentOrNil(v any, prefix, indent string) *string { | ||||
| 	bin, err := json.MarshalIndent(v, prefix, indent) | ||||
| 	if err != nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return Ptr(string(bin)) | ||||
| } | ||||
|   | ||||
							
								
								
									
										19
									
								
								langext/object.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								langext/object.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,19 @@ | ||||
| package langext | ||||
|  | ||||
| import "encoding/json" | ||||
|  | ||||
| func DeepCopyByJson[T any](v T) (T, error) { | ||||
|  | ||||
| 	bin, err := json.Marshal(v) | ||||
| 	if err != nil { | ||||
| 		return *new(T), err | ||||
| 	} | ||||
|  | ||||
| 	var result T | ||||
| 	err = json.Unmarshal(bin, &result) | ||||
| 	if err != nil { | ||||
| 		return *new(T), err | ||||
| 	} | ||||
|  | ||||
| 	return result, nil | ||||
| } | ||||
| @@ -27,6 +27,10 @@ func DblPtrNil[T any]() **T { | ||||
| 	return &v | ||||
| } | ||||
|  | ||||
| func ArrPtr[T any](v ...T) *[]T { | ||||
| 	return &v | ||||
| } | ||||
|  | ||||
| func PtrInt32(v int32) *int32 { | ||||
| 	return &v | ||||
| } | ||||
|   | ||||
| @@ -2,29 +2,44 @@ package reflectext | ||||
|  | ||||
| import ( | ||||
| 	"encoding/json" | ||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/langext" | ||||
| 	"reflect" | ||||
| ) | ||||
|  | ||||
| type ConvertStructToMapOpt struct { | ||||
| 	KeepJsonMarshalTypes bool | ||||
| 	MaxDepth             *int | ||||
| } | ||||
|  | ||||
| func ConvertStructToMap(v any, opts ...ConvertStructToMapOpt) any { | ||||
| func ConvertStructToMap(v any, opts ...ConvertStructToMapOpt) map[string]any { | ||||
| 	opt := ConvertStructToMapOpt{} | ||||
| 	if len(opts) > 0 { | ||||
| 		opt = opts[0] | ||||
| 	} | ||||
| 	return reflectToMap(reflect.ValueOf(v), opt) | ||||
|  | ||||
| 	res := reflectToMap(reflect.ValueOf(v), 1, opt) | ||||
|  | ||||
| 	if v, ok := res.(map[string]any); ok { | ||||
| 		return v | ||||
| 	} else if langext.IsNil(res) { | ||||
| 		return nil | ||||
| 	} else { | ||||
| 		panic("not an object") | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func reflectToMap(fv reflect.Value, opt ConvertStructToMapOpt) any { | ||||
| func reflectToMap(fv reflect.Value, depth int, opt ConvertStructToMapOpt) any { | ||||
|  | ||||
| 	if opt.MaxDepth != nil && depth > *opt.MaxDepth { | ||||
| 		return fv.Interface() | ||||
| 	} | ||||
|  | ||||
| 	if fv.Kind() == reflect.Ptr { | ||||
|  | ||||
| 		if fv.IsNil() { | ||||
| 			return nil | ||||
| 		} else { | ||||
| 			return reflectToMap(fv.Elem(), opt) | ||||
| 			return reflectToMap(fv.Elem(), depth, opt) | ||||
| 		} | ||||
|  | ||||
| 	} | ||||
| @@ -41,7 +56,7 @@ func reflectToMap(fv reflect.Value, opt ConvertStructToMapOpt) any { | ||||
| 		arrlen := fv.Len() | ||||
| 		arr := make([]any, arrlen) | ||||
| 		for i := 0; i < arrlen; i++ { | ||||
| 			arr[i] = reflectToMap(fv.Index(i), opt) | ||||
| 			arr[i] = reflectToMap(fv.Index(i), depth+1, opt) | ||||
| 		} | ||||
| 		return arr | ||||
|  | ||||
| @@ -52,7 +67,7 @@ func reflectToMap(fv reflect.Value, opt ConvertStructToMapOpt) any { | ||||
| 		arrlen := fv.Len() | ||||
| 		arr := make([]any, arrlen) | ||||
| 		for i := 0; i < arrlen; i++ { | ||||
| 			arr[i] = reflectToMap(fv.Index(i), opt) | ||||
| 			arr[i] = reflectToMap(fv.Index(i), depth+1, opt) | ||||
| 		} | ||||
| 		return arr | ||||
|  | ||||
| @@ -75,7 +90,7 @@ func reflectToMap(fv reflect.Value, opt ConvertStructToMapOpt) any { | ||||
|  | ||||
| 		for i := 0; i < fv.NumField(); i++ { | ||||
| 			if fv.Type().Field(i).IsExported() { | ||||
| 				res[fv.Type().Field(i).Name] = reflectToMap(fv.Field(i), opt) | ||||
| 				res[fv.Type().Field(i).Name] = reflectToMap(fv.Field(i), depth+1, opt) | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
|   | ||||
							
								
								
									
										42
									
								
								reflectext/convertToMap_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								reflectext/convertToMap_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| package reflectext | ||||
|  | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/langext" | ||||
| 	"testing" | ||||
| 	"time" | ||||
| ) | ||||
|  | ||||
| func TestConvertStructToMap(t *testing.T) { | ||||
|  | ||||
| 	type tst struct { | ||||
| 		FieldA  int | ||||
| 		FieldB  string | ||||
| 		FieldC  time.Time | ||||
| 		FieldD  []float64 | ||||
| 		FieldE1 *int | ||||
| 		FieldE2 **int | ||||
| 		FieldE3 *int | ||||
| 		FieldE4 **int | ||||
| 		FieldE5 *int | ||||
| 		FieldE6 **int | ||||
| 	} | ||||
|  | ||||
| 	value := tst{ | ||||
| 		FieldA:  123, | ||||
| 		FieldB:  "hello", | ||||
| 		FieldC:  time.Date(2020, 05, 12, 8, 30, 0, 0, time.UTC), | ||||
| 		FieldD:  []float64{1, 2, 3, 4, 5, 6, 7}, | ||||
| 		FieldE1: nil, | ||||
| 		FieldE2: nil, | ||||
| 		FieldE3: langext.Ptr(12), | ||||
| 		FieldE4: langext.DblPtr(12), | ||||
| 		FieldE5: nil, | ||||
| 		FieldE6: langext.DblPtrNil[int](), | ||||
| 	} | ||||
|  | ||||
| 	valueOut := ConvertStructToMap(value, ConvertStructToMapOpt{KeepJsonMarshalTypes: true}) | ||||
|  | ||||
| 	fmt.Printf("%+v\n", valueOut) | ||||
|  | ||||
| } | ||||
| @@ -47,9 +47,3 @@ func TestGetMapField(t *testing.T) { | ||||
| 	tst.AssertEqual(t, fmt.Sprint(GetMapField[PseudoInt](maany2, "Test")), "12 true") | ||||
| 	tst.AssertEqual(t, fmt.Sprint(GetMapField[PseudoInt](maany2, "Test2")), "0 false") | ||||
| } | ||||
|  | ||||
| func main2() { | ||||
| } | ||||
|  | ||||
| func main() { | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsoncodec" | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsonrw" | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsontype" | ||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/langext" | ||||
| 	"reflect" | ||||
| 	"time" | ||||
| ) | ||||
| @@ -245,6 +246,13 @@ func NewRFC3339(t time.Time) RFC3339Time { | ||||
| 	return RFC3339Time(t) | ||||
| } | ||||
|  | ||||
| func NewRFC3339Ptr(t *time.Time) *RFC3339Time { | ||||
| 	if t == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return langext.Ptr(RFC3339Time(*t)) | ||||
| } | ||||
|  | ||||
| func NowRFC3339() RFC3339Time { | ||||
| 	return RFC3339Time(time.Now()) | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsoncodec" | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsonrw" | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsontype" | ||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/langext" | ||||
| 	"reflect" | ||||
| 	"time" | ||||
| ) | ||||
| @@ -245,6 +246,13 @@ func NewRFC3339Nano(t time.Time) RFC3339NanoTime { | ||||
| 	return RFC3339NanoTime(t) | ||||
| } | ||||
|  | ||||
| func NewRFC3339NanoPtr(t *time.Time) *RFC3339NanoTime { | ||||
| 	if t == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return langext.Ptr(RFC3339NanoTime(*t)) | ||||
| } | ||||
|  | ||||
| func NowRFC3339Nano() RFC3339NanoTime { | ||||
| 	return RFC3339NanoTime(time.Now()) | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsoncodec" | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsonrw" | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsontype" | ||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/langext" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| @@ -239,6 +240,13 @@ func NewUnix(t time.Time) UnixTime { | ||||
| 	return UnixTime(t) | ||||
| } | ||||
|  | ||||
| func NewUnixPtr(t *time.Time) *UnixTime { | ||||
| 	if t == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return langext.Ptr(UnixTime(*t)) | ||||
| } | ||||
|  | ||||
| func NowUnix() UnixTime { | ||||
| 	return UnixTime(time.Now()) | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsoncodec" | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsonrw" | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsontype" | ||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/langext" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| @@ -239,6 +240,13 @@ func NewUnixMilli(t time.Time) UnixMilliTime { | ||||
| 	return UnixMilliTime(t) | ||||
| } | ||||
|  | ||||
| func NewUnixMilliPtr(t *time.Time) *UnixMilliTime { | ||||
| 	if t == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return langext.Ptr(UnixMilliTime(*t)) | ||||
| } | ||||
|  | ||||
| func NowUnixMilli() UnixMilliTime { | ||||
| 	return UnixMilliTime(time.Now()) | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ import ( | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsoncodec" | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsonrw" | ||||
| 	"go.mongodb.org/mongo-driver/bson/bsontype" | ||||
| 	"gogs.mikescher.com/BlackForestBytes/goext/langext" | ||||
| 	"reflect" | ||||
| 	"strconv" | ||||
| 	"time" | ||||
| @@ -239,6 +240,13 @@ func NewUnixNano(t time.Time) UnixNanoTime { | ||||
| 	return UnixNanoTime(t) | ||||
| } | ||||
|  | ||||
| func NewUnixNanoPtr(t *time.Time) *UnixNanoTime { | ||||
| 	if t == nil { | ||||
| 		return nil | ||||
| 	} | ||||
| 	return langext.Ptr(UnixNanoTime(*t)) | ||||
| } | ||||
|  | ||||
| func NowUnixNano() UnixNanoTime { | ||||
| 	return UnixNanoTime(time.Now()) | ||||
| } | ||||
|   | ||||
| @@ -125,3 +125,17 @@ func (c *Coll[TData]) createToken(fieldPrimary string, dirPrimary ct.SortDirecti | ||||
| 		Extra:          ct.Extra{}, | ||||
| 	}, nil | ||||
| } | ||||
|  | ||||
| func (c *Coll[TData]) needsDoubleSort(ctx context.Context) bool { | ||||
| 	for _, ppl := range c.extraModPipeline { | ||||
| 		for _, stage := range ppl(ctx) { | ||||
| 			for _, bsone := range stage { | ||||
| 				if bsone.Key == "$group" { | ||||
| 					// a group stage in extraModPipeline results in unsorted data, which means the caller must sort again after these pipeline stages... | ||||
| 					return true | ||||
| 				} | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
| 	return false | ||||
| } | ||||
|   | ||||
| @@ -36,6 +36,14 @@ func (c *Coll[TData]) Find(ctx context.Context, filter bson.M, opts ...*options. | ||||
| 		pipeline = langext.ArrConcat(pipeline, ppl(ctx)) | ||||
| 	} | ||||
|  | ||||
| 	if c.needsDoubleSort(ctx) { | ||||
| 		for _, opt := range opts { | ||||
| 			if opt != nil && opt.Sort != nil { | ||||
| 				pipeline = append(pipeline, bson.D{{Key: "$sort", Value: opt.Sort}}) | ||||
| 			} | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	for _, opt := range opts { | ||||
| 		if opt != nil && opt.Projection != nil { | ||||
| 			pipeline = append(pipeline, bson.D{{Key: "$project", Value: opt.Projection}}) | ||||
|   | ||||
| @@ -35,7 +35,7 @@ func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int, | ||||
| 		sortDirSecondary = nil | ||||
| 	} | ||||
|  | ||||
| 	paginationPipeline, err := createPaginationPipeline(c, inTok, sortPrimary, sortDirPrimary, sortSecondary, sortDirSecondary, pageSize) | ||||
| 	paginationPipeline, doubleSortPipeline, err := createPaginationPipeline(c, inTok, sortPrimary, sortDirPrimary, sortSecondary, sortDirSecondary, pageSize) | ||||
| 	if err != nil { | ||||
| 		return nil, ct.CursorToken{}, exerr. | ||||
| 			Wrap(err, "failed to create pagination"). | ||||
| @@ -56,6 +56,10 @@ func (c *Coll[TData]) List(ctx context.Context, filter ct.Filter, pageSize *int, | ||||
| 		pipeline = langext.ArrConcat(pipeline, ppl(ctx)) | ||||
| 	} | ||||
|  | ||||
| 	if c.needsDoubleSort(ctx) { | ||||
| 		pipeline = langext.ArrConcat(pipeline, doubleSortPipeline) | ||||
| 	} | ||||
|  | ||||
| 	cursor, err := c.coll.Aggregate(ctx, pipeline) | ||||
| 	if err != nil { | ||||
| 		return nil, ct.CursorToken{}, exerr.Wrap(err, "mongo-aggregation failed").Any("pipeline", pipeline).Str("collection", c.Name()).Build() | ||||
| @@ -136,14 +140,50 @@ func (c *Coll[TData]) ListWithCount(ctx context.Context, filter ct.Filter, pageS | ||||
| 	return data, token, count, nil | ||||
| } | ||||
|  | ||||
| func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CursorToken, fieldPrimary string, sortPrimary ct.SortDirection, fieldSecondary *string, sortSecondary *ct.SortDirection, pageSize *int) ([]bson.D, error) { | ||||
| func (c *Coll[TData]) ListAllIDs(ctx context.Context, filter ct.RawFilter) ([]string, error) { | ||||
| 	type idObject struct { | ||||
| 		ID string `bson:"_id"` | ||||
| 	} | ||||
|  | ||||
| 	pipelineFilter := mongo.Pipeline{} | ||||
|  | ||||
| 	if filter != nil { | ||||
| 		pipelineFilter = filter.FilterQuery() | ||||
| 	} | ||||
|  | ||||
| 	extrModPipelineResolved := mongo.Pipeline{} | ||||
| 	for _, ppl := range c.extraModPipeline { | ||||
| 		extrModPipelineResolved = langext.ArrConcat(extrModPipelineResolved, ppl(ctx)) | ||||
| 	} | ||||
|  | ||||
| 	pipelineProjectIDs := mongo.Pipeline{} | ||||
| 	pipelineProjectIDs = append(pipelineProjectIDs, bson.D{{Key: "$project", Value: bson.M{"_id": 1}}}) | ||||
|  | ||||
| 	pipelineList := langext.ArrConcat(pipelineFilter, extrModPipelineResolved, pipelineProjectIDs) | ||||
|  | ||||
| 	cursorList, err := c.coll.Aggregate(ctx, pipelineList) | ||||
| 	if err != nil { | ||||
| 		return nil, exerr.Wrap(err, "mongo-aggregation failed").Any("pipeline", pipelineList).Str("collection", c.Name()).Build() | ||||
| 	} | ||||
|  | ||||
| 	var res []idObject | ||||
|  | ||||
| 	err = cursorList.All(ctx, &res) | ||||
| 	if err != nil { | ||||
| 		return nil, exerr.Wrap(err, "failed to decode entities").Any("pipeline", pipelineList).Str("collection", c.Name()).Build() | ||||
| 	} | ||||
|  | ||||
| 	return langext.ArrMap(res, func(v idObject) string { return v.ID }), nil | ||||
| } | ||||
|  | ||||
| func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CursorToken, fieldPrimary string, sortPrimary ct.SortDirection, fieldSecondary *string, sortSecondary *ct.SortDirection, pageSize *int) ([]bson.D, []bson.D, error) { | ||||
|  | ||||
| 	cond := bson.A{} | ||||
| 	sort := bson.D{} | ||||
|  | ||||
| 	valuePrimary, err := coll.getTokenValueAsMongoType(token.ValuePrimary, fieldPrimary) | ||||
| 	if err != nil { | ||||
| 		return nil, exerr.Wrap(err, "failed to get (primary) token-value as mongo-type").Build() | ||||
| 		return nil, nil, exerr.Wrap(err, "failed to get (primary) token-value as mongo-type").Build() | ||||
| 	} | ||||
|  | ||||
| 	if sortPrimary == ct.SortASC { | ||||
| @@ -160,7 +200,7 @@ func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CursorToken | ||||
|  | ||||
| 		valueSecondary, err := coll.getTokenValueAsMongoType(token.ValueSecondary, *fieldSecondary) | ||||
| 		if err != nil { | ||||
| 			return nil, exerr.Wrap(err, "failed to get (secondary) token-value as mongo-type").Build() | ||||
| 			return nil, nil, exerr.Wrap(err, "failed to get (secondary) token-value as mongo-type").Build() | ||||
| 		} | ||||
|  | ||||
| 		if *sortSecondary == ct.SortASC { | ||||
| @@ -203,15 +243,17 @@ func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CursorToken | ||||
|  | ||||
| 	} else { | ||||
|  | ||||
| 		return nil, exerr.New(exerr.TypeInternal, "unknown ct mode: "+string(token.Mode)).Any("token.Mode", token.Mode).Build() | ||||
| 		return nil, nil, exerr.New(exerr.TypeInternal, "unknown ct mode: "+string(token.Mode)).Any("token.Mode", token.Mode).Build() | ||||
|  | ||||
| 	} | ||||
|  | ||||
| 	pipeline = append(pipeline, bson.D{{Key: "$sort", Value: sort}}) | ||||
|  | ||||
| 	pipelineSort := mongo.Pipeline{bson.D{{Key: "$sort", Value: sort}}} | ||||
|  | ||||
| 	if pageSize != nil { | ||||
| 		pipeline = append(pipeline, bson.D{{Key: "$limit", Value: int64(*pageSize + 1)}}) | ||||
| 	} | ||||
|  | ||||
| 	return pipeline, nil | ||||
| 	return pipeline, pipelineSort, nil | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user