209 lines
5.9 KiB
Go
209 lines
5.9 KiB
Go
package dbx
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/go-jet/jet/v2/mysql"
|
|
"golang.org/x/exp/constraints"
|
|
)
|
|
|
|
// StringToFilter processes a string to be used as a filter in an SQL LIKE
|
|
// statement. It replaces all spaces with % and adds % to the beginning and
|
|
// end of the string.
|
|
func StringToFilter(str string) string {
|
|
// Remove any existing leading or trailing % characters
|
|
str = strings.Trim(str, "%")
|
|
|
|
// Replace all spaces with % and add % to the beginning and end of the string
|
|
str = strings.ReplaceAll(str, " ", "%")
|
|
str = "%" + str + "%"
|
|
|
|
return str
|
|
}
|
|
|
|
// ExprValues converts a list of values to a list of mysql.Expression values using
|
|
// function f to transform the values (mysql.String for strings, mysql.Uint64, etc).
|
|
func ExprValues[T any](values []T, f func(T) mysql.Expression) []mysql.Expression {
|
|
expressions := make([]mysql.Expression, len(values))
|
|
for i, v := range values {
|
|
expressions[i] = f(v)
|
|
}
|
|
return expressions
|
|
}
|
|
|
|
// ExprStringers converts a list of fmt.Stringers to a list of mysql.Expression values.
|
|
func ExprStringers(values []fmt.Stringer) []mysql.Expression {
|
|
expressions := make([]mysql.Expression, len(values))
|
|
for i, v := range values {
|
|
expressions[i] = mysql.String(v.String())
|
|
}
|
|
return expressions
|
|
}
|
|
|
|
// NowPtr returns a pointer to the current time.
|
|
func NowPtr() *time.Time {
|
|
now := time.Now()
|
|
return &now
|
|
}
|
|
|
|
// Ptr returns a pointer to the given value of any scalar type. Returns nil if the value is a zero value.
|
|
func Ptr[T any](val T) *T {
|
|
if reflect.ValueOf(val).IsZero() {
|
|
return nil
|
|
}
|
|
return &val
|
|
}
|
|
|
|
// Val returns the value of the pointer to a scalar type, or the zero value if the pointer is nil.
|
|
func Val[T any](ptr *T) T {
|
|
if ptr == nil {
|
|
var zero T
|
|
return zero
|
|
}
|
|
return *ptr
|
|
}
|
|
|
|
// TrimPtr trims the whitespace from a pointer to a string and returns nil only if the pointer is nil.
|
|
func TrimPtr(s *string) *string {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
trimmed := strings.TrimSpace(*s)
|
|
return &trimmed
|
|
}
|
|
|
|
// TrimPtrToNil trims the whitespace from a pointer to a string and returns nil
|
|
// if the resulting string is empty or if the pointer is nil.
|
|
func TrimPtrToNil(s *string) *string {
|
|
if s == nil {
|
|
return nil
|
|
}
|
|
trimmed := strings.TrimSpace(*s)
|
|
if trimmed == "" {
|
|
return nil
|
|
}
|
|
return &trimmed
|
|
}
|
|
|
|
// IsZero checks if a pointer references the zero value of a given type and
|
|
// returns an error if this condition is met, otherwise returns nil if the
|
|
// pointer is nil or the value is not zero.
|
|
func IsZero[T any](ptr *T) error {
|
|
if ptr == nil {
|
|
return nil
|
|
}
|
|
if reflect.ValueOf(*ptr).IsZero() {
|
|
return ErrValueIsZero
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ApplyPtr compares the existing value with a new value and returns the updated value if they differ.
|
|
// If the new value is nil, the existing value is retained. If the new value is a zero-value, the
|
|
// existing value is NOT retained, it will be set to nil. If the value is changed, targetColumn is pushed
|
|
// to updatedColumns.
|
|
func ApplyPtr[T constraints.Float | constraints.Integer | string | bool](
|
|
existing *T,
|
|
newVal *T,
|
|
updatedColumns *mysql.ColumnList,
|
|
targetColumn mysql.Column,
|
|
) *T {
|
|
if newVal == nil {
|
|
return existing
|
|
}
|
|
if reflect.ValueOf(*newVal).IsZero() {
|
|
newVal = nil
|
|
}
|
|
if newVal == nil && existing == nil || newVal != nil && existing != nil && *existing == *newVal {
|
|
return existing
|
|
}
|
|
*updatedColumns = append(*updatedColumns, targetColumn)
|
|
return newVal
|
|
}
|
|
|
|
// ApplyComplexPtr compares the existing value with a new value and returns the updated value if they differ.
|
|
// The new value may be of a different type (e.g. existing is uint16 and new is uint64), but it will be
|
|
// converted to match the current type resulting in potential loss of data. If the new value is nil, the
|
|
// existing value is retained. If the new value is a zero-value, the existing value is NOT retained, it
|
|
// will be set to nil. If the value is changed, targetColumn is pushed to updatedColumns.
|
|
func ApplyComplexPtr[
|
|
Existing constraints.Float | constraints.Integer,
|
|
New constraints.Float | constraints.Integer,
|
|
](
|
|
existing *Existing,
|
|
newVal *New,
|
|
updatedColumns *mysql.ColumnList,
|
|
targetColumn mysql.Column,
|
|
) *Existing {
|
|
if newVal == nil {
|
|
return existing
|
|
}
|
|
cast := Existing(*newVal) // Convert new value to existing type
|
|
if existing != nil && *existing == cast {
|
|
return existing
|
|
}
|
|
if reflect.ValueOf(cast).IsZero() {
|
|
if existing != nil {
|
|
*updatedColumns = append(*updatedColumns, targetColumn)
|
|
}
|
|
return nil
|
|
} else {
|
|
*updatedColumns = append(*updatedColumns, targetColumn)
|
|
return &cast
|
|
}
|
|
}
|
|
|
|
type ApplyInterface[T any] interface {
|
|
Equal(T) bool
|
|
IsZero() bool
|
|
}
|
|
|
|
// ApplyInterfacePtr compares the existing value with a new value and returns the updated value if
|
|
// they differ. Comparable types must have IsZero and Equal methods. If the new value is nil, the
|
|
// existing value is retained. If the new value is a zero-value, the existing value is NOT retained,
|
|
// it will be set to nil. If the value is changed, targetColumn is pushed to updatedColumns.
|
|
func ApplyInterfacePtr[T ApplyInterface[T]](
|
|
existing *T,
|
|
newVal *T,
|
|
updatedColumns *mysql.ColumnList,
|
|
targetColumn mysql.Column,
|
|
) *T {
|
|
if newVal == nil {
|
|
return existing
|
|
}
|
|
if existing != nil && (*existing).Equal(*newVal) {
|
|
return existing
|
|
}
|
|
if (*newVal).IsZero() {
|
|
if existing != nil {
|
|
*updatedColumns = append(*updatedColumns, targetColumn)
|
|
}
|
|
return nil
|
|
} else {
|
|
*updatedColumns = append(*updatedColumns, targetColumn)
|
|
return newVal
|
|
}
|
|
}
|
|
|
|
// ApplyVal compares the existing value with a pointer to a new value and returns the updated value if they
|
|
// differ. If the new value is nil, the existing value is retained. If the value is changed, targetColumn
|
|
// is pushed to updatedColumns
|
|
func ApplyVal[T constraints.Float | constraints.Integer | string | bool](
|
|
existing T,
|
|
newVal *T,
|
|
updatedColumns *mysql.ColumnList,
|
|
targetColumn mysql.Column,
|
|
) T {
|
|
if newVal == nil {
|
|
return existing
|
|
}
|
|
if existing == *newVal {
|
|
return existing
|
|
}
|
|
*updatedColumns = append(*updatedColumns, targetColumn)
|
|
return *newVal
|
|
}
|