Compare commits
28 Commits
Author | SHA1 | Date | |
---|---|---|---|
61801ff20d
|
|||
361dca5c85
|
|||
9f85a243e8
|
|||
dc6cb274ee
|
|||
f6b47792a4
|
|||
295b3ef793
|
|||
721c176337
|
|||
ebba6545a3
|
|||
19c7e22ced
|
|||
9f883b458f
|
|||
1f456c5134
|
|||
d7fbef37db
|
|||
a1668b6e5a
|
|||
3a17edfaf0
|
|||
3320a9c19d
|
|||
8dcd8a270a
|
|||
03a9b276d8
|
|||
9c8cde384f
|
|||
99b000ecf4
|
|||
a173e30090
|
|||
a3481a7d2d
|
|||
a8e6f98a89
|
|||
ab805403b9
|
|||
1e98d351ce
|
|||
c40bdc8e9e
|
|||
7204562879
|
|||
741611a2e1
|
|||
133aeb8374
|
@@ -20,6 +20,7 @@ Potentially needs `export GOPRIVATE="gogs.mikescher.com"`
|
|||||||
| zipext | Mike | Utility for zip/gzip/tar etc |
|
| zipext | Mike | Utility for zip/gzip/tar etc |
|
||||||
| reflectext | Mike | Utility for golang reflection |
|
| reflectext | Mike | Utility for golang reflection |
|
||||||
| fsext | Mike | Utility for filesytem access |
|
| fsext | Mike | Utility for filesytem access |
|
||||||
|
| ctxext | Mike | Utility for context.Context |
|
||||||
| | | |
|
| | | |
|
||||||
| mongoext | Mike | Utility/Helper functions for mongodb (kinda abandoned) |
|
| mongoext | Mike | Utility/Helper functions for mongodb (kinda abandoned) |
|
||||||
| cursortoken | Mike | MongoDB cursortoken implementation |
|
| cursortoken | Mike | MongoDB cursortoken implementation |
|
||||||
|
@@ -46,7 +46,7 @@ var rexEnumPackage = rext.W(regexp.MustCompile(`^package\s+(?P<name>[A-Za-z0-9_]
|
|||||||
|
|
||||||
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 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 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_:\s\-.]+"|[0-9]+))\s*(//(?P<comm>.*))?.*$`))
|
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_:\s\-.]*"|[0-9]+))\s*(//(?P<comm>.*))?.*$`))
|
||||||
|
|
||||||
var rexEnumChecksumConst = rext.W(regexp.MustCompile(`const ChecksumEnumGenerator = "(?P<cs>[A-Za-z0-9_]*)"`))
|
var rexEnumChecksumConst = rext.W(regexp.MustCompile(`const ChecksumEnumGenerator = "(?P<cs>[A-Za-z0-9_]*)"`))
|
||||||
|
|
||||||
|
27
ctxext/getter.go
Normal file
27
ctxext/getter.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package ctxext
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
func Value[T any](ctx context.Context, key any) (T, bool) {
|
||||||
|
v := ctx.Value(key)
|
||||||
|
if v == nil {
|
||||||
|
return *new(T), false
|
||||||
|
}
|
||||||
|
if tv, ok := v.(T); !ok {
|
||||||
|
return *new(T), false
|
||||||
|
} else {
|
||||||
|
return tv, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ValueOrDefault[T any](ctx context.Context, key any, def T) T {
|
||||||
|
v := ctx.Value(key)
|
||||||
|
if v == nil {
|
||||||
|
return def
|
||||||
|
}
|
||||||
|
if tv, ok := v.(T); !ok {
|
||||||
|
return def
|
||||||
|
} else {
|
||||||
|
return tv
|
||||||
|
}
|
||||||
|
}
|
@@ -57,3 +57,11 @@ func (m JsonOpt[T]) MustValue() T {
|
|||||||
}
|
}
|
||||||
return m.value
|
return m.value
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m JsonOpt[T]) IfSet(fn func(v T)) bool {
|
||||||
|
if !m.isSet {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
fn(m.value)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
@@ -19,6 +19,14 @@ func (s Single[T1]) TupleValues() []any {
|
|||||||
return []any{s.V1}
|
return []any{s.V1}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewSingle[T1 any](v1 T1) Single[T1] {
|
||||||
|
return Single[T1]{V1: v1}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTuple1[T1 any](v1 T1) Single[T1] {
|
||||||
|
return Single[T1]{V1: v1}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
type Tuple[T1 any, T2 any] struct {
|
type Tuple[T1 any, T2 any] struct {
|
||||||
@@ -34,6 +42,14 @@ func (t Tuple[T1, T2]) TupleValues() []any {
|
|||||||
return []any{t.V1, t.V2}
|
return []any{t.V1, t.V2}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewTuple[T1 any, T2 any](v1 T1, v2 T2) Tuple[T1, T2] {
|
||||||
|
return Tuple[T1, T2]{V1: v1, V2: v2}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTuple2[T1 any, T2 any](v1 T1, v2 T2) Tuple[T1, T2] {
|
||||||
|
return Tuple[T1, T2]{V1: v1, V2: v2}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
type Triple[T1 any, T2 any, T3 any] struct {
|
type Triple[T1 any, T2 any, T3 any] struct {
|
||||||
@@ -50,6 +66,14 @@ func (t Triple[T1, T2, T3]) TupleValues() []any {
|
|||||||
return []any{t.V1, t.V2, t.V3}
|
return []any{t.V1, t.V2, t.V3}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewTriple[T1 any, T2 any, T3 any](v1 T1, v2 T2, v3 T3) Triple[T1, T2, T3] {
|
||||||
|
return Triple[T1, T2, T3]{V1: v1, V2: v2, V3: v3}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTuple3[T1 any, T2 any, T3 any](v1 T1, v2 T2, v3 T3) Triple[T1, T2, T3] {
|
||||||
|
return Triple[T1, T2, T3]{V1: v1, V2: v2, V3: v3}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
type Quadruple[T1 any, T2 any, T3 any, T4 any] struct {
|
type Quadruple[T1 any, T2 any, T3 any, T4 any] struct {
|
||||||
@@ -67,6 +91,14 @@ func (t Quadruple[T1, T2, T3, T4]) TupleValues() []any {
|
|||||||
return []any{t.V1, t.V2, t.V3, t.V4}
|
return []any{t.V1, t.V2, t.V3, t.V4}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewQuadruple[T1 any, T2 any, T3 any, T4 any](v1 T1, v2 T2, v3 T3, v4 T4) Quadruple[T1, T2, T3, T4] {
|
||||||
|
return Quadruple[T1, T2, T3, T4]{V1: v1, V2: v2, V3: v3, V4: v4}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTuple4[T1 any, T2 any, T3 any, T4 any](v1 T1, v2 T2, v3 T3, v4 T4) Quadruple[T1, T2, T3, T4] {
|
||||||
|
return Quadruple[T1, T2, T3, T4]{V1: v1, V2: v2, V3: v3, V4: v4}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
type Quintuple[T1 any, T2 any, T3 any, T4 any, T5 any] struct {
|
type Quintuple[T1 any, T2 any, T3 any, T4 any, T5 any] struct {
|
||||||
@@ -86,6 +118,14 @@ func (t Quintuple[T1, T2, T3, T4, T5]) TupleValues() []any {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewQuintuple[T1 any, T2 any, T3 any, T4 any, T5 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5) Quintuple[T1, T2, T3, T4, T5] {
|
||||||
|
return Quintuple[T1, T2, T3, T4, T5]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTuple5[T1 any, T2 any, T3 any, T4 any, T5 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5) Quintuple[T1, T2, T3, T4, T5] {
|
||||||
|
return Quintuple[T1, T2, T3, T4, T5]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
type Sextuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any] struct {
|
type Sextuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any] struct {
|
||||||
@@ -106,6 +146,14 @@ func (t Sextuple[T1, T2, T3, T4, T5, T6]) TupleValues() []any {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewSextuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6) Sextuple[T1, T2, T3, T4, T5, T6] {
|
||||||
|
return Sextuple[T1, T2, T3, T4, T5, T6]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTuple6[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6) Sextuple[T1, T2, T3, T4, T5, T6] {
|
||||||
|
return Sextuple[T1, T2, T3, T4, T5, T6]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
type Septuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any] struct {
|
type Septuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any] struct {
|
||||||
@@ -126,6 +174,14 @@ func (t Septuple[T1, T2, T3, T4, T5, T6, T7]) TupleValues() []any {
|
|||||||
return []any{t.V1, t.V2, t.V3, t.V4, t.V5, t.V6, t.V7}
|
return []any{t.V1, t.V2, t.V3, t.V4, t.V5, t.V6, t.V7}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewSeptuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6, v7 T7) Septuple[T1, T2, T3, T4, T5, T6, T7] {
|
||||||
|
return Septuple[T1, T2, T3, T4, T5, T6, T7]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6, V7: v7}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTuple7[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6, v7 T7) Septuple[T1, T2, T3, T4, T5, T6, T7] {
|
||||||
|
return Septuple[T1, T2, T3, T4, T5, T6, T7]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6, V7: v7}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
type Octuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any] struct {
|
type Octuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any] struct {
|
||||||
@@ -147,6 +203,14 @@ func (t Octuple[T1, T2, T3, T4, T5, T6, T7, T8]) TupleValues() []any {
|
|||||||
return []any{t.V1, t.V2, t.V3, t.V4, t.V5, t.V6, t.V7, t.V8}
|
return []any{t.V1, t.V2, t.V3, t.V4, t.V5, t.V6, t.V7, t.V8}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewOctuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6, v7 T7, v8 T8) Octuple[T1, T2, T3, T4, T5, T6, T7, T8] {
|
||||||
|
return Octuple[T1, T2, T3, T4, T5, T6, T7, T8]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6, V7: v7, V8: v8}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTuple8[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6, v7 T7, v8 T8) Octuple[T1, T2, T3, T4, T5, T6, T7, T8] {
|
||||||
|
return Octuple[T1, T2, T3, T4, T5, T6, T7, T8]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6, V7: v7, V8: v8}
|
||||||
|
}
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
// ----------------------------------------------------------------------------
|
||||||
|
|
||||||
type Nonuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any] struct {
|
type Nonuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any] struct {
|
||||||
@@ -168,3 +232,10 @@ func (t Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]) TupleLength() int {
|
|||||||
func (t Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]) TupleValues() []any {
|
func (t Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]) TupleValues() []any {
|
||||||
return []any{t.V1, t.V2, t.V3, t.V4, t.V5, t.V6, t.V7, t.V8, t.V9}
|
return []any{t.V1, t.V2, t.V3, t.V4, t.V5, t.V6, t.V7, t.V8, t.V9}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewNonuple[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6, v7 T7, v8 T8, v9 T9) Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9] {
|
||||||
|
return Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6, V7: v7, V8: v8, V9: v9}
|
||||||
|
}
|
||||||
|
func NewTuple9[T1 any, T2 any, T3 any, T4 any, T5 any, T6 any, T7 any, T8 any, T9 any](v1 T1, v2 T2, v3 T3, v4 T4, v5 T5, v6 T6, v7 T7, v8 T8, v9 T9) Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9] {
|
||||||
|
return Nonuple[T1, T2, T3, T4, T5, T6, T7, T8, T9]{V1: v1, V2: v2, V3: v3, V4: v4, V5: v5, V6: v6, V7: v7, V8: v8, V9: v9}
|
||||||
|
}
|
||||||
|
@@ -12,6 +12,8 @@ import (
|
|||||||
var reflectTypeStr = reflect.TypeOf("")
|
var reflectTypeStr = reflect.TypeOf("")
|
||||||
|
|
||||||
func FromError(err error) *ExErr {
|
func FromError(err error) *ExErr {
|
||||||
|
|
||||||
|
//goland:noinspection GoTypeAssertionOnErrors
|
||||||
if verr, ok := err.(*ExErr); ok {
|
if verr, ok := err.(*ExErr); ok {
|
||||||
// A simple ExErr
|
// A simple ExErr
|
||||||
return verr
|
return verr
|
||||||
|
@@ -38,6 +38,13 @@ func (ee *ExErr) Error() string {
|
|||||||
// Unwrap must be implemented so that some error.XXX methods work
|
// Unwrap must be implemented so that some error.XXX methods work
|
||||||
func (ee *ExErr) Unwrap() error {
|
func (ee *ExErr) Unwrap() error {
|
||||||
if ee.OriginalError == nil {
|
if ee.OriginalError == nil {
|
||||||
|
|
||||||
|
if ee.WrappedErr != nil {
|
||||||
|
if werr, ok := ee.WrappedErr.(error); ok {
|
||||||
|
return werr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return nil // this is neccessary - otherwise we return a wrapped nil and the `x == nil` comparison fails (= panic in errors.Is and other failures)
|
return nil // this is neccessary - otherwise we return a wrapped nil and the `x == nil` comparison fails (= panic in errors.Is and other failures)
|
||||||
}
|
}
|
||||||
return ee.OriginalError
|
return ee.OriginalError
|
||||||
|
@@ -86,3 +86,28 @@ func MessageMatch(e error, matcher func(string) bool) bool {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// OriginalError returns the lowest level error, probably the original/external error that was originally wrapped
|
||||||
|
func OriginalError(e error) error {
|
||||||
|
if e == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
//goland:noinspection GoTypeAssertionOnErrors
|
||||||
|
bmerr, ok := e.(*ExErr)
|
||||||
|
for !ok {
|
||||||
|
return e
|
||||||
|
}
|
||||||
|
|
||||||
|
for bmerr.OriginalError != nil {
|
||||||
|
bmerr = bmerr.OriginalError
|
||||||
|
}
|
||||||
|
|
||||||
|
if bmerr.WrappedErr != nil {
|
||||||
|
if werr, ok := bmerr.WrappedErr.(error); ok {
|
||||||
|
return werr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bmerr
|
||||||
|
}
|
||||||
|
@@ -7,10 +7,11 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type jsonHTTPResponse struct {
|
type jsonHTTPResponse struct {
|
||||||
statusCode int
|
statusCode int
|
||||||
data any
|
data any
|
||||||
headers []headerval
|
headers []headerval
|
||||||
cookies []cookieval
|
cookies []cookieval
|
||||||
|
filterOverride *string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (j jsonHTTPResponse) jsonRenderer(g *gin.Context) json.GoJsonRender {
|
func (j jsonHTTPResponse) jsonRenderer(g *gin.Context) json.GoJsonRender {
|
||||||
@@ -18,6 +19,9 @@ func (j jsonHTTPResponse) jsonRenderer(g *gin.Context) json.GoJsonRender {
|
|||||||
if jsonfilter := g.GetString(jsonFilterKey); jsonfilter != "" {
|
if jsonfilter := g.GetString(jsonFilterKey); jsonfilter != "" {
|
||||||
f = &jsonfilter
|
f = &jsonfilter
|
||||||
}
|
}
|
||||||
|
if j.filterOverride != nil {
|
||||||
|
f = j.filterOverride
|
||||||
|
}
|
||||||
return json.GoJsonRender{Data: j.data, NilSafeSlices: true, NilSafeMaps: true, Filter: f}
|
return json.GoJsonRender{Data: j.data, NilSafeSlices: true, NilSafeMaps: true, Filter: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,3 +72,7 @@ func (j jsonHTTPResponse) Headers() []string {
|
|||||||
func JSON(sc int, data any) HTTPResponse {
|
func JSON(sc int, data any) HTTPResponse {
|
||||||
return &jsonHTTPResponse{statusCode: sc, data: data}
|
return &jsonHTTPResponse{statusCode: sc, data: data}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func JSONWithFilter(sc int, data any, f string) HTTPResponse {
|
||||||
|
return &jsonHTTPResponse{statusCode: sc, data: data, filterOverride: &f}
|
||||||
|
}
|
||||||
|
26
go.mod
26
go.mod
@@ -6,12 +6,12 @@ require (
|
|||||||
github.com/gin-gonic/gin v1.10.0
|
github.com/gin-gonic/gin v1.10.0
|
||||||
github.com/glebarez/go-sqlite v1.22.0 // only needed for tests -.-
|
github.com/glebarez/go-sqlite v1.22.0 // only needed for tests -.-
|
||||||
github.com/jmoiron/sqlx v1.4.0
|
github.com/jmoiron/sqlx v1.4.0
|
||||||
github.com/rs/xid v1.5.0
|
github.com/rs/xid v1.6.0
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
go.mongodb.org/mongo-driver v1.16.0
|
go.mongodb.org/mongo-driver v1.17.1
|
||||||
golang.org/x/crypto v0.26.0
|
golang.org/x/crypto v0.28.0
|
||||||
golang.org/x/sys v0.23.0
|
golang.org/x/sys v0.26.0
|
||||||
golang.org/x/term v0.23.0
|
golang.org/x/term v0.25.0
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -21,7 +21,7 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/bytedance/sonic v1.12.1 // indirect
|
github.com/bytedance/sonic v1.12.3 // indirect
|
||||||
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
github.com/bytedance/sonic/loader v0.2.0 // indirect
|
||||||
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
github.com/chenzhuoyu/base64x v0.0.0-20230717121745-296ad89f973d // indirect
|
||||||
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
github.com/chenzhuoyu/iasm v0.9.1 // indirect
|
||||||
@@ -32,12 +32,12 @@ require (
|
|||||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||||
github.com/go-playground/locales v0.14.1 // indirect
|
github.com/go-playground/locales v0.14.1 // indirect
|
||||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||||
github.com/go-playground/validator/v10 v10.22.0 // indirect
|
github.com/go-playground/validator/v10 v10.22.1 // indirect
|
||||||
github.com/goccy/go-json v0.10.3 // indirect
|
github.com/goccy/go-json v0.10.3 // indirect
|
||||||
github.com/golang/snappy v0.0.4 // indirect
|
github.com/golang/snappy v0.0.4 // indirect
|
||||||
github.com/google/uuid v1.5.0 // indirect
|
github.com/google/uuid v1.5.0 // indirect
|
||||||
github.com/json-iterator/go v1.1.12 // indirect
|
github.com/json-iterator/go v1.1.12 // indirect
|
||||||
github.com/klauspost/compress v1.17.9 // indirect
|
github.com/klauspost/compress v1.17.10 // indirect
|
||||||
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
github.com/klauspost/cpuid/v2 v2.2.8 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
@@ -45,7 +45,7 @@ require (
|
|||||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||||
github.com/ugorji/go/codec v1.2.12 // indirect
|
github.com/ugorji/go/codec v1.2.12 // indirect
|
||||||
@@ -53,10 +53,10 @@ require (
|
|||||||
github.com/xdg-go/scram v1.1.2 // indirect
|
github.com/xdg-go/scram v1.1.2 // indirect
|
||||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||||
golang.org/x/arch v0.9.0 // indirect
|
golang.org/x/arch v0.11.0 // indirect
|
||||||
golang.org/x/image v0.19.0 // indirect
|
golang.org/x/image v0.21.0 // indirect
|
||||||
golang.org/x/net v0.28.0 // indirect
|
golang.org/x/net v0.30.0 // indirect
|
||||||
golang.org/x/text v0.17.0 // indirect
|
golang.org/x/text v0.19.0 // indirect
|
||||||
google.golang.org/protobuf v1.34.2 // indirect
|
google.golang.org/protobuf v1.34.2 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
modernc.org/libc v1.37.6 // indirect
|
modernc.org/libc v1.37.6 // indirect
|
||||||
|
48
go.sum
48
go.sum
@@ -28,6 +28,10 @@ github.com/bytedance/sonic v1.12.0 h1:YGPgxF9xzaCNvd/ZKdQ28yRovhfMFZQjuk6fKBzZ3l
|
|||||||
github.com/bytedance/sonic v1.12.0/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
github.com/bytedance/sonic v1.12.0/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||||
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
|
github.com/bytedance/sonic v1.12.1 h1:jWl5Qz1fy7X1ioY74WqO0KjAMtAGQs4sYnjiEBiyX24=
|
||||||
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
github.com/bytedance/sonic v1.12.1/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||||
|
github.com/bytedance/sonic v1.12.2 h1:oaMFuRTpMHYLpCntGca65YWt5ny+wAceDERTkT2L9lg=
|
||||||
|
github.com/bytedance/sonic v1.12.2/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||||
|
github.com/bytedance/sonic v1.12.3 h1:W2MGa7RCU1QTeYRTPE3+88mVC0yXmsRQRChiyVocVjU=
|
||||||
|
github.com/bytedance/sonic v1.12.3/go.mod h1:B8Gt/XvtZ3Fqj+iSKMypzymZxw/FVwgIGKzMzT9r/rk=
|
||||||
github.com/bytedance/sonic/loader v0.1.0/go.mod h1:UmRT+IRTGKz/DAkzcEGzyVqQFJ7H9BqwBO3pm9H/+HY=
|
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 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=
|
||||||
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU=
|
||||||
@@ -93,6 +97,8 @@ github.com/go-playground/validator/v10 v10.21.0 h1:4fZA11ovvtkdgaeev9RGWPgc1uj3H
|
|||||||
github.com/go-playground/validator/v10 v10.21.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
github.com/go-playground/validator/v10 v10.21.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
github.com/go-playground/validator/v10 v10.22.0 h1:k6HsTZ0sTnROkhS//R0O+55JgM8C4Bx7ia+JlgcnOao=
|
||||||
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
github.com/go-playground/validator/v10 v10.22.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
|
||||||
|
github.com/go-playground/validator/v10 v10.22.1 h1:40JcKH+bBNGFczGuoBYgX4I6m/i27HYW8P9FDk5PbgA=
|
||||||
|
github.com/go-playground/validator/v10 v10.22.1/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 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
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 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
@@ -134,6 +140,8 @@ github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0N
|
|||||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
|
||||||
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||||
|
github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
|
||||||
|
github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
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 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||||
@@ -175,6 +183,8 @@ github.com/pelletier/go-toml/v2 v2.2.1 h1:9TA9+T8+8CUCO2+WYnDLCgrYi9+omqKXyjDtos
|
|||||||
github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
github.com/pelletier/go-toml/v2 v2.2.1/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||||
|
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||||
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
github.com/phpdave11/gofpdi v1.0.7/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI=
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
@@ -184,6 +194,8 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94
|
|||||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
|
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
|
||||||
|
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
|
||||||
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
github.com/rs/zerolog v1.31.0 h1:FcTR3NnLWW+NnTwwhFWiJSZr4ECLpqCm6QsEnyvbV4A=
|
||||||
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
github.com/rs/zerolog v1.31.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
|
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
|
||||||
@@ -234,6 +246,12 @@ go.mongodb.org/mongo-driver v1.15.1 h1:l+RvoUOoMXFmADTLfYDm7On9dRm7p4T80/lEQM+r7
|
|||||||
go.mongodb.org/mongo-driver v1.15.1/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
go.mongodb.org/mongo-driver v1.15.1/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
|
||||||
go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4=
|
go.mongodb.org/mongo-driver v1.16.0 h1:tpRsfBJMROVHKpdGyc1BBEzzjDUWjItxbVSZ8Ls4BQ4=
|
||||||
go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
|
go.mongodb.org/mongo-driver v1.16.0/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
|
||||||
|
go.mongodb.org/mongo-driver v1.16.1 h1:rIVLL3q0IHM39dvE+z2ulZLp9ENZKThVfuvN/IiN4l8=
|
||||||
|
go.mongodb.org/mongo-driver v1.16.1/go.mod h1:oB6AhJQvFQL4LEHyXi6aJzQJtBiTQHiAd83l0GdFaiw=
|
||||||
|
go.mongodb.org/mongo-driver v1.17.0 h1:Hp4q2MCjvY19ViwimTs00wHi7G4yzxh4/2+nTx8r40k=
|
||||||
|
go.mongodb.org/mongo-driver v1.17.0/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
|
||||||
|
go.mongodb.org/mongo-driver v1.17.1 h1:Wic5cJIwJgSpBhe3lx3+/RybR5PiYRMpVFgO7cOHyIM=
|
||||||
|
go.mongodb.org/mongo-driver v1.17.1/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
|
||||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
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 h1:pskyeJh/3AmoQ8CPE95vxHLqp1G1GfGNXTmcl9NEKTc=
|
||||||
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.7.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
@@ -241,6 +259,10 @@ golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc=
|
|||||||
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
|
golang.org/x/arch v0.9.0 h1:ub9TgUInamJ8mrZIGlBG6/4TqWeMszd4N8lNorbrr6k=
|
||||||
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
golang.org/x/arch v0.9.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
|
golang.org/x/arch v0.10.0 h1:S3huipmSclq3PJMNe76NGwkBR504WFkQ5dhzWzP8ZW8=
|
||||||
|
golang.org/x/arch v0.10.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
|
golang.org/x/arch v0.11.0 h1:KXV8WWKCXm6tRpLirl2szsO5j/oOODwZf4hATmGVNs4=
|
||||||
|
golang.org/x/arch v0.11.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys=
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||||
@@ -265,6 +287,10 @@ golang.org/x/crypto v0.25.0 h1:ypSNr+bnYL2YhwoMt2zPxHFmbAN1KZs/njMG3hxUp30=
|
|||||||
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
|
||||||
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
golang.org/x/crypto v0.26.0 h1:RrRspgV4mU+YwB4FYnuBoKsUapNIL5cohGAmSH3azsw=
|
||||||
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
golang.org/x/crypto v0.26.0/go.mod h1:GY7jblb9wI+FOo5y8/S2oY4zWP07AkOJ4+jxCqdqn54=
|
||||||
|
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||||
|
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||||
|
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||||
|
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||||
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20190910094157-69e4b8554b2a/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
|
||||||
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||||
@@ -276,6 +302,10 @@ golang.org/x/image v0.18.0 h1:jGzIakQa/ZXI1I0Fxvaa9W7yP25TqT6cHIHn+6CqvSQ=
|
|||||||
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
|
||||||
golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ=
|
golang.org/x/image v0.19.0 h1:D9FX4QWkLfkeqaC62SonffIIuYdOk/UE2XKUBgRIBIQ=
|
||||||
golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys=
|
golang.org/x/image v0.19.0/go.mod h1:y0zrRqlQRWQ5PXaYCOMLTW2fpsxZ8Qh9I/ohnInJEys=
|
||||||
|
golang.org/x/image v0.20.0 h1:7cVCUjQwfL18gyBJOmYvptfSHS8Fb3YUDtfLIZ7Nbpw=
|
||||||
|
golang.org/x/image v0.20.0/go.mod h1:0a88To4CYVBAHp5FXJm8o7QbUl37Vd85ply1vyD8auM=
|
||||||
|
golang.org/x/image v0.21.0 h1:c5qV36ajHpdj4Qi0GnE0jUc/yuo33OLFaa0d+crTD5s=
|
||||||
|
golang.org/x/image v0.21.0/go.mod h1:vUbsLavqK/W303ZroQQVKQ+Af3Yl6Uz1Ppu5J/cLz78=
|
||||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
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-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
@@ -300,6 +330,10 @@ golang.org/x/net v0.27.0 h1:5K3Njcw06/l2y9vpGCSdcxWOYHOUk3dVNGDXN+FvAys=
|
|||||||
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
golang.org/x/net v0.27.0/go.mod h1:dDi0PyhWNoiUOrAS8uXv/vnScO4wnHQO4mj9fn/RytE=
|
||||||
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE=
|
||||||
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg=
|
||||||
|
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||||
|
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||||
|
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||||
|
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/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 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||||
@@ -335,6 +369,12 @@ golang.org/x/sys v0.22.0 h1:RI27ohtqKCnwULzJLqkv897zojh5/DwS/ENaMzUOaWI=
|
|||||||
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
golang.org/x/sys v0.23.0 h1:YfKFowiIMvtgl1UERQoTPPToxltDeZfbj4H7dVUCwmM=
|
||||||
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.23.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.24.0 h1:Twjiwq9dn6R1fQcyiK+wQyHWfaz/BJB+YIpzU/Cv3Xg=
|
||||||
|
golang.org/x/sys v0.24.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||||
|
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||||
|
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
|
||||||
@@ -353,6 +393,10 @@ golang.org/x/term v0.22.0 h1:BbsgPEJULsl2fV/AT3v15Mjva5yXKQDyKf+TbDz7QJk=
|
|||||||
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
golang.org/x/term v0.22.0/go.mod h1:F3qCibpT5AMpCRfhfT53vVJwhLtIVHhB9XDjfFvnMI4=
|
||||||
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
golang.org/x/term v0.23.0 h1:F6D4vR+EHoL9/sWAWgAR1H2DcHr4PareCbAaCo1RpuU=
|
||||||
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
golang.org/x/term v0.23.0/go.mod h1:DgV24QBUrK6jhZXl+20l6UWznPlwAHm1Q1mGHtydmSk=
|
||||||
|
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||||
|
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||||
|
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
|
||||||
|
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
@@ -367,6 +411,10 @@ golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
|||||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||||
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
golang.org/x/text v0.17.0 h1:XtiM5bkSOt+ewxlOE/aE/AKEHibwj/6gvWMl9Rsh0Qc=
|
||||||
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||||
|
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
|
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
||||||
|
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
package goext
|
package goext
|
||||||
|
|
||||||
const GoextVersion = "0.0.494"
|
const GoextVersion = "0.0.522"
|
||||||
|
|
||||||
const GoextVersionTimestamp = "2024-08-07T13:57:29+0200"
|
const GoextVersionTimestamp = "2024-10-05T01:12:00+0200"
|
||||||
|
@@ -788,7 +788,7 @@ FieldLoop:
|
|||||||
|
|
||||||
if f.omitEmpty && isEmptyValue(fv) {
|
if f.omitEmpty && isEmptyValue(fv) {
|
||||||
continue
|
continue
|
||||||
} else if opts.filter != nil && !matchesJSONFilter(f.jsonfilter, *opts.filter) {
|
} else if !matchesJSONFilter(f.jsonfilter, opts.filter) {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
e.WriteByte(next)
|
e.WriteByte(next)
|
||||||
@@ -808,16 +808,20 @@ FieldLoop:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchesJSONFilter(filter jsonfilter, value string) bool {
|
func matchesJSONFilter(filter jsonfilter, value *string) bool {
|
||||||
if len(filter) == 0 {
|
if len(filter) == 0 {
|
||||||
return true
|
return true // no filter in struct
|
||||||
|
}
|
||||||
|
|
||||||
|
if value == nil || *value == "" {
|
||||||
|
return false // no filter set, but struct has filter, return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(filter) == 1 && filter[0] == "-" {
|
if len(filter) == 1 && filter[0] == "-" {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if filter.Contains(value) {
|
if filter.Contains(*value) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1353,7 +1357,7 @@ func typeFields(t reflect.Type, tagkey string) structFields {
|
|||||||
|
|
||||||
var jsonfilter []string
|
var jsonfilter []string
|
||||||
jsonfilterTag := sf.Tag.Get("jsonfilter")
|
jsonfilterTag := sf.Tag.Get("jsonfilter")
|
||||||
if jsonfilterTag != "" && jsonfilterTag != "-" {
|
if jsonfilterTag != "" {
|
||||||
jsonfilter = strings.Split(jsonfilterTag, ",")
|
jsonfilter = strings.Split(jsonfilterTag, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -214,7 +214,7 @@ func ObjectFitImage(img image.Image, bbw float64, bbh float64, fit ImageFit, fil
|
|||||||
draw.Draw(newImg, newImg.Bounds(), &image.Uniform{C: fillColor}, image.Pt(0, 0), draw.Src)
|
draw.Draw(newImg, newImg.Bounds(), &image.Uniform{C: fillColor}, image.Pt(0, 0), draw.Src)
|
||||||
draw.Draw(newImg, newImg.Bounds(), img, image.Pt(0, 0), draw.Over)
|
draw.Draw(newImg, newImg.Bounds(), img, image.Pt(0, 0), draw.Over)
|
||||||
|
|
||||||
return newImg, newImg.Bounds(), nil
|
return newImg, PercentageRectangle{0, 0, 1, 1}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if fit == ImageFitContainCenter || fit == ImageFitContainTopLeft || fit == ImageFitContainTopRight || fit == ImageFitContainBottomLeft || fit == ImageFitContainBottomRight {
|
if fit == ImageFitContainCenter || fit == ImageFitContainTopLeft || fit == ImageFitContainTopRight || fit == ImageFitContainBottomLeft || fit == ImageFitContainBottomRight {
|
||||||
@@ -266,7 +266,7 @@ func ObjectFitImage(img image.Image, bbw float64, bbh float64, fit ImageFit, fil
|
|||||||
draw.Draw(newImg, newImg.Bounds(), &image.Uniform{C: fillColor}, image.Pt(0, 0), draw.Src)
|
draw.Draw(newImg, newImg.Bounds(), &image.Uniform{C: fillColor}, image.Pt(0, 0), draw.Src)
|
||||||
draw.Draw(newImg, destBounds, img, image.Pt(0, 0), draw.Over)
|
draw.Draw(newImg, destBounds, img, image.Pt(0, 0), draw.Over)
|
||||||
|
|
||||||
return newImg, destBounds, nil
|
return newImg, calcRelativeRect(destBounds, newImg.Bounds()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if fit == ImageFitStretch {
|
if fit == ImageFitStretch {
|
||||||
@@ -293,10 +293,10 @@ func ObjectFitImage(img image.Image, bbw float64, bbh float64, fit ImageFit, fil
|
|||||||
draw.Draw(newImg, newImg.Bounds(), &image.Uniform{C: fillColor}, image.Pt(0, 0), draw.Src)
|
draw.Draw(newImg, newImg.Bounds(), &image.Uniform{C: fillColor}, image.Pt(0, 0), draw.Src)
|
||||||
draw.Draw(newImg, newImg.Bounds(), img, image.Pt(0, 0), draw.Over)
|
draw.Draw(newImg, newImg.Bounds(), img, image.Pt(0, 0), draw.Over)
|
||||||
|
|
||||||
return newImg, newImg.Bounds(), nil
|
return newImg, PercentageRectangle{0, 0, 1, 1}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, image.Rectangle{}, exerr.New(exerr.TypeInternal, fmt.Sprintf("unknown image-fit: '%s'", fit)).Build()
|
return nil, PercentageRectangle{}, exerr.New(exerr.TypeInternal, fmt.Sprintf("unknown image-fit: '%s'", fit)).Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
func VerifyAndDecodeImage(data io.Reader, mime string) (image.Image, error) {
|
func VerifyAndDecodeImage(data io.Reader, mime string) (image.Image, error) {
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
package imageext
|
package imageext
|
||||||
|
|
||||||
|
import "image"
|
||||||
|
|
||||||
type Rectangle struct {
|
type Rectangle struct {
|
||||||
X float64
|
X float64
|
||||||
Y float64
|
Y float64
|
||||||
@@ -22,3 +24,12 @@ func (r PercentageRectangle) Of(ref Rectangle) Rectangle {
|
|||||||
H: r.H * ref.H,
|
H: r.H * ref.H,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func calcRelativeRect(inner image.Rectangle, outer image.Rectangle) PercentageRectangle {
|
||||||
|
return PercentageRectangle{
|
||||||
|
X: float64(inner.Min.X-outer.Min.X) / float64(outer.Dx()),
|
||||||
|
Y: float64(inner.Min.Y-outer.Min.Y) / float64(outer.Dy()),
|
||||||
|
W: float64(inner.Dx()) / float64(outer.Dx()),
|
||||||
|
H: float64(inner.Dy()) / float64(outer.Dy()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -323,6 +323,16 @@ func ArrMap[T1 any, T2 any](arr []T1, conv func(v T1) T2) []T2 {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ArrDeRef[T1 any](arr []*T1) []T1 {
|
||||||
|
r := make([]T1, 0, len(arr))
|
||||||
|
for _, v := range arr {
|
||||||
|
if v != nil {
|
||||||
|
r = append(r, *v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
func MapMap[TK comparable, TV any, TR any](inmap map[TK]TV, conv func(k TK, v TV) TR) []TR {
|
func MapMap[TK comparable, TV any, TR any](inmap map[TK]TV, conv func(k TK, v TV) TR) []TR {
|
||||||
r := make([]TR, 0, len(inmap))
|
r := make([]TR, 0, len(inmap))
|
||||||
for k, v := range inmap {
|
for k, v := range inmap {
|
||||||
|
15
langext/io.go
Normal file
15
langext/io.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package langext
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
|
type nopCloser struct {
|
||||||
|
io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n nopCloser) Close() error {
|
||||||
|
return nil // no op
|
||||||
|
}
|
||||||
|
|
||||||
|
func WriteNopCloser(w io.Writer) io.WriteCloser {
|
||||||
|
return nopCloser{w}
|
||||||
|
}
|
@@ -7,6 +7,7 @@ import (
|
|||||||
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
|
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DB interface {
|
type DB interface {
|
||||||
@@ -57,89 +58,121 @@ func (db *database) AddListener(listener Listener) {
|
|||||||
|
|
||||||
func (db *database) Exec(ctx context.Context, sqlstr string, prep PP) (sql.Result, error) {
|
func (db *database) Exec(ctx context.Context, sqlstr string, prep PP) (sql.Result, error) {
|
||||||
origsql := sqlstr
|
origsql := sqlstr
|
||||||
|
|
||||||
|
t0 := time.Now()
|
||||||
|
|
||||||
|
preMeta := PreExecMeta{Context: ctx, TransactionConstructorContext: nil}
|
||||||
for _, v := range db.lstr {
|
for _, v := range db.lstr {
|
||||||
err := v.PreExec(ctx, nil, &sqlstr, &prep)
|
err := v.PreExec(ctx, nil, &sqlstr, &prep, preMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, exerr.Wrap(err, "failed to call SQL pre-exec listener").Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
return nil, exerr.Wrap(err, "failed to call SQL pre-exec listener").Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t1 := time.Now()
|
||||||
|
|
||||||
res, err := db.db.NamedExecContext(ctx, sqlstr, prep)
|
res, err := db.db.NamedExecContext(ctx, sqlstr, prep)
|
||||||
|
|
||||||
|
postMeta := PostExecMeta{Context: ctx, TransactionConstructorContext: nil, Init: t0, Start: t1, End: time.Now()}
|
||||||
for _, v := range db.lstr {
|
for _, v := range db.lstr {
|
||||||
v.PostExec(nil, origsql, sqlstr, prep)
|
v.PostExec(nil, origsql, sqlstr, prep, err, postMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, exerr.Wrap(err, "Failed to [exec] sql statement").Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
return nil, exerr.Wrap(err, "Failed to [exec] sql statement").Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *database) Query(ctx context.Context, sqlstr string, prep PP) (*sqlx.Rows, error) {
|
func (db *database) Query(ctx context.Context, sqlstr string, prep PP) (*sqlx.Rows, error) {
|
||||||
origsql := sqlstr
|
origsql := sqlstr
|
||||||
|
|
||||||
|
t0 := time.Now()
|
||||||
|
|
||||||
|
preMeta := PreQueryMeta{Context: ctx, TransactionConstructorContext: nil}
|
||||||
for _, v := range db.lstr {
|
for _, v := range db.lstr {
|
||||||
err := v.PreQuery(ctx, nil, &sqlstr, &prep)
|
err := v.PreQuery(ctx, nil, &sqlstr, &prep, preMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, exerr.Wrap(err, "failed to call SQL pre-query listener").Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
return nil, exerr.Wrap(err, "failed to call SQL pre-query listener").Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t1 := time.Now()
|
||||||
|
|
||||||
rows, err := sqlx.NamedQueryContext(ctx, db.db, sqlstr, prep)
|
rows, err := sqlx.NamedQueryContext(ctx, db.db, sqlstr, prep)
|
||||||
|
|
||||||
|
postMeta := PostQueryMeta{Context: ctx, TransactionConstructorContext: nil, Init: t0, Start: t1, End: time.Now()}
|
||||||
for _, v := range db.lstr {
|
for _, v := range db.lstr {
|
||||||
v.PostQuery(nil, origsql, sqlstr, prep)
|
v.PostQuery(nil, origsql, sqlstr, prep, err, postMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, exerr.Wrap(err, "Failed to [query] sql statement").Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
return nil, exerr.Wrap(err, "Failed to [query] sql statement").Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows, nil
|
return rows, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *database) Ping(ctx context.Context) error {
|
func (db *database) Ping(ctx context.Context) error {
|
||||||
|
|
||||||
|
t0 := time.Now()
|
||||||
|
|
||||||
|
preMeta := PrePingMeta{Context: ctx}
|
||||||
for _, v := range db.lstr {
|
for _, v := range db.lstr {
|
||||||
err := v.PrePing(ctx)
|
err := v.PrePing(ctx, preMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t1 := time.Now()
|
||||||
|
|
||||||
err := db.db.PingContext(ctx)
|
err := db.db.PingContext(ctx)
|
||||||
|
|
||||||
|
postMeta := PostPingMeta{Context: ctx, Init: t0, Start: t1, End: time.Now()}
|
||||||
for _, v := range db.lstr {
|
for _, v := range db.lstr {
|
||||||
v.PostPing(err)
|
v.PostPing(err, postMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return exerr.Wrap(err, "Failed to [ping] sql database").Build()
|
return exerr.Wrap(err, "Failed to [ping] sql database").Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *database) BeginTransaction(ctx context.Context, iso sql.IsolationLevel) (Tx, error) {
|
func (db *database) BeginTransaction(ctx context.Context, iso sql.IsolationLevel) (Tx, error) {
|
||||||
|
|
||||||
|
t0 := time.Now()
|
||||||
|
|
||||||
db.lock.Lock()
|
db.lock.Lock()
|
||||||
txid := db.txctr
|
txid := db.txctr
|
||||||
db.txctr += 1 // with overflow !
|
db.txctr += 1 // with overflow !
|
||||||
db.lock.Unlock()
|
db.lock.Unlock()
|
||||||
|
|
||||||
|
preMeta := PreTxBeginMeta{Context: ctx}
|
||||||
for _, v := range db.lstr {
|
for _, v := range db.lstr {
|
||||||
err := v.PreTxBegin(ctx, txid)
|
err := v.PreTxBegin(ctx, txid, preMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t1 := time.Now()
|
||||||
|
|
||||||
xtx, err := db.db.BeginTxx(ctx, &sql.TxOptions{Isolation: iso})
|
xtx, err := db.db.BeginTxx(ctx, &sql.TxOptions{Isolation: iso})
|
||||||
|
|
||||||
|
postMeta := PostTxBeginMeta{Context: ctx, Init: t0, Start: t1, End: time.Now()}
|
||||||
|
for _, v := range db.lstr {
|
||||||
|
v.PostTxBegin(txid, err, postMeta)
|
||||||
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, exerr.Wrap(err, "Failed to start sql transaction").Build()
|
return nil, exerr.Wrap(err, "Failed to start sql transaction").Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, v := range db.lstr {
|
return newTransaction(ctx, xtx, txid, db), nil
|
||||||
v.PostTxBegin(txid, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return NewTransaction(xtx, txid, db), nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (db *database) Exit() error {
|
func (db *database) Exit() error {
|
||||||
|
214
sq/listener.go
214
sq/listener.go
@@ -1,188 +1,266 @@
|
|||||||
package sq
|
package sq
|
||||||
|
|
||||||
import "context"
|
import (
|
||||||
|
"context"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type PrePingMeta struct {
|
||||||
|
Context context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
type PreTxBeginMeta struct {
|
||||||
|
Context context.Context
|
||||||
|
ConstructorContext context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
type PreTxCommitMeta struct {
|
||||||
|
ConstructorContext context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
type PreTxRollbackMeta struct {
|
||||||
|
ConstructorContext context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
type PreQueryMeta struct {
|
||||||
|
Context context.Context
|
||||||
|
TransactionConstructorContext context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
type PreExecMeta struct {
|
||||||
|
Context context.Context
|
||||||
|
TransactionConstructorContext context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostPingMeta struct {
|
||||||
|
Context context.Context
|
||||||
|
Init time.Time
|
||||||
|
Start time.Time
|
||||||
|
End time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostTxBeginMeta struct {
|
||||||
|
Context context.Context
|
||||||
|
Init time.Time
|
||||||
|
Start time.Time
|
||||||
|
End time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostTxCommitMeta struct {
|
||||||
|
ConstructorContext context.Context
|
||||||
|
Init time.Time
|
||||||
|
Start time.Time
|
||||||
|
End time.Time
|
||||||
|
ExecCounter int
|
||||||
|
QueryCounter int
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostTxRollbackMeta struct {
|
||||||
|
ConstructorContext context.Context
|
||||||
|
Init time.Time
|
||||||
|
Start time.Time
|
||||||
|
End time.Time
|
||||||
|
ExecCounter int
|
||||||
|
QueryCounter int
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostQueryMeta struct {
|
||||||
|
Context context.Context
|
||||||
|
TransactionConstructorContext context.Context
|
||||||
|
Init time.Time
|
||||||
|
Start time.Time
|
||||||
|
End time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
type PostExecMeta struct {
|
||||||
|
Context context.Context
|
||||||
|
TransactionConstructorContext context.Context
|
||||||
|
Init time.Time
|
||||||
|
Start time.Time
|
||||||
|
End time.Time
|
||||||
|
}
|
||||||
|
|
||||||
type Listener interface {
|
type Listener interface {
|
||||||
PrePing(ctx context.Context) error
|
PrePing(ctx context.Context, meta PrePingMeta) error
|
||||||
PreTxBegin(ctx context.Context, txid uint16) error
|
PreTxBegin(ctx context.Context, txid uint16, meta PreTxBeginMeta) error
|
||||||
PreTxCommit(txid uint16) error
|
PreTxCommit(txid uint16, meta PreTxCommitMeta) error
|
||||||
PreTxRollback(txid uint16) error
|
PreTxRollback(txid uint16, meta PreTxRollbackMeta) error
|
||||||
PreQuery(ctx context.Context, txID *uint16, sql *string, params *PP) error
|
PreQuery(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreQueryMeta) error
|
||||||
PreExec(ctx context.Context, txID *uint16, sql *string, params *PP) error
|
PreExec(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreExecMeta) error
|
||||||
|
|
||||||
PostPing(result error)
|
PostPing(result error, meta PostPingMeta)
|
||||||
PostTxBegin(txid uint16, result error)
|
PostTxBegin(txid uint16, result error, meta PostTxBeginMeta)
|
||||||
PostTxCommit(txid uint16, result error)
|
PostTxCommit(txid uint16, result error, meta PostTxCommitMeta)
|
||||||
PostTxRollback(txid uint16, result error)
|
PostTxRollback(txid uint16, result error, meta PostTxRollbackMeta)
|
||||||
PostQuery(txID *uint16, sqlOriginal string, sqlReal string, params PP)
|
PostQuery(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostQueryMeta)
|
||||||
PostExec(txID *uint16, sqlOriginal string, sqlReal string, params PP)
|
PostExec(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostExecMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
type genListener struct {
|
type genListener struct {
|
||||||
prePing func(ctx context.Context) error
|
prePing func(ctx context.Context, meta PrePingMeta) error
|
||||||
preTxBegin func(ctx context.Context, txid uint16) error
|
preTxBegin func(ctx context.Context, txid uint16, meta PreTxBeginMeta) error
|
||||||
preTxCommit func(txid uint16) error
|
preTxCommit func(txid uint16, meta PreTxCommitMeta) error
|
||||||
preTxRollback func(txid uint16) error
|
preTxRollback func(txid uint16, meta PreTxRollbackMeta) error
|
||||||
preQuery func(ctx context.Context, txID *uint16, sql *string, params *PP) error
|
preQuery func(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreQueryMeta) error
|
||||||
preExec func(ctx context.Context, txID *uint16, sql *string, params *PP) error
|
preExec func(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreExecMeta) error
|
||||||
postPing func(result error)
|
postPing func(result error, meta PostPingMeta)
|
||||||
postTxBegin func(txid uint16, result error)
|
postTxBegin func(txid uint16, result error, meta PostTxBeginMeta)
|
||||||
postTxCommit func(txid uint16, result error)
|
postTxCommit func(txid uint16, result error, meta PostTxCommitMeta)
|
||||||
postTxRollback func(txid uint16, result error)
|
postTxRollback func(txid uint16, result error, meta PostTxRollbackMeta)
|
||||||
postQuery func(txID *uint16, sqlOriginal string, sqlReal string, params PP)
|
postQuery func(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostQueryMeta)
|
||||||
postExec func(txID *uint16, sqlOriginal string, sqlReal string, params PP)
|
postExec func(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostExecMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g genListener) PrePing(ctx context.Context) error {
|
func (g genListener) PrePing(ctx context.Context, meta PrePingMeta) error {
|
||||||
if g.prePing != nil {
|
if g.prePing != nil {
|
||||||
return g.prePing(ctx)
|
return g.prePing(ctx, meta)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g genListener) PreTxBegin(ctx context.Context, txid uint16) error {
|
func (g genListener) PreTxBegin(ctx context.Context, txid uint16, meta PreTxBeginMeta) error {
|
||||||
if g.preTxBegin != nil {
|
if g.preTxBegin != nil {
|
||||||
return g.preTxBegin(ctx, txid)
|
return g.preTxBegin(ctx, txid, meta)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g genListener) PreTxCommit(txid uint16) error {
|
func (g genListener) PreTxCommit(txid uint16, meta PreTxCommitMeta) error {
|
||||||
if g.preTxCommit != nil {
|
if g.preTxCommit != nil {
|
||||||
return g.preTxCommit(txid)
|
return g.preTxCommit(txid, meta)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g genListener) PreTxRollback(txid uint16) error {
|
func (g genListener) PreTxRollback(txid uint16, meta PreTxRollbackMeta) error {
|
||||||
if g.preTxRollback != nil {
|
if g.preTxRollback != nil {
|
||||||
return g.preTxRollback(txid)
|
return g.preTxRollback(txid, meta)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g genListener) PreQuery(ctx context.Context, txID *uint16, sql *string, params *PP) error {
|
func (g genListener) PreQuery(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreQueryMeta) error {
|
||||||
if g.preQuery != nil {
|
if g.preQuery != nil {
|
||||||
return g.preQuery(ctx, txID, sql, params)
|
return g.preQuery(ctx, txID, sql, params, meta)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g genListener) PreExec(ctx context.Context, txID *uint16, sql *string, params *PP) error {
|
func (g genListener) PreExec(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreExecMeta) error {
|
||||||
if g.preExec != nil {
|
if g.preExec != nil {
|
||||||
return g.preExec(ctx, txID, sql, params)
|
return g.preExec(ctx, txID, sql, params, meta)
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g genListener) PostPing(result error) {
|
func (g genListener) PostPing(result error, meta PostPingMeta) {
|
||||||
if g.postPing != nil {
|
if g.postPing != nil {
|
||||||
g.postPing(result)
|
g.postPing(result, meta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g genListener) PostTxBegin(txid uint16, result error) {
|
func (g genListener) PostTxBegin(txid uint16, result error, meta PostTxBeginMeta) {
|
||||||
if g.postTxBegin != nil {
|
if g.postTxBegin != nil {
|
||||||
g.postTxBegin(txid, result)
|
g.postTxBegin(txid, result, meta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g genListener) PostTxCommit(txid uint16, result error) {
|
func (g genListener) PostTxCommit(txid uint16, result error, meta PostTxCommitMeta) {
|
||||||
if g.postTxCommit != nil {
|
if g.postTxCommit != nil {
|
||||||
g.postTxCommit(txid, result)
|
g.postTxCommit(txid, result, meta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g genListener) PostTxRollback(txid uint16, result error) {
|
func (g genListener) PostTxRollback(txid uint16, result error, meta PostTxRollbackMeta) {
|
||||||
if g.postTxRollback != nil {
|
if g.postTxRollback != nil {
|
||||||
g.postTxRollback(txid, result)
|
g.postTxRollback(txid, result, meta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g genListener) PostQuery(txID *uint16, sqlOriginal string, sqlReal string, params PP) {
|
func (g genListener) PostQuery(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostQueryMeta) {
|
||||||
if g.postQuery != nil {
|
if g.postQuery != nil {
|
||||||
g.postQuery(txID, sqlOriginal, sqlReal, params)
|
g.postQuery(txID, sqlOriginal, sqlReal, params, result, meta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g genListener) PostExec(txID *uint16, sqlOriginal string, sqlReal string, params PP) {
|
func (g genListener) PostExec(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostExecMeta) {
|
||||||
if g.postExec != nil {
|
if g.postExec != nil {
|
||||||
g.postExec(txID, sqlOriginal, sqlReal, params)
|
g.postExec(txID, sqlOriginal, sqlReal, params, result, meta)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPrePingListener(f func(ctx context.Context) error) Listener {
|
func NewPrePingListener(f func(ctx context.Context, meta PrePingMeta) error) Listener {
|
||||||
return genListener{prePing: f}
|
return genListener{prePing: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPreTxBeginListener(f func(ctx context.Context, txid uint16) error) Listener {
|
func NewPreTxBeginListener(f func(ctx context.Context, txid uint16, meta PreTxBeginMeta) error) Listener {
|
||||||
return genListener{preTxBegin: f}
|
return genListener{preTxBegin: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPreTxCommitListener(f func(txid uint16) error) Listener {
|
func NewPreTxCommitListener(f func(txid uint16, meta PreTxCommitMeta) error) Listener {
|
||||||
return genListener{preTxCommit: f}
|
return genListener{preTxCommit: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPreTxRollbackListener(f func(txid uint16) error) Listener {
|
func NewPreTxRollbackListener(f func(txid uint16, meta PreTxRollbackMeta) error) Listener {
|
||||||
return genListener{preTxRollback: f}
|
return genListener{preTxRollback: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPreQueryListener(f func(ctx context.Context, txID *uint16, sql *string, params *PP) error) Listener {
|
func NewPreQueryListener(f func(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreQueryMeta) error) Listener {
|
||||||
return genListener{preQuery: f}
|
return genListener{preQuery: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPreExecListener(f func(ctx context.Context, txID *uint16, sql *string, params *PP) error) Listener {
|
func NewPreExecListener(f func(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreExecMeta) error) Listener {
|
||||||
return genListener{preExec: f}
|
return genListener{preExec: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPreListener(f func(ctx context.Context, cmdtype string, txID *uint16, sql *string, params *PP) error) Listener {
|
func NewPreListener(f func(ctx context.Context, cmdtype string, txID *uint16, sql *string, params *PP) error) Listener {
|
||||||
return genListener{
|
return genListener{
|
||||||
preExec: func(ctx context.Context, txID *uint16, sql *string, params *PP) error {
|
preExec: func(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreExecMeta) error {
|
||||||
return f(ctx, "EXEC", txID, sql, params)
|
return f(ctx, "EXEC", txID, sql, params)
|
||||||
},
|
},
|
||||||
preQuery: func(ctx context.Context, txID *uint16, sql *string, params *PP) error {
|
preQuery: func(ctx context.Context, txID *uint16, sql *string, params *PP, meta PreQueryMeta) error {
|
||||||
return f(ctx, "QUERY", txID, sql, params)
|
return f(ctx, "QUERY", txID, sql, params)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPostPingListener(f func(result error)) Listener {
|
func NewPostPingListener(f func(result error, meta PostPingMeta)) Listener {
|
||||||
return genListener{postPing: f}
|
return genListener{postPing: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPostTxBeginListener(f func(txid uint16, result error)) Listener {
|
func NewPostTxBeginListener(f func(txid uint16, result error, meta PostTxBeginMeta)) Listener {
|
||||||
return genListener{postTxBegin: f}
|
return genListener{postTxBegin: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPostTxCommitListener(f func(txid uint16, result error)) Listener {
|
func NewPostTxCommitListener(f func(txid uint16, result error, meta PostTxCommitMeta)) Listener {
|
||||||
return genListener{postTxCommit: f}
|
return genListener{postTxCommit: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPostTxRollbackListener(f func(txid uint16, result error)) Listener {
|
func NewPostTxRollbackListener(f func(txid uint16, result error, meta PostTxRollbackMeta)) Listener {
|
||||||
return genListener{postTxRollback: f}
|
return genListener{postTxRollback: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPostQueryListener(f func(txID *uint16, sqlOriginal string, sqlReal string, params PP)) Listener {
|
func NewPostQueryListener(f func(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostQueryMeta)) Listener {
|
||||||
return genListener{postQuery: f}
|
return genListener{postQuery: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPostExecListener(f func(txID *uint16, sqlOriginal string, sqlReal string, params PP)) Listener {
|
func NewPostExecListener(f func(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostExecMeta)) Listener {
|
||||||
return genListener{postExec: f}
|
return genListener{postExec: f}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPostListener(f func(cmdtype string, txID *uint16, sqlOriginal string, sqlReal string, params PP)) Listener {
|
func NewPostListener(f func(cmdtype string, txID *uint16, sqlOriginal string, sqlReal string, result error, params PP)) Listener {
|
||||||
return genListener{
|
return genListener{
|
||||||
postExec: func(txID *uint16, sqlOriginal string, sqlReal string, params PP) {
|
postExec: func(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostExecMeta) {
|
||||||
f("EXEC", txID, sqlOriginal, sqlReal, params)
|
f("EXEC", txID, sqlOriginal, sqlReal, result, params)
|
||||||
},
|
},
|
||||||
postQuery: func(txID *uint16, sqlOriginal string, sqlReal string, params PP) {
|
postQuery: func(txID *uint16, sqlOriginal string, sqlReal string, params PP, result error, meta PostQueryMeta) {
|
||||||
f("QUERY", txID, sqlOriginal, sqlReal, params)
|
f("QUERY", txID, sqlOriginal, sqlReal, result, params)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -6,6 +6,7 @@ import (
|
|||||||
"github.com/jmoiron/sqlx"
|
"github.com/jmoiron/sqlx"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
|
"gogs.mikescher.com/BlackForestBytes/goext/exerr"
|
||||||
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type TxStatus string
|
type TxStatus string
|
||||||
@@ -26,62 +27,78 @@ type Tx interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type transaction struct {
|
type transaction struct {
|
||||||
tx *sqlx.Tx
|
constructorContext context.Context
|
||||||
id uint16
|
tx *sqlx.Tx
|
||||||
status TxStatus
|
id uint16
|
||||||
execCtr int
|
status TxStatus
|
||||||
queryCtr int
|
execCtr int
|
||||||
db *database
|
queryCtr int
|
||||||
|
db *database
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewTransaction(xtx *sqlx.Tx, txid uint16, db *database) Tx {
|
func newTransaction(ctx context.Context, xtx *sqlx.Tx, txid uint16, db *database) Tx {
|
||||||
return &transaction{
|
return &transaction{
|
||||||
tx: xtx,
|
constructorContext: ctx,
|
||||||
id: txid,
|
tx: xtx,
|
||||||
status: TxStatusInitial,
|
id: txid,
|
||||||
execCtr: 0,
|
status: TxStatusInitial,
|
||||||
queryCtr: 0,
|
execCtr: 0,
|
||||||
db: db,
|
queryCtr: 0,
|
||||||
|
db: db,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *transaction) Rollback() error {
|
func (tx *transaction) Rollback() error {
|
||||||
|
|
||||||
|
t0 := time.Now()
|
||||||
|
|
||||||
|
preMeta := PreTxRollbackMeta{ConstructorContext: tx.constructorContext}
|
||||||
for _, v := range tx.db.lstr {
|
for _, v := range tx.db.lstr {
|
||||||
err := v.PreTxRollback(tx.id)
|
err := v.PreTxRollback(tx.id, preMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return exerr.Wrap(err, "failed to call SQL pre-rollback listener").Int("tx.id", int(tx.id)).Build()
|
return exerr.Wrap(err, "failed to call SQL pre-rollback listener").Int("tx.id", int(tx.id)).Build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t1 := time.Now()
|
||||||
|
|
||||||
result := tx.tx.Rollback()
|
result := tx.tx.Rollback()
|
||||||
|
|
||||||
if result == nil {
|
if result == nil {
|
||||||
tx.status = TxStatusRollback
|
tx.status = TxStatusRollback
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postMeta := PostTxRollbackMeta{ConstructorContext: tx.constructorContext, Init: t0, Start: t1, End: time.Now(), ExecCounter: tx.execCtr, QueryCounter: tx.queryCtr}
|
||||||
for _, v := range tx.db.lstr {
|
for _, v := range tx.db.lstr {
|
||||||
v.PostTxRollback(tx.id, result)
|
v.PostTxRollback(tx.id, result, postMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
func (tx *transaction) Commit() error {
|
func (tx *transaction) Commit() error {
|
||||||
|
|
||||||
|
t0 := time.Now()
|
||||||
|
|
||||||
|
preMeta := PreTxCommitMeta{ConstructorContext: tx.constructorContext}
|
||||||
for _, v := range tx.db.lstr {
|
for _, v := range tx.db.lstr {
|
||||||
err := v.PreTxCommit(tx.id)
|
err := v.PreTxCommit(tx.id, preMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return exerr.Wrap(err, "failed to call SQL pre-commit listener").Int("tx.id", int(tx.id)).Build()
|
return exerr.Wrap(err, "failed to call SQL pre-commit listener").Int("tx.id", int(tx.id)).Build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t1 := time.Now()
|
||||||
|
|
||||||
result := tx.tx.Commit()
|
result := tx.tx.Commit()
|
||||||
|
|
||||||
if result == nil {
|
if result == nil {
|
||||||
tx.status = TxStatusComitted
|
tx.status = TxStatusComitted
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postMeta := PostTxCommitMeta{ConstructorContext: tx.constructorContext, Init: t0, Start: t1, End: time.Now(), ExecCounter: tx.execCtr, QueryCounter: tx.queryCtr}
|
||||||
for _, v := range tx.db.lstr {
|
for _, v := range tx.db.lstr {
|
||||||
v.PostTxRollback(tx.id, result)
|
v.PostTxCommit(tx.id, result, postMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
@@ -89,21 +106,29 @@ func (tx *transaction) Commit() error {
|
|||||||
|
|
||||||
func (tx *transaction) Exec(ctx context.Context, sqlstr string, prep PP) (sql.Result, error) {
|
func (tx *transaction) Exec(ctx context.Context, sqlstr string, prep PP) (sql.Result, error) {
|
||||||
origsql := sqlstr
|
origsql := sqlstr
|
||||||
|
|
||||||
|
t0 := time.Now()
|
||||||
|
|
||||||
|
preMeta := PreExecMeta{Context: ctx, TransactionConstructorContext: tx.constructorContext}
|
||||||
for _, v := range tx.db.lstr {
|
for _, v := range tx.db.lstr {
|
||||||
err := v.PreExec(ctx, langext.Ptr(tx.id), &sqlstr, &prep)
|
err := v.PreExec(ctx, langext.Ptr(tx.id), &sqlstr, &prep, preMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, exerr.Wrap(err, "failed to call SQL pre-exec listener").Int("tx.id", int(tx.id)).Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
return nil, exerr.Wrap(err, "failed to call SQL pre-exec listener").Int("tx.id", int(tx.id)).Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t1 := time.Now()
|
||||||
|
|
||||||
res, err := tx.tx.NamedExecContext(ctx, sqlstr, prep)
|
res, err := tx.tx.NamedExecContext(ctx, sqlstr, prep)
|
||||||
|
tx.execCtr++
|
||||||
|
|
||||||
if tx.status == TxStatusInitial && err == nil {
|
if tx.status == TxStatusInitial && err == nil {
|
||||||
tx.status = TxStatusActive
|
tx.status = TxStatusActive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postMeta := PostExecMeta{Context: ctx, TransactionConstructorContext: tx.constructorContext, Init: t0, Start: t1, End: time.Now()}
|
||||||
for _, v := range tx.db.lstr {
|
for _, v := range tx.db.lstr {
|
||||||
v.PostExec(langext.Ptr(tx.id), origsql, sqlstr, prep)
|
v.PostExec(langext.Ptr(tx.id), origsql, sqlstr, prep, err, postMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -114,21 +139,29 @@ func (tx *transaction) Exec(ctx context.Context, sqlstr string, prep PP) (sql.Re
|
|||||||
|
|
||||||
func (tx *transaction) Query(ctx context.Context, sqlstr string, prep PP) (*sqlx.Rows, error) {
|
func (tx *transaction) Query(ctx context.Context, sqlstr string, prep PP) (*sqlx.Rows, error) {
|
||||||
origsql := sqlstr
|
origsql := sqlstr
|
||||||
|
|
||||||
|
t0 := time.Now()
|
||||||
|
|
||||||
|
preMeta := PreQueryMeta{Context: ctx, TransactionConstructorContext: tx.constructorContext}
|
||||||
for _, v := range tx.db.lstr {
|
for _, v := range tx.db.lstr {
|
||||||
err := v.PreQuery(ctx, langext.Ptr(tx.id), &sqlstr, &prep)
|
err := v.PreQuery(ctx, langext.Ptr(tx.id), &sqlstr, &prep, preMeta)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, exerr.Wrap(err, "failed to call SQL pre-query listener").Int("tx.id", int(tx.id)).Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
return nil, exerr.Wrap(err, "failed to call SQL pre-query listener").Int("tx.id", int(tx.id)).Str("original_sql", origsql).Str("sql", sqlstr).Any("sql_params", prep).Build()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
t1 := time.Now()
|
||||||
|
|
||||||
rows, err := sqlx.NamedQueryContext(ctx, tx.tx, sqlstr, prep)
|
rows, err := sqlx.NamedQueryContext(ctx, tx.tx, sqlstr, prep)
|
||||||
|
tx.queryCtr++
|
||||||
|
|
||||||
if tx.status == TxStatusInitial && err == nil {
|
if tx.status == TxStatusInitial && err == nil {
|
||||||
tx.status = TxStatusActive
|
tx.status = TxStatusActive
|
||||||
}
|
}
|
||||||
|
|
||||||
|
postMeta := PostQueryMeta{Context: ctx, TransactionConstructorContext: tx.constructorContext, Init: t0, Start: t1, End: time.Now()}
|
||||||
for _, v := range tx.db.lstr {
|
for _, v := range tx.db.lstr {
|
||||||
v.PostQuery(langext.Ptr(tx.id), origsql, sqlstr, prep)
|
v.PostQuery(langext.Ptr(tx.id), origsql, sqlstr, prep, err, postMeta)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -2,6 +2,9 @@ package timeext
|
|||||||
|
|
||||||
import "time"
|
import "time"
|
||||||
|
|
||||||
|
// YearDifference calculates the difference between two timestamps in years.
|
||||||
|
// = t1 - t2
|
||||||
|
// returns a float value
|
||||||
func YearDifference(t1 time.Time, t2 time.Time, tz *time.Location) float64 {
|
func YearDifference(t1 time.Time, t2 time.Time, tz *time.Location) float64 {
|
||||||
|
|
||||||
yDelta := float64(t1.Year() - t2.Year())
|
yDelta := float64(t1.Year() - t2.Year())
|
||||||
@@ -11,3 +14,31 @@ func YearDifference(t1 time.Time, t2 time.Time, tz *time.Location) float64 {
|
|||||||
|
|
||||||
return yDelta + (processT1 - processT2)
|
return yDelta + (processT1 - processT2)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// MonthDifference calculates the difference between two timestamps in months.
|
||||||
|
// = t1 - t2
|
||||||
|
// returns a float value
|
||||||
|
func MonthDifference(t1 time.Time, t2 time.Time) float64 {
|
||||||
|
|
||||||
|
yDelta := float64(t1.Year() - t2.Year())
|
||||||
|
mDelta := float64(t1.Month() - t2.Month())
|
||||||
|
|
||||||
|
dDelta := float64(0)
|
||||||
|
|
||||||
|
t1MonthDays := DaysInMonth(t1)
|
||||||
|
t2MonthDays := DaysInMonth(t2)
|
||||||
|
|
||||||
|
if t2.Year() > t1.Year() || (t2.Year() == t1.Year() && t2.Month() > t1.Month()) {
|
||||||
|
dDelta -= 1
|
||||||
|
dDelta += float64(t1MonthDays-t1.Day()) / float64(t1MonthDays)
|
||||||
|
dDelta += float64(t2.Day()) / float64(t2MonthDays)
|
||||||
|
} else if t2.Year() < t1.Year() || (t2.Year() == t1.Year() && t2.Month() < t1.Month()) {
|
||||||
|
dDelta -= 1
|
||||||
|
dDelta += float64(t1.Day()) / float64(t1MonthDays)
|
||||||
|
dDelta += float64(t2MonthDays-t2.Day()) / float64(t2MonthDays)
|
||||||
|
} else {
|
||||||
|
dDelta += float64(t1.Day()-t2.Day()) / float64(t1MonthDays)
|
||||||
|
}
|
||||||
|
|
||||||
|
return yDelta*12 + mDelta + dDelta
|
||||||
|
}
|
||||||
|
@@ -81,3 +81,63 @@ func epsilonEquals(a, b float64) bool {
|
|||||||
epsilon := 0.01
|
epsilon := 0.01
|
||||||
return math.Abs(a-b) < epsilon
|
return math.Abs(a-b) < epsilon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestMonthDifferenceSameDate(t *testing.T) {
|
||||||
|
t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
t2 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
expected := 0.0
|
||||||
|
result := MonthDifference(t2, t1)
|
||||||
|
if !epsilonEquals(result, expected) {
|
||||||
|
t.Errorf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonthDifferenceSameMonth(t *testing.T) {
|
||||||
|
t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
t2 := time.Date(2022, 1, 31, 0, 0, 0, 0, time.UTC)
|
||||||
|
expected := 0.967741935483871 // Approximation of 30/31 days
|
||||||
|
result := MonthDifference(t2, t1)
|
||||||
|
if !epsilonEquals(result, expected) {
|
||||||
|
t.Errorf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonthDifferenceDifferentMonthsSameYear(t *testing.T) {
|
||||||
|
t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
t2 := time.Date(2022, 3, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
expected := 2.0
|
||||||
|
result := MonthDifference(t2, t1)
|
||||||
|
if !epsilonEquals(result, expected) {
|
||||||
|
t.Errorf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonthDifferenceDifferentYears(t *testing.T) {
|
||||||
|
t1 := time.Date(2021, 12, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
t2 := time.Date(2022, 2, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
expected := 2.0
|
||||||
|
result := MonthDifference(t2, t1)
|
||||||
|
if !epsilonEquals(result, expected) {
|
||||||
|
t.Errorf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonthDifferenceT1BeforeT2(t *testing.T) {
|
||||||
|
t1 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
t2 := time.Date(2022, 6, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
expected := 5.0
|
||||||
|
result := MonthDifference(t2, t1)
|
||||||
|
if !epsilonEquals(result, expected) {
|
||||||
|
t.Errorf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestMonthDifferenceT1AfterT2(t *testing.T) {
|
||||||
|
t1 := time.Date(2022, 6, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
t2 := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
expected := -5.0
|
||||||
|
result := MonthDifference(t2, t1)
|
||||||
|
if !epsilonEquals(result, expected) {
|
||||||
|
t.Errorf("Expected %v, got %v", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -184,3 +184,10 @@ func AddYears(t time.Time, yearCount float64, tz *time.Location) time.Time {
|
|||||||
|
|
||||||
return t.Add(time.Duration(float64(t1.Sub(t0)) * floatCount))
|
return t.Add(time.Duration(float64(t1.Sub(t0)) * floatCount))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DaysInMonth(t time.Time) int {
|
||||||
|
// https://stackoverflow.com/a/73882035/1761622
|
||||||
|
|
||||||
|
y, m, _ := t.Date()
|
||||||
|
return time.Date(y, m+1, 0, 0, 0, 0, 0, time.UTC).Day()
|
||||||
|
}
|
||||||
|
@@ -191,3 +191,39 @@ func TestCombineDateAndTime_CombineDifferentParts(t *testing.T) {
|
|||||||
t.Errorf("Expected %v, got %v", expected, result)
|
t.Errorf("Expected %v, got %v", expected, result)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestDaysInMonth_31Days(t *testing.T) {
|
||||||
|
date := time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC) // January
|
||||||
|
expected := 31
|
||||||
|
result := DaysInMonth(date)
|
||||||
|
if result != expected {
|
||||||
|
t.Errorf("Expected %d but got %d", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDaysInMonth_30Days(t *testing.T) {
|
||||||
|
date := time.Date(2022, 4, 1, 0, 0, 0, 0, time.UTC) // April
|
||||||
|
expected := 30
|
||||||
|
result := DaysInMonth(date)
|
||||||
|
if result != expected {
|
||||||
|
t.Errorf("Expected %d but got %d", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDaysInMonth_FebruaryLeapYear(t *testing.T) {
|
||||||
|
date := time.Date(2020, 2, 1, 0, 0, 0, 0, time.UTC) // February in a leap year
|
||||||
|
expected := 29
|
||||||
|
result := DaysInMonth(date)
|
||||||
|
if result != expected {
|
||||||
|
t.Errorf("Expected %d but got %d", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestDaysInMonth_FebruaryNonLeapYear(t *testing.T) {
|
||||||
|
date := time.Date(2021, 2, 1, 0, 0, 0, 0, time.UTC) // February in a non-leap year
|
||||||
|
expected := 28
|
||||||
|
result := DaysInMonth(date)
|
||||||
|
if result != expected {
|
||||||
|
t.Errorf("Expected %d but got %d", expected, result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -12,7 +12,11 @@ import (
|
|||||||
func (c *Coll[TData]) FindOne(ctx context.Context, filter bson.M) (TData, error) {
|
func (c *Coll[TData]) FindOne(ctx context.Context, filter bson.M) (TData, error) {
|
||||||
r, err := c.findOneInternal(ctx, filter, false)
|
r, err := c.findOneInternal(ctx, filter, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return *new(TData), exerr.Wrap(err, "mongo-query[find-one] failed").Str("collection", c.Name()).Build()
|
if filterId, ok := filter["_id"]; ok {
|
||||||
|
return *new(TData), exerr.Wrap(err, "mongo-query[find-one] failed").Str("collection", c.Name()).Any("filter", filter).Any("filter_id", filterId).Build()
|
||||||
|
} else {
|
||||||
|
return *new(TData), exerr.Wrap(err, "mongo-query[find-one] failed").Str("collection", c.Name()).Any("filter", filter).Build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return *r, nil
|
return *r, nil
|
||||||
@@ -21,7 +25,7 @@ func (c *Coll[TData]) FindOne(ctx context.Context, filter bson.M) (TData, error)
|
|||||||
func (c *Coll[TData]) FindOneOpt(ctx context.Context, filter bson.M) (*TData, error) {
|
func (c *Coll[TData]) FindOneOpt(ctx context.Context, filter bson.M) (*TData, error) {
|
||||||
r, err := c.findOneInternal(ctx, filter, true)
|
r, err := c.findOneInternal(ctx, filter, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, exerr.Wrap(err, "mongo-query[find-one-opt] failed").Str("collection", c.Name()).Build()
|
return nil, exerr.Wrap(err, "mongo-query[find-one-opt] failed").Str("collection", c.Name()).Any("filter", filter).Build()
|
||||||
}
|
}
|
||||||
|
|
||||||
return r, nil
|
return r, nil
|
||||||
@@ -58,7 +62,11 @@ func (c *Coll[TData]) findOneInternal(ctx context.Context, filter bson.M, allowN
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, exerr.Wrap(err, "mongo-query[find-one] failed").Any("filter", filter).Str("collection", c.Name()).NoLog().Build()
|
if filterId, ok := filter["_id"]; ok {
|
||||||
|
return nil, exerr.Wrap(err, "mongo-query[find-one|internal] failed").Str("collection", c.Name()).Any("filter", filter).Any("filter_id", filterId).NoLog().Build()
|
||||||
|
} else {
|
||||||
|
return nil, exerr.Wrap(err, "mongo-query[find-one|internal] failed").Str("collection", c.Name()).Any("filter", filter).NoLog().Build()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return &res, nil
|
return &res, nil
|
||||||
|
@@ -215,7 +215,7 @@ func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CursorToken
|
|||||||
|
|
||||||
// the conflict-resolution condition, for entries with the _same_ <field> as the $primary we take the ones with a greater $secondary (= newer)
|
// the conflict-resolution condition, for entries with the _same_ <field> as the $primary we take the ones with a greater $secondary (= newer)
|
||||||
cond = append(cond, bson.M{"$and": bson.A{
|
cond = append(cond, bson.M{"$and": bson.A{
|
||||||
bson.M{fieldPrimary: valuePrimary},
|
bson.M{"$or": bson.A{bson.M{fieldPrimary: valuePrimary}, bson.M{fieldPrimary: nil}, bson.M{fieldPrimary: bson.M{"$exists": false}}}},
|
||||||
bson.M{*fieldSecondary: bson.M{"$gt": valueSecondary}},
|
bson.M{*fieldSecondary: bson.M{"$gt": valueSecondary}},
|
||||||
}})
|
}})
|
||||||
|
|
||||||
@@ -225,7 +225,7 @@ func createPaginationPipeline[TData any](coll *Coll[TData], token ct.CursorToken
|
|||||||
|
|
||||||
// the conflict-resolution condition, for entries with the _same_ <field> as the $primary we take the ones with a smaller $secondary (= older)
|
// the conflict-resolution condition, for entries with the _same_ <field> as the $primary we take the ones with a smaller $secondary (= older)
|
||||||
cond = append(cond, bson.M{"$and": bson.A{
|
cond = append(cond, bson.M{"$and": bson.A{
|
||||||
bson.M{fieldPrimary: valuePrimary},
|
bson.M{"$or": bson.A{bson.M{fieldPrimary: valuePrimary}, bson.M{fieldPrimary: nil}, bson.M{fieldPrimary: bson.M{"$exists": false}}}},
|
||||||
bson.M{*fieldSecondary: bson.M{"$lt": valueSecondary}},
|
bson.M{*fieldSecondary: bson.M{"$lt": valueSecondary}},
|
||||||
}})
|
}})
|
||||||
|
|
||||||
|
1
wpdf/.gitignore
vendored
Normal file
1
wpdf/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
wpdf_test.pdf
|
BIN
wpdf/logo.png
Normal file
BIN
wpdf/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 11 KiB |
36
wpdf/wpdf.go
36
wpdf/wpdf.go
@@ -14,6 +14,7 @@ type WPDFBuilder struct {
|
|||||||
fontName PDFFontFamily
|
fontName PDFFontFamily
|
||||||
fontStyle PDFFontStyle
|
fontStyle PDFFontStyle
|
||||||
fontSize float64
|
fontSize float64
|
||||||
|
debug bool
|
||||||
}
|
}
|
||||||
|
|
||||||
type PDFMargins struct {
|
type PDFMargins struct {
|
||||||
@@ -62,6 +63,19 @@ func (b *WPDFBuilder) SetMargins(v PDFMargins) {
|
|||||||
|
|
||||||
func (b *WPDFBuilder) AddPage() {
|
func (b *WPDFBuilder) AddPage() {
|
||||||
b.b.AddPage()
|
b.b.AddPage()
|
||||||
|
|
||||||
|
if b.debug {
|
||||||
|
|
||||||
|
ml, mt, mr, mb := b.GetMargins()
|
||||||
|
pw, ph := b.GetPageSize()
|
||||||
|
|
||||||
|
b.Rect(pw-ml-mr, ph-mt-mb, RectOutline, NewPDFRectOpt().X(ml).Y(mt).LineWidth(0.25).DrawColor(0, 0, 128))
|
||||||
|
|
||||||
|
b.Rect(pw, mt, RectFill, NewPDFRectOpt().X(0).Y(0).FillColor(0, 0, 255).Alpha(0.2, BlendNormal))
|
||||||
|
b.Rect(ml, ph-mt-mb, RectFill, NewPDFRectOpt().X(0).Y(mt).FillColor(0, 0, 255).Alpha(0.2, BlendNormal))
|
||||||
|
b.Rect(mr, ph-mt-mb, RectFill, NewPDFRectOpt().X(pw-mr).Y(mt).FillColor(0, 0, 255).Alpha(0.2, BlendNormal))
|
||||||
|
b.Rect(pw, mb, RectFill, NewPDFRectOpt().X(0).Y(ph-mb).FillColor(0, 0, 255).Alpha(0.2, BlendNormal))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *WPDFBuilder) SetTextColor(cr, cg, cb int) {
|
func (b *WPDFBuilder) SetTextColor(cr, cg, cb int) {
|
||||||
@@ -123,7 +137,21 @@ func (b *WPDFBuilder) SetCellSpacing(h float64) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (b *WPDFBuilder) Ln(h float64) {
|
func (b *WPDFBuilder) Ln(h float64) {
|
||||||
|
xBefore, yBefore := b.GetXY()
|
||||||
|
|
||||||
b.b.Ln(h)
|
b.b.Ln(h)
|
||||||
|
|
||||||
|
yAfter := b.GetY()
|
||||||
|
|
||||||
|
if b.debug {
|
||||||
|
|
||||||
|
_, _, mr, _ := b.GetMargins()
|
||||||
|
pw, _ := b.GetPageSize()
|
||||||
|
|
||||||
|
b.Rect(pw-mr-xBefore, yAfter-yBefore, RectOutline, NewPDFRectOpt().X(xBefore).Y(yBefore).LineWidth(0.25).DrawColor(128, 128, 0).Alpha(0.5, BlendNormal))
|
||||||
|
b.Rect(pw-mr-xBefore, yAfter-yBefore, RectFill, NewPDFRectOpt().X(xBefore).Y(yBefore).LineWidth(0.25).FillColor(128, 128, 0).Alpha(0.1, BlendNormal))
|
||||||
|
b.Line(xBefore, yBefore, pw-mr, yAfter, NewPDFLineOpt().LineWidth(0.25).DrawColor(128, 128, 0))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *WPDFBuilder) Build() ([]byte, error) {
|
func (b *WPDFBuilder) Build() ([]byte, error) {
|
||||||
@@ -217,6 +245,10 @@ func (b *WPDFBuilder) PageNo() int {
|
|||||||
return b.b.PageNo()
|
return b.b.PageNo()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *WPDFBuilder) Bookmark(txtStr string, level int, y float64) {
|
||||||
|
b.b.Bookmark(b.tr(txtStr), level, y)
|
||||||
|
}
|
||||||
|
|
||||||
func (b *WPDFBuilder) GetStringWidth(str string, opts ...PDFCellOpt) float64 {
|
func (b *WPDFBuilder) GetStringWidth(str string, opts ...PDFCellOpt) float64 {
|
||||||
|
|
||||||
var fontNameOverride *PDFFontFamily
|
var fontNameOverride *PDFFontFamily
|
||||||
@@ -242,3 +274,7 @@ func (b *WPDFBuilder) GetStringWidth(str string, opts ...PDFCellOpt) float64 {
|
|||||||
|
|
||||||
return b.b.GetStringWidth(str)
|
return b.b.GetStringWidth(str)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *WPDFBuilder) Debug(v bool) {
|
||||||
|
b.debug = v
|
||||||
|
}
|
||||||
|
@@ -25,6 +25,7 @@ type PDFCellOpt struct {
|
|||||||
borderColor *PDFColor
|
borderColor *PDFColor
|
||||||
fillColor *PDFColor
|
fillColor *PDFColor
|
||||||
autoWidthPaddingX *float64
|
autoWidthPaddingX *float64
|
||||||
|
debug *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPDFCellOpt() *PDFCellOpt {
|
func NewPDFCellOpt() *PDFCellOpt {
|
||||||
@@ -158,6 +159,11 @@ func (opt *PDFCellOpt) Alpha(alpha float64, blendMode PDFBlendMode) *PDFCellOpt
|
|||||||
return opt
|
return opt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (opt *PDFCellOpt) Debug(v bool) *PDFCellOpt {
|
||||||
|
opt.debug = &v
|
||||||
|
return opt
|
||||||
|
}
|
||||||
|
|
||||||
func (opt *PDFCellOpt) Copy() *PDFCellOpt {
|
func (opt *PDFCellOpt) Copy() *PDFCellOpt {
|
||||||
c := *opt
|
c := *opt
|
||||||
return &c
|
return &c
|
||||||
@@ -186,7 +192,7 @@ func (b *WPDFBuilder) Cell(txt string, opts ...*PDFCellOpt) {
|
|||||||
txtTR := b.tr(txt)
|
txtTR := b.tr(txt)
|
||||||
|
|
||||||
width := float64(0)
|
width := float64(0)
|
||||||
height := b.cellHeight + b.cellSpacing
|
var height *float64 = nil
|
||||||
border := BorderNone
|
border := BorderNone
|
||||||
ln := BreakToNextLine
|
ln := BreakToNextLine
|
||||||
align := AlignLeft
|
align := AlignLeft
|
||||||
@@ -204,10 +210,11 @@ func (b *WPDFBuilder) Cell(txt string, opts ...*PDFCellOpt) {
|
|||||||
var borderColor *PDFColor
|
var borderColor *PDFColor
|
||||||
var fillColor *PDFColor
|
var fillColor *PDFColor
|
||||||
autoWidthPaddingX := float64(0)
|
autoWidthPaddingX := float64(0)
|
||||||
|
debug := b.debug
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
width = langext.Coalesce(opt.width, width)
|
width = langext.Coalesce(opt.width, width)
|
||||||
height = langext.Coalesce(opt.height, height)
|
height = langext.CoalesceOpt(opt.height, height)
|
||||||
border = langext.Coalesce(opt.border, border)
|
border = langext.Coalesce(opt.border, border)
|
||||||
ln = langext.Coalesce(opt.ln, ln)
|
ln = langext.Coalesce(opt.ln, ln)
|
||||||
align = langext.Coalesce(opt.align, align)
|
align = langext.Coalesce(opt.align, align)
|
||||||
@@ -225,6 +232,7 @@ func (b *WPDFBuilder) Cell(txt string, opts ...*PDFCellOpt) {
|
|||||||
borderColor = langext.CoalesceOpt(opt.borderColor, borderColor)
|
borderColor = langext.CoalesceOpt(opt.borderColor, borderColor)
|
||||||
fillColor = langext.CoalesceOpt(opt.fillColor, fillColor)
|
fillColor = langext.CoalesceOpt(opt.fillColor, fillColor)
|
||||||
autoWidthPaddingX = langext.Coalesce(opt.autoWidthPaddingX, autoWidthPaddingX)
|
autoWidthPaddingX = langext.Coalesce(opt.autoWidthPaddingX, autoWidthPaddingX)
|
||||||
|
debug = langext.Coalesce(opt.debug, debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fontNameOverride != nil || fontStyleOverride != nil || fontSizeOverride != nil {
|
if fontNameOverride != nil || fontStyleOverride != nil || fontSizeOverride != nil {
|
||||||
@@ -238,6 +246,11 @@ func (b *WPDFBuilder) Cell(txt string, opts ...*PDFCellOpt) {
|
|||||||
defer func() { b.SetFont(oldFontName, oldFontStyle, oldFontSize) }()
|
defer func() { b.SetFont(oldFontName, oldFontStyle, oldFontSize) }()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if height == nil {
|
||||||
|
// (do after SetFont, so that b.cellHeight is correctly set to fontOverride)
|
||||||
|
height = langext.Ptr(b.cellHeight + b.cellSpacing)
|
||||||
|
}
|
||||||
|
|
||||||
if textColor != nil {
|
if textColor != nil {
|
||||||
oldColorR, oldColorG, oldColorB := b.b.GetTextColor()
|
oldColorR, oldColorG, oldColorB := b.b.GetTextColor()
|
||||||
b.SetTextColor(textColor.R, textColor.G, textColor.B)
|
b.SetTextColor(textColor.R, textColor.G, textColor.B)
|
||||||
@@ -267,10 +280,22 @@ func (b *WPDFBuilder) Cell(txt string, opts ...*PDFCellOpt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if autoWidth {
|
if autoWidth {
|
||||||
width = b.b.GetStringWidth(txtTR) + autoWidthPaddingX
|
width = b.GetStringWidth(txtTR, langext.ArrDeRef(opts)...) + autoWidthPaddingX
|
||||||
}
|
}
|
||||||
|
|
||||||
b.b.CellFormat(width, height, txtTR, string(border), int(ln), string(align), fill, link, linkStr)
|
xBefore, yBefore := b.b.GetXY()
|
||||||
|
|
||||||
|
b.b.CellFormat(width, *height, txtTR, string(border), int(ln), string(align), fill, link, linkStr)
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
if ln == BreakToNextLine {
|
||||||
|
b.Rect(b.GetPageWidth()-xBefore-b.GetMarginRight(), *height, RectOutline, NewPDFRectOpt().X(xBefore).Y(yBefore).LineWidth(0.25).DrawColor(0, 128, 0))
|
||||||
|
} else if ln == BreakToRight {
|
||||||
|
b.Rect(b.GetX()-xBefore, *height, RectOutline, NewPDFRectOpt().X(xBefore).Y(yBefore).LineWidth(0.25).DrawColor(0, 128, 0))
|
||||||
|
} else if ln == BreakToBelow {
|
||||||
|
b.Rect(b.GetPageWidth()-xBefore-b.GetMarginRight(), *height, RectOutline, NewPDFRectOpt().X(xBefore).Y(yBefore).LineWidth(0.25).DrawColor(0, 128, 0))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if extraLn != 0 {
|
if extraLn != 0 {
|
||||||
b.b.Ln(extraLn)
|
b.b.Ln(extraLn)
|
||||||
|
@@ -247,7 +247,7 @@ func (b *WPDFBuilder) Image(img *PDFImageRef, opts ...*PDFImageOpt) {
|
|||||||
var imageFit *imageext.ImageFit = nil
|
var imageFit *imageext.ImageFit = nil
|
||||||
var fillColor color.Color = color.Transparent
|
var fillColor color.Color = color.Transparent
|
||||||
compression := imageext.CompressionPNGSpeed
|
compression := imageext.CompressionPNGSpeed
|
||||||
debug := false
|
debug := b.debug
|
||||||
var crop *imageext.ImageCrop = nil
|
var crop *imageext.ImageCrop = nil
|
||||||
var alphaOverride *dataext.Tuple[float64, PDFBlendMode]
|
var alphaOverride *dataext.Tuple[float64, PDFBlendMode]
|
||||||
|
|
||||||
@@ -271,6 +271,10 @@ func (b *WPDFBuilder) Image(img *PDFImageRef, opts ...*PDFImageOpt) {
|
|||||||
alphaOverride = langext.CoalesceOpt(opt.alphaOverride, alphaOverride)
|
alphaOverride = langext.CoalesceOpt(opt.alphaOverride, alphaOverride)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if flow {
|
||||||
|
y = b.GetY()
|
||||||
|
}
|
||||||
|
|
||||||
regName := img.Name
|
regName := img.Name
|
||||||
|
|
||||||
var subImageBounds *imageext.PercentageRectangle = nil
|
var subImageBounds *imageext.PercentageRectangle = nil
|
||||||
@@ -355,13 +359,14 @@ func (b *WPDFBuilder) Image(img *PDFImageRef, opts ...*PDFImageOpt) {
|
|||||||
b.b.ImageOptions(regName, x, y, w, h, flow, fpdfOpt, link, linkStr)
|
b.b.ImageOptions(regName, x, y, w, h, flow, fpdfOpt, link, linkStr)
|
||||||
|
|
||||||
if debug {
|
if debug {
|
||||||
b.Rect(w, h, RectOutline, NewPDFRectOpt().X(x).Y(y).LineWidth(2).DrawColor(255, 0, 0))
|
b.Rect(w, h, RectOutline, NewPDFRectOpt().X(x).Y(y).LineWidth(0.25).DrawColor(255, 0, 0))
|
||||||
|
|
||||||
if subImageBounds != nil {
|
if subImageBounds != nil {
|
||||||
r := subImageBounds.Of(imageext.Rectangle{X: x, Y: y, W: w, H: h})
|
r := subImageBounds.Of(imageext.Rectangle{X: x, Y: y, W: w, H: h})
|
||||||
|
b.Rect(r.W, r.H, RectOutline, NewPDFRectOpt().X(r.X).Y(r.Y).LineWidth(0.25).DrawColor(255, 0, 0))
|
||||||
b.Rect(r.W, r.H, RectFill, NewPDFRectOpt().X(r.X).Y(r.Y).FillColor(255, 0, 0).Alpha(0.2, BlendNormal))
|
b.Rect(r.W, r.H, RectFill, NewPDFRectOpt().X(r.X).Y(r.Y).FillColor(255, 0, 0).Alpha(0.2, BlendNormal))
|
||||||
b.Line(r.X, r.Y, r.X+r.W, r.Y+r.H, NewPDFLineOpt().LineWidth(2).DrawColor(255, 0, 0))
|
b.Line(r.X, r.Y, r.X+r.W, r.Y+r.H, NewPDFLineOpt().LineWidth(0.25).DrawColor(255, 0, 0))
|
||||||
b.Line(r.X+r.W, r.Y, r.X, r.Y+r.H, NewPDFLineOpt().LineWidth(2).DrawColor(255, 0, 0))
|
b.Line(r.X+r.W, r.Y, r.X, r.Y+r.H, NewPDFLineOpt().LineWidth(0.25).DrawColor(255, 0, 0))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -10,6 +10,7 @@ type PDFLineOpt struct {
|
|||||||
drawColor *PDFColor
|
drawColor *PDFColor
|
||||||
alpha *dataext.Tuple[float64, PDFBlendMode]
|
alpha *dataext.Tuple[float64, PDFBlendMode]
|
||||||
capStyle *PDFLineCapStyle
|
capStyle *PDFLineCapStyle
|
||||||
|
debug *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPDFLineOpt() *PDFLineOpt {
|
func NewPDFLineOpt() *PDFLineOpt {
|
||||||
@@ -51,17 +52,24 @@ func (opt *PDFLineOpt) CapRound() *PDFLineOpt {
|
|||||||
return opt
|
return opt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (opt *PDFLineOpt) Debug(v bool) *PDFLineOpt {
|
||||||
|
opt.debug = &v
|
||||||
|
return opt
|
||||||
|
}
|
||||||
|
|
||||||
func (b *WPDFBuilder) Line(x1 float64, y1 float64, x2 float64, y2 float64, opts ...*PDFLineOpt) {
|
func (b *WPDFBuilder) Line(x1 float64, y1 float64, x2 float64, y2 float64, opts ...*PDFLineOpt) {
|
||||||
var lineWidth *float64
|
var lineWidth *float64
|
||||||
var drawColor *PDFColor
|
var drawColor *PDFColor
|
||||||
var alphaOverride *dataext.Tuple[float64, PDFBlendMode]
|
var alphaOverride *dataext.Tuple[float64, PDFBlendMode]
|
||||||
capStyle := CapButt
|
capStyle := CapButt
|
||||||
|
debug := b.debug
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
lineWidth = langext.CoalesceOpt(opt.lineWidth, lineWidth)
|
lineWidth = langext.CoalesceOpt(opt.lineWidth, lineWidth)
|
||||||
drawColor = langext.CoalesceOpt(opt.drawColor, drawColor)
|
drawColor = langext.CoalesceOpt(opt.drawColor, drawColor)
|
||||||
alphaOverride = langext.CoalesceOpt(opt.alpha, alphaOverride)
|
alphaOverride = langext.CoalesceOpt(opt.alpha, alphaOverride)
|
||||||
capStyle = langext.Coalesce(opt.capStyle, capStyle)
|
capStyle = langext.Coalesce(opt.capStyle, capStyle)
|
||||||
|
debug = langext.Coalesce(opt.debug, debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
if lineWidth != nil {
|
if lineWidth != nil {
|
||||||
|
@@ -20,6 +20,7 @@ type PDFMultiCellOpt struct {
|
|||||||
textColor *PDFColor
|
textColor *PDFColor
|
||||||
borderColor *PDFColor
|
borderColor *PDFColor
|
||||||
fillColor *PDFColor
|
fillColor *PDFColor
|
||||||
|
debug *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPDFMultiCellOpt() *PDFMultiCellOpt {
|
func NewPDFMultiCellOpt() *PDFMultiCellOpt {
|
||||||
@@ -128,6 +129,16 @@ func (opt *PDFMultiCellOpt) Alpha(alpha float64, blendMode PDFBlendMode) *PDFMul
|
|||||||
return opt
|
return opt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (opt *PDFMultiCellOpt) Debug(v bool) *PDFMultiCellOpt {
|
||||||
|
opt.debug = &v
|
||||||
|
return opt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (opt *PDFMultiCellOpt) Copy() *PDFMultiCellOpt {
|
||||||
|
c := *opt
|
||||||
|
return &c
|
||||||
|
}
|
||||||
|
|
||||||
func (b *WPDFBuilder) MultiCell(txt string, opts ...*PDFMultiCellOpt) {
|
func (b *WPDFBuilder) MultiCell(txt string, opts ...*PDFMultiCellOpt) {
|
||||||
|
|
||||||
txtTR := b.tr(txt)
|
txtTR := b.tr(txt)
|
||||||
@@ -146,6 +157,7 @@ func (b *WPDFBuilder) MultiCell(txt string, opts ...*PDFMultiCellOpt) {
|
|||||||
var textColor *PDFColor
|
var textColor *PDFColor
|
||||||
var borderColor *PDFColor
|
var borderColor *PDFColor
|
||||||
var fillColor *PDFColor
|
var fillColor *PDFColor
|
||||||
|
debug := b.debug
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
width = langext.Coalesce(opt.width, width)
|
width = langext.Coalesce(opt.width, width)
|
||||||
@@ -162,6 +174,7 @@ func (b *WPDFBuilder) MultiCell(txt string, opts ...*PDFMultiCellOpt) {
|
|||||||
textColor = langext.CoalesceOpt(opt.textColor, textColor)
|
textColor = langext.CoalesceOpt(opt.textColor, textColor)
|
||||||
borderColor = langext.CoalesceOpt(opt.borderColor, borderColor)
|
borderColor = langext.CoalesceOpt(opt.borderColor, borderColor)
|
||||||
fillColor = langext.CoalesceOpt(opt.fillColor, fillColor)
|
fillColor = langext.CoalesceOpt(opt.fillColor, fillColor)
|
||||||
|
debug = langext.Coalesce(opt.debug, debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
if fontNameOverride != nil || fontStyleOverride != nil || fontSizeOverride != nil {
|
if fontNameOverride != nil || fontStyleOverride != nil || fontSizeOverride != nil {
|
||||||
@@ -203,8 +216,14 @@ func (b *WPDFBuilder) MultiCell(txt string, opts ...*PDFMultiCellOpt) {
|
|||||||
b.b.SetX(*x)
|
b.b.SetX(*x)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
xBefore, yBefore := b.b.GetXY()
|
||||||
|
|
||||||
b.b.MultiCell(width, height, txtTR, string(border), string(align), fill)
|
b.b.MultiCell(width, height, txtTR, string(border), string(align), fill)
|
||||||
|
|
||||||
|
if debug {
|
||||||
|
b.Rect(b.GetPageWidth()-xBefore-b.GetMarginRight(), b.GetY()-yBefore, RectOutline, NewPDFRectOpt().X(xBefore).Y(yBefore).LineWidth(0.25).DrawColor(0, 128, 0))
|
||||||
|
}
|
||||||
|
|
||||||
if extraLn != 0 {
|
if extraLn != 0 {
|
||||||
b.b.Ln(extraLn)
|
b.b.Ln(extraLn)
|
||||||
}
|
}
|
||||||
|
@@ -16,6 +16,7 @@ type PDFRectOpt struct {
|
|||||||
radiusTR *float64
|
radiusTR *float64
|
||||||
radiusBR *float64
|
radiusBR *float64
|
||||||
radiusBL *float64
|
radiusBL *float64
|
||||||
|
debug *bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewPDFRectOpt() *PDFRectOpt {
|
func NewPDFRectOpt() *PDFRectOpt {
|
||||||
@@ -90,6 +91,11 @@ func (opt *PDFRectOpt) Alpha(alpha float64, blendMode PDFBlendMode) *PDFRectOpt
|
|||||||
return opt
|
return opt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (opt *PDFRectOpt) Debug(v bool) *PDFRectOpt {
|
||||||
|
opt.debug = &v
|
||||||
|
return opt
|
||||||
|
}
|
||||||
|
|
||||||
func (b *WPDFBuilder) Rect(w float64, h float64, styleStr PDFRectStyle, opts ...*PDFRectOpt) {
|
func (b *WPDFBuilder) Rect(w float64, h float64, styleStr PDFRectStyle, opts ...*PDFRectOpt) {
|
||||||
x := b.GetX()
|
x := b.GetX()
|
||||||
y := b.GetY()
|
y := b.GetY()
|
||||||
@@ -101,6 +107,7 @@ func (b *WPDFBuilder) Rect(w float64, h float64, styleStr PDFRectStyle, opts ...
|
|||||||
radiusTR := float64(0)
|
radiusTR := float64(0)
|
||||||
radiusBR := float64(0)
|
radiusBR := float64(0)
|
||||||
radiusBL := float64(0)
|
radiusBL := float64(0)
|
||||||
|
debug := b.debug
|
||||||
|
|
||||||
for _, opt := range opts {
|
for _, opt := range opts {
|
||||||
x = langext.Coalesce(opt.x, x)
|
x = langext.Coalesce(opt.x, x)
|
||||||
@@ -113,6 +120,7 @@ func (b *WPDFBuilder) Rect(w float64, h float64, styleStr PDFRectStyle, opts ...
|
|||||||
radiusTR = langext.Coalesce(opt.radiusTR, radiusTR)
|
radiusTR = langext.Coalesce(opt.radiusTR, radiusTR)
|
||||||
radiusBR = langext.Coalesce(opt.radiusBR, radiusBR)
|
radiusBR = langext.Coalesce(opt.radiusBR, radiusBR)
|
||||||
radiusBL = langext.Coalesce(opt.radiusBL, radiusBL)
|
radiusBL = langext.Coalesce(opt.radiusBL, radiusBL)
|
||||||
|
debug = langext.Coalesce(opt.debug, debug)
|
||||||
}
|
}
|
||||||
|
|
||||||
if lineWidth != nil {
|
if lineWidth != nil {
|
||||||
|
@@ -35,24 +35,7 @@ type TableBuilder struct {
|
|||||||
rows []tableRow
|
rows []tableRow
|
||||||
defaultCellStyle *TableCellStyleOpt
|
defaultCellStyle *TableCellStyleOpt
|
||||||
columnWidths *[]string
|
columnWidths *[]string
|
||||||
}
|
debug *bool
|
||||||
|
|
||||||
type TableCell struct {
|
|
||||||
Content string
|
|
||||||
Style TableCellStyleOpt
|
|
||||||
}
|
|
||||||
|
|
||||||
type TableCellStyleOpt struct {
|
|
||||||
MultiCell *bool
|
|
||||||
Ellipsize *bool
|
|
||||||
PaddingHorz *float64
|
|
||||||
MinWidth *float64
|
|
||||||
|
|
||||||
PDFCellOpt
|
|
||||||
}
|
|
||||||
|
|
||||||
type tableRow struct {
|
|
||||||
cells []TableCell
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r tableRow) maxFontSize(defaultFontSize float64) float64 {
|
func (r tableRow) maxFontSize(defaultFontSize float64) float64 {
|
||||||
@@ -70,8 +53,8 @@ func (b *TableBuilder) Widths(v ...string) *TableBuilder {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *TableBuilder) DefaultStyle(s TableCellStyleOpt) *TableBuilder {
|
func (b *TableBuilder) DefaultStyle(s *TableCellStyleOpt) *TableBuilder {
|
||||||
b.defaultCellStyle = &s
|
b.defaultCellStyle = s
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -85,31 +68,42 @@ func (b *TableBuilder) PadY(v float64) *TableBuilder {
|
|||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *TableBuilder) AddRow(cells ...TableCell) {
|
func (b *TableBuilder) AddRow(cells ...TableCell) *TableBuilder {
|
||||||
b.rows = append(b.rows, tableRow{cells: cells})
|
b.rows = append(b.rows, tableRow{cells: cells})
|
||||||
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *TableBuilder) AddRowWithStyle(style TableCellStyleOpt, cells ...string) {
|
func (b *TableBuilder) AddRowWithStyle(style *TableCellStyleOpt, cells ...string) *TableBuilder {
|
||||||
tcels := make([]TableCell, 0, len(cells))
|
tcels := make([]TableCell, 0, len(cells))
|
||||||
for _, cell := range cells {
|
for _, cell := range cells {
|
||||||
tcels = append(tcels, TableCell{Content: cell, Style: style})
|
tcels = append(tcels, TableCell{Content: cell, Style: *style})
|
||||||
}
|
}
|
||||||
|
|
||||||
b.rows = append(b.rows, tableRow{cells: tcels})
|
b.rows = append(b.rows, tableRow{cells: tcels})
|
||||||
|
|
||||||
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *TableBuilder) AddRowDefaultStyle(cells ...string) {
|
func (b *TableBuilder) AddRowDefaultStyle(cells ...string) *TableBuilder {
|
||||||
tcels := make([]TableCell, 0, len(cells))
|
tcels := make([]TableCell, 0, len(cells))
|
||||||
for _, cell := range cells {
|
for _, cell := range cells {
|
||||||
tcels = append(tcels, TableCell{Content: cell, Style: langext.Coalesce(b.defaultCellStyle, TableCellStyleOpt{})})
|
tcels = append(tcels, TableCell{Content: cell, Style: langext.Coalesce(b.defaultCellStyle, TableCellStyleOpt{})})
|
||||||
}
|
}
|
||||||
|
|
||||||
b.rows = append(b.rows, tableRow{cells: tcels})
|
b.rows = append(b.rows, tableRow{cells: tcels})
|
||||||
|
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *TableBuilder) BuildRow() *TableRowBuilder {
|
||||||
|
return &TableRowBuilder{tabbuilder: b, cells: make([]TableCell, 0)}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (b *TableBuilder) Build() {
|
func (b *TableBuilder) Build() {
|
||||||
builder := b.builder
|
builder := b.builder
|
||||||
|
|
||||||
|
debug := langext.Coalesce(b.debug, b.builder.debug)
|
||||||
|
|
||||||
if len(b.rows) == 0 {
|
if len(b.rows) == 0 {
|
||||||
return // nothing to do
|
return // nothing to do
|
||||||
}
|
}
|
||||||
@@ -140,17 +134,21 @@ func (b *TableBuilder) Build() {
|
|||||||
str := cell.Content
|
str := cell.Content
|
||||||
style := cell.Style
|
style := cell.Style
|
||||||
|
|
||||||
ellipsize := langext.Coalesce(style.Ellipsize, true)
|
ellipsize := langext.Coalesce(style.ellipsize, true)
|
||||||
cellPaddingHorz := langext.Coalesce(style.PaddingHorz, 2)
|
cellPaddingHorz := langext.Coalesce(style.paddingHorz, 2)
|
||||||
|
|
||||||
|
fillHeight := langext.Coalesce(style.fillHeight, false)
|
||||||
|
|
||||||
bx := builder.GetX()
|
bx := builder.GetX()
|
||||||
by := builder.GetY()
|
by := builder.GetY()
|
||||||
|
|
||||||
cellWidth := columnWidths[cellIdx]
|
cellWidth := columnWidths[cellIdx]
|
||||||
|
|
||||||
if langext.Coalesce(style.MultiCell, true) {
|
_ = fillHeight // TODO implement, but how?? ( cells with fillHeight=true should have a border of the full column height, even if another column is growing it, but we do not know teh height beforehand ... )
|
||||||
|
|
||||||
builder.MultiCell(str, style.PDFCellOpt.Copy().ToMulti().Width(cellWidth))
|
if langext.Coalesce(style.multiCell, true) {
|
||||||
|
|
||||||
|
builder.MultiCell(str, style.PDFCellOpt.Copy().ToMulti().Width(cellWidth).Debug(debug))
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
@@ -163,7 +161,7 @@ func (b *TableBuilder) Build() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.Cell(str, style.PDFCellOpt.Copy().Width(cellWidth))
|
builder.Cell(str, style.PDFCellOpt.Copy().Width(cellWidth).Debug(debug))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -222,8 +220,8 @@ func (b *TableBuilder) calculateColumns() []float64 {
|
|||||||
for _, row := range b.rows {
|
for _, row := range b.rows {
|
||||||
if len(row.cells) > colIdx {
|
if len(row.cells) > colIdx {
|
||||||
|
|
||||||
ph := langext.Coalesce(row.cells[colIdx].Style.PaddingHorz, 2)
|
ph := langext.Coalesce(row.cells[colIdx].Style.paddingHorz, 2)
|
||||||
mw := langext.Coalesce(row.cells[colIdx].Style.MinWidth, 0)
|
mw := langext.Coalesce(row.cells[colIdx].Style.minWidth, 0)
|
||||||
|
|
||||||
minWidth = max(minWidth, ph+mw)
|
minWidth = max(minWidth, ph+mw)
|
||||||
|
|
||||||
@@ -281,23 +279,28 @@ func (b *TableBuilder) calculateColumns() []float64 {
|
|||||||
return columnWidths
|
return columnWidths
|
||||||
}
|
}
|
||||||
|
|
||||||
for i, _ := range columnDef {
|
{
|
||||||
if frColumnWeights[i] != 0 {
|
rmSub := 0.0
|
||||||
w := min(autoWidths[i], (remainingWidth/float64(frColumnWidthCount))*frColumnWeights[i])
|
for i := range columnDef {
|
||||||
remainingWidth += columnWidths[i]
|
if frColumnWeights[i] != 0 {
|
||||||
columnWidths[i] = w
|
w := min(autoWidths[i], (remainingWidth/float64(frColumnWidthCount))*frColumnWeights[i])
|
||||||
remainingWidth -= w
|
rmSub += w - columnWidths[i]
|
||||||
|
columnWidths[i] = w
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
remainingWidth -= rmSub
|
||||||
}
|
}
|
||||||
|
|
||||||
if remainingWidth > 0 {
|
if remainingWidth > 0.01 {
|
||||||
|
rmSub := 0.0
|
||||||
for i, _ := range columnDef {
|
for i, _ := range columnDef {
|
||||||
if frColumnWeights[i] != 0 {
|
if frColumnWeights[i] != 0 {
|
||||||
addW := (remainingWidth / float64(frColumnWidthCount)) * frColumnWeights[i]
|
addW := (remainingWidth / float64(frColumnWidthCount)) * frColumnWeights[i]
|
||||||
|
rmSub += addW
|
||||||
columnWidths[i] += addW
|
columnWidths[i] += addW
|
||||||
remainingWidth -= addW
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
remainingWidth -= rmSub
|
||||||
}
|
}
|
||||||
|
|
||||||
return columnWidths
|
return columnWidths
|
||||||
@@ -307,6 +310,11 @@ func (b *TableBuilder) RowCount() int {
|
|||||||
return len(b.rows)
|
return len(b.rows)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (b *TableBuilder) Debug(v bool) *TableBuilder {
|
||||||
|
b.debug = &v
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
func (b *WPDFBuilder) Table() *TableBuilder {
|
func (b *WPDFBuilder) Table() *TableBuilder {
|
||||||
return &TableBuilder{
|
return &TableBuilder{
|
||||||
builder: b,
|
builder: b,
|
||||||
@@ -321,12 +329,13 @@ func defaultTableStyle() *TableCellStyleOpt {
|
|||||||
return &TableCellStyleOpt{
|
return &TableCellStyleOpt{
|
||||||
PDFCellOpt: *NewPDFCellOpt().
|
PDFCellOpt: *NewPDFCellOpt().
|
||||||
FontSize(float64(8)).
|
FontSize(float64(8)).
|
||||||
BorderColorHex(uint32(0x888888)).
|
Border(BorderFull).
|
||||||
FillColorHex(uint32(0xFFFFFF)).
|
BorderColorHex(uint32(0x666666)).
|
||||||
|
FillColorHex(uint32(0xF0F0F0)).
|
||||||
TextColorHex(uint32(0x000000)).
|
TextColorHex(uint32(0x000000)).
|
||||||
FillBackground(false),
|
FillBackground(true),
|
||||||
MinWidth: langext.Ptr(float64(5)),
|
minWidth: langext.Ptr(float64(5)),
|
||||||
Ellipsize: langext.PTrue,
|
ellipsize: langext.PTrue,
|
||||||
MultiCell: langext.PFalse,
|
multiCell: langext.PFalse,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
187
wpdf/wpdfTableCell.go
Normal file
187
wpdf/wpdfTableCell.go
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
package wpdf
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/dataext"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TableCell struct {
|
||||||
|
Content string
|
||||||
|
Style TableCellStyleOpt
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableCellStyleOpt struct {
|
||||||
|
multiCell *bool
|
||||||
|
ellipsize *bool
|
||||||
|
paddingHorz *float64
|
||||||
|
minWidth *float64
|
||||||
|
fillHeight *bool
|
||||||
|
|
||||||
|
PDFCellOpt
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewTableCellStyleOpt() *TableCellStyleOpt {
|
||||||
|
return &TableCellStyleOpt{}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) FillHeight(b bool) *TableCellStyleOpt {
|
||||||
|
o.fillHeight = &b
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) MultiCell(v bool) *TableCellStyleOpt {
|
||||||
|
o.multiCell = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) Ellipsize(v bool) *TableCellStyleOpt {
|
||||||
|
o.ellipsize = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) PaddingHorz(v float64) *TableCellStyleOpt {
|
||||||
|
o.paddingHorz = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) MinWidth(v float64) *TableCellStyleOpt {
|
||||||
|
o.minWidth = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) CellStyle(v PDFCellOpt) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt = v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) Width(v float64) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.width = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) Height(v float64) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.height = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) Border(v PDFBorder) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.border = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) LnPos(v PDFTextBreak) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.ln = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) Align(v PDFTextAlign) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.align = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) FillBackground(v bool) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.fill = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) Link(v int) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.link = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) LinkStr(v string) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.linkStr = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) Font(fontName PDFFontFamily, fontStyle PDFFontStyle, fontSize float64) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.fontNameOverride = &fontName
|
||||||
|
o.PDFCellOpt.fontStyleOverride = &fontStyle
|
||||||
|
o.PDFCellOpt.fontSizeOverride = &fontSize
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) FontName(v PDFFontFamily) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.fontNameOverride = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) FontStyle(v PDFFontStyle) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.fontStyleOverride = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) FontSize(v float64) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.fontSizeOverride = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) Bold() *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.fontStyleOverride = langext.Ptr(Bold)
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) Italic() *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.fontStyleOverride = langext.Ptr(Italic)
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) LnAfter(v float64) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.extraLn = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) X(v float64) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.x = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) AutoWidth() *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.autoWidth = langext.PTrue
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) AutoWidthPaddingX(v float64) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.autoWidthPaddingX = &v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) TextColor(cr, cg, cb int) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.textColor = langext.Ptr(rgbToColor(cr, cg, cb))
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) TextColorHex(c uint32) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.textColor = langext.Ptr(hexToColor(c))
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) BorderColor(cr, cg, cb int) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.borderColor = langext.Ptr(rgbToColor(cr, cg, cb))
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) BorderColorHex(c uint32) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.borderColor = langext.Ptr(hexToColor(c))
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) FillColor(cr, cg, cb int) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.fillColor = langext.Ptr(rgbToColor(cr, cg, cb))
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) FillColorHex(c uint32) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.fillColor = langext.Ptr(hexToColor(c))
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) Alpha(alpha float64, blendMode PDFBlendMode) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.alphaOverride = &dataext.Tuple[float64, PDFBlendMode]{V1: alpha, V2: blendMode}
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o *TableCellStyleOpt) Debug(v bool) *TableCellStyleOpt {
|
||||||
|
o.PDFCellOpt.debug = &v
|
||||||
|
return o
|
||||||
|
}
|
52
wpdf/wpdfTableRow.go
Normal file
52
wpdf/wpdfTableRow.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package wpdf
|
||||||
|
|
||||||
|
import "gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
|
|
||||||
|
type tableRow struct {
|
||||||
|
cells []TableCell
|
||||||
|
}
|
||||||
|
|
||||||
|
type TableRowBuilder struct {
|
||||||
|
tabbuilder *TableBuilder
|
||||||
|
defaultStyle *TableCellStyleOpt
|
||||||
|
cells []TableCell
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TableRowBuilder) RowStyle(style *TableCellStyleOpt) *TableRowBuilder {
|
||||||
|
r.defaultStyle = style
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TableRowBuilder) Cell(cell string) *TableRowBuilder {
|
||||||
|
r.cells = append(r.cells, TableCell{Content: cell, Style: langext.Coalesce3(r.defaultStyle, r.tabbuilder.defaultCellStyle, TableCellStyleOpt{})})
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TableRowBuilder) Cells(cells ...string) *TableRowBuilder {
|
||||||
|
for _, cell := range cells {
|
||||||
|
r.cells = append(r.cells, TableCell{Content: cell, Style: langext.Coalesce3(r.defaultStyle, r.tabbuilder.defaultCellStyle, TableCellStyleOpt{})})
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TableRowBuilder) CellObject(cell TableCell) *TableRowBuilder {
|
||||||
|
r.cells = append(r.cells, cell)
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TableRowBuilder) CellObjects(cells ...TableCell) *TableRowBuilder {
|
||||||
|
for _, cell := range cells {
|
||||||
|
r.cells = append(r.cells, cell)
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TableRowBuilder) CellWithStyle(cell string, style *TableCellStyleOpt) *TableRowBuilder {
|
||||||
|
r.cells = append(r.cells, TableCell{Content: cell, Style: *style})
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *TableRowBuilder) BuildRow() *TableBuilder {
|
||||||
|
r.tabbuilder.AddRow(r.cells...)
|
||||||
|
return r.tabbuilder
|
||||||
|
}
|
105
wpdf/wpdf_test.go
Normal file
105
wpdf/wpdf_test.go
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
package wpdf
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"fmt"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/imageext"
|
||||||
|
"gogs.mikescher.com/BlackForestBytes/goext/langext"
|
||||||
|
"os"
|
||||||
|
"path"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed logo.png
|
||||||
|
var logoData []byte
|
||||||
|
|
||||||
|
func TestPDFBuilder(t *testing.T) {
|
||||||
|
builder := NewPDFBuilder(Portrait, SizeA4, true)
|
||||||
|
|
||||||
|
builder.Debug(true)
|
||||||
|
|
||||||
|
logoRef := builder.RegisterImage(logoData)
|
||||||
|
|
||||||
|
builder.SetMargins(PDFMargins{Left: 15, Top: 40, Right: 10})
|
||||||
|
builder.AddPage()
|
||||||
|
|
||||||
|
builder.SetFont(FontHelvetica, Normal, 10)
|
||||||
|
builder.Cell("Neueinrichtung deiner Entgeltumwandlung", NewPDFCellOpt().Bold().FontSize(20))
|
||||||
|
builder.Ln(10)
|
||||||
|
|
||||||
|
builder.SetFont(FontHelvetica, Normal, 10)
|
||||||
|
builder.Cell("Hello World", NewPDFCellOpt().Width(50).Align(AlignHorzCenter).LnPos(BreakToRight))
|
||||||
|
builder.IncX(10)
|
||||||
|
builder.Cell("Second Text", NewPDFCellOpt().AutoWidth().AutoWidthPaddingX(2).LnPos(BreakToRight))
|
||||||
|
builder.Ln(10)
|
||||||
|
|
||||||
|
builder.MultiCell("Im Fall einer individuellen Entgeltumwandlung ist die Zuschussverpflichtung auf der Grundlage des Betriebsrentenstärkungsgesetzes in der gesetzlich vorgeschriebenen Höhe (§ 1a Abs. 1a BetrAVG), über den arbeitgeberfinanzierten Zuschuss erfüllt.")
|
||||||
|
builder.Ln(4)
|
||||||
|
|
||||||
|
builder.Image(logoRef, NewPDFImageOpt().X(90).Y(160).Width(70).Height(30).ImageFit(imageext.ImageFitContainCenter))
|
||||||
|
|
||||||
|
builder.Ln(4)
|
||||||
|
|
||||||
|
cellStyleHeader := &TableCellStyleOpt{
|
||||||
|
PDFCellOpt: *NewPDFCellOpt().
|
||||||
|
FontSize(float64(8)).
|
||||||
|
BorderColorHex(uint32(0x666666)).
|
||||||
|
Border(BorderFull).
|
||||||
|
FillColorHex(uint32(0xC0C0C0)).
|
||||||
|
FillBackground(true).
|
||||||
|
TextColorHex(uint32(0x000000)).
|
||||||
|
Align(AlignHorzCenter).
|
||||||
|
Bold(),
|
||||||
|
minWidth: langext.Ptr(float64(5)),
|
||||||
|
ellipsize: langext.PTrue,
|
||||||
|
multiCell: langext.PFalse,
|
||||||
|
}
|
||||||
|
|
||||||
|
cellStyleMulti := &TableCellStyleOpt{
|
||||||
|
PDFCellOpt: *NewPDFCellOpt().
|
||||||
|
FontSize(float64(8)).
|
||||||
|
BorderColorHex(uint32(0x666666)).
|
||||||
|
Border(BorderFull).
|
||||||
|
FillColorHex(uint32(0xC060C0)).
|
||||||
|
FillBackground(true).
|
||||||
|
TextColorHex(uint32(0x000000)),
|
||||||
|
minWidth: langext.Ptr(float64(5)),
|
||||||
|
ellipsize: langext.PFalse,
|
||||||
|
multiCell: langext.PTrue,
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.Table().
|
||||||
|
Widths("auto", "20", "1fr", "20").
|
||||||
|
PadX(2).
|
||||||
|
PadY(2).
|
||||||
|
AddRowWithStyle(cellStyleHeader, "test", "hello", "123", "end").
|
||||||
|
AddRowDefaultStyle("test", "hello", "123", "end").
|
||||||
|
AddRowDefaultStyle("123", "helasdsalo", "a", "enwqad").
|
||||||
|
AddRowDefaultStyle("123asd", "TrimMeTrimMeTrimMeTrimMe", "a", "enwqad").
|
||||||
|
AddRowWithStyle(cellStyleMulti, "123", "helasdsalo", "a", "MultiCell: enwqad enw\nqad enwqad enwqad enwqad enwqad").
|
||||||
|
AddRowDefaultStyle("123", "helasdsalo", "a", "enwqad").
|
||||||
|
Debug(false).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
builder.Ln(8)
|
||||||
|
|
||||||
|
builder.Table().
|
||||||
|
Widths("auto", "20", "1fr", "20").
|
||||||
|
PadX(2).
|
||||||
|
PadY(2).
|
||||||
|
BuildRow().RowStyle(cellStyleHeader).Cells("test", "hello", "123", "end").BuildRow().
|
||||||
|
BuildRow().Cells("test", "hello", "123", "end").BuildRow().
|
||||||
|
BuildRow().RowStyle(cellStyleMulti.FillHeight(true)).Cell("123").Cell("helasdsalo").Cell("a").Cell("MultiCell: enwqad enw\nqad enwqad enwqad enwqad enwqad").BuildRow().
|
||||||
|
AddRowDefaultStyle("123", "helasdsalo", "a", "enwqad").
|
||||||
|
Debug(false).
|
||||||
|
Build()
|
||||||
|
|
||||||
|
bin, err := builder.Build()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn := "wpdf_test.pdf"
|
||||||
|
_ = os.WriteFile(fn, bin, 0644)
|
||||||
|
fmt.Println("file://" + path.Join(langext.Must(os.Getwd()), fn))
|
||||||
|
}
|
Reference in New Issue
Block a user