1st
This commit is contained in:
commit
e8a06e8edd
3 changed files with 585 additions and 0 deletions
13
go.mod
Normal file
13
go.mod
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
module github.com/zeevdiukman/go-helper
|
||||
|
||||
go 1.24.0
|
||||
|
||||
require github.com/gookit/goutil v0.6.18
|
||||
|
||||
require (
|
||||
github.com/gookit/color v1.5.4 // indirect
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/sys v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
)
|
||||
22
go.sum
Normal file
22
go.sum
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/gookit/goutil v0.6.18 h1:MUVj0G16flubWT8zYVicIuisUiHdgirPAkmnfD2kKgw=
|
||||
github.com/gookit/goutil v0.6.18/go.mod h1:AY/5sAwKe7Xck+mEbuxj0n/bc3qwrGNe3Oeulln7zBA=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
|
||||
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
550
helper.go
Normal file
550
helper.go
Normal file
|
|
@ -0,0 +1,550 @@
|
|||
package helper
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
"os/signal"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/gookit/goutil/dump"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
// const (
|
||||
// COLOR_Reset = "\033[0m"
|
||||
// COLOR_Red = "\033[31m"
|
||||
// COLOR_Green = "\033[32m"
|
||||
// COLOR_Yellow = "\033[33m"
|
||||
// COLOR_Blue = "\033[34m"
|
||||
// COLOR_Purple = "\033[35m"
|
||||
// COLOR_Cyan = "\033[36m"
|
||||
// COLOR_Gray = "\033[37m"
|
||||
// COLOR_White = "\033[97m" // Brighter white
|
||||
// )
|
||||
|
||||
// func Log(msg string) {
|
||||
// log.New(os.Stdout, msg, log.Ldate|log.Ltime)
|
||||
|
||||
// }
|
||||
|
||||
// func Colorize(colorCode string, message string) string {
|
||||
// return colorCode + message + COLOR_Reset
|
||||
// }
|
||||
|
||||
type IsStruct struct{}
|
||||
|
||||
var (
|
||||
Is IsStruct
|
||||
h = New()
|
||||
)
|
||||
|
||||
type Helper struct {
|
||||
Convert Convert
|
||||
Struct Struct
|
||||
Screen Screen
|
||||
Error Error
|
||||
If If
|
||||
Log Log
|
||||
}
|
||||
type Convert struct {
|
||||
}
|
||||
type Struct struct {
|
||||
}
|
||||
type Screen struct {
|
||||
}
|
||||
type Error struct {
|
||||
Val error
|
||||
}
|
||||
type If struct {
|
||||
Error Error
|
||||
}
|
||||
type Log struct {
|
||||
Error Error
|
||||
}
|
||||
|
||||
func (h *Helper) P(val any) {
|
||||
dump.Println(val)
|
||||
}
|
||||
|
||||
// InsertStringValueIntoField inserts a string value into a specified field of a struct.
|
||||
//
|
||||
// structPtr must be a pointer to a struct.
|
||||
// fieldName is the name of the field (case-sensitive).
|
||||
// stringValue is the string value to be inserted.
|
||||
//
|
||||
// Returns an error if:
|
||||
// - structPtr is not a pointer to a struct.
|
||||
// - The fieldName is not found in the struct.
|
||||
// - The field is not settable (e.g., unexported).
|
||||
// - The field is not of type string.
|
||||
func New() *Helper {
|
||||
return &Helper{}
|
||||
}
|
||||
|
||||
func (e *Error) Log() {
|
||||
if e.Val != nil {
|
||||
log.Println(e.Val.Error())
|
||||
}
|
||||
}
|
||||
func (e *Error) In(err error) *Error {
|
||||
e.Val = err
|
||||
return e
|
||||
}
|
||||
func (conv *Struct) Insert(structPtr interface{}, param ...any) {
|
||||
// func (conv *Struct) Insert(structPtr interface{}, fieldName string, stringValue string, sep string) string {
|
||||
|
||||
rawFieldName := param[0].(string)
|
||||
fieldValue := param[1]
|
||||
sep := ""
|
||||
if len(param) > 2 {
|
||||
sep = param[2].(string)
|
||||
} else {
|
||||
sep = "_"
|
||||
}
|
||||
// if len(param[2].(string)) > 0 {
|
||||
// sep = param[2].(string)
|
||||
// }
|
||||
seperatorRune := []rune(sep)[0]
|
||||
field := CapitalizeAfterChar(rawFieldName, seperatorRune)
|
||||
field = CapitalizeFirstLetter(field)
|
||||
field = strings.ReplaceAll(field, sep, "")
|
||||
|
||||
err := InsertAnyValueIntoField(structPtr, field, fieldValue)
|
||||
h.Error.Val = err
|
||||
h.If.Error.Log()
|
||||
}
|
||||
func (conv *Struct) ToStructField(str string, seperator string) string {
|
||||
seperatorRune := []rune(seperator)[0]
|
||||
field := CapitalizeAfterChar(str, seperatorRune)
|
||||
field = CapitalizeFirstLetter(field)
|
||||
field = strings.ReplaceAll(field, seperator, "")
|
||||
return field
|
||||
}
|
||||
func InsertStringValueIntoField(structPtr interface{}, fieldName string, stringValue string) error {
|
||||
if structPtr == nil {
|
||||
return errors.New("structPtr cannot be nil")
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(structPtr)
|
||||
if val.Kind() != reflect.Ptr {
|
||||
return errors.New("structPtr must be a pointer to a struct")
|
||||
}
|
||||
|
||||
elem := val.Elem()
|
||||
if elem.Kind() != reflect.Struct {
|
||||
return errors.New("structPtr must be a pointer to a struct")
|
||||
}
|
||||
|
||||
fieldVal := elem.FieldByName(fieldName)
|
||||
if !fieldVal.IsValid() {
|
||||
return fmt.Errorf("field '%s' not found in struct", fieldName)
|
||||
}
|
||||
|
||||
if !fieldVal.CanSet() {
|
||||
return fmt.Errorf("field '%s' is not settable (unexported or embedded without being exported)", fieldName)
|
||||
}
|
||||
|
||||
if fieldVal.Kind() != reflect.String {
|
||||
return fmt.Errorf("field '%s' is not a string type", fieldName)
|
||||
}
|
||||
|
||||
fieldVal.SetString(stringValue)
|
||||
return nil
|
||||
}
|
||||
func InsertAnyValueIntoField(structPtr interface{}, fieldName string, anyValue any) error {
|
||||
if structPtr == nil {
|
||||
return errors.New("structPtr cannot be nil")
|
||||
}
|
||||
|
||||
val := reflect.ValueOf(structPtr)
|
||||
if val.Kind() != reflect.Ptr {
|
||||
return errors.New("structPtr must be a pointer to a struct")
|
||||
}
|
||||
|
||||
elem := val.Elem()
|
||||
if elem.Kind() != reflect.Struct {
|
||||
return errors.New("structPtr must be a pointer to a struct")
|
||||
}
|
||||
|
||||
fieldVal := elem.FieldByName(fieldName)
|
||||
if !fieldVal.IsValid() {
|
||||
return fmt.Errorf("field '%s' not found in struct", fieldName)
|
||||
}
|
||||
|
||||
if !fieldVal.CanSet() {
|
||||
return fmt.Errorf("field '%s' is not settable (unexported or embedded without being exported)", fieldName)
|
||||
}
|
||||
|
||||
switch v := anyValue.(type) {
|
||||
case string:
|
||||
{
|
||||
if fieldVal.Kind() != reflect.String {
|
||||
return fmt.Errorf("field '%s' is not a string type", fieldName)
|
||||
}
|
||||
|
||||
fieldVal.SetString(v)
|
||||
return nil
|
||||
}
|
||||
case bool:
|
||||
{
|
||||
if fieldVal.Kind() != reflect.Bool {
|
||||
return fmt.Errorf("field '%s' is not a string type", fieldName)
|
||||
}
|
||||
|
||||
fieldVal.SetBool(v)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("value of field '%s' is unknown type", fieldName)
|
||||
}
|
||||
|
||||
// CapitalizeAfterChar capitalizes the first letter immediately following each occurrence of a specific character in a string.
|
||||
//
|
||||
// For example:
|
||||
// CapitalizeAfterChar("hello_world", '_') == "hello_World"
|
||||
// CapitalizeAfterChar("this-is-a-test", '-') == "this-Is-A-Test"
|
||||
// CapitalizeAfterChar(" leading spaces and_underscores", '_') == " leading spaces and_Underscores"
|
||||
func CapitalizeAfterChar(input string, char rune) string {
|
||||
var result strings.Builder
|
||||
capitalizeNext := false // Flag to indicate if the next letter should be capitalized
|
||||
|
||||
for _, r := range input {
|
||||
if capitalizeNext {
|
||||
result.WriteRune(unicode.ToUpper(r)) // Capitalize the current rune
|
||||
capitalizeNext = false // Reset the flag
|
||||
} else {
|
||||
result.WriteRune(r) // Write the rune as is
|
||||
}
|
||||
|
||||
if r == char {
|
||||
capitalizeNext = true // Set the flag to capitalize the next letter
|
||||
}
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
func CapitalizeAfterCharMulti(input string, char rune) string {
|
||||
var result strings.Builder
|
||||
capitalizeNext := false // Flag to indicate if the next letter should be capitalized
|
||||
|
||||
for _, r := range input {
|
||||
if capitalizeNext {
|
||||
result.WriteRune(unicode.ToUpper(r)) // Capitalize the current rune
|
||||
capitalizeNext = false // Reset the flag
|
||||
} else {
|
||||
result.WriteRune(r) // Write the rune as is
|
||||
}
|
||||
|
||||
if r == char {
|
||||
capitalizeNext = true // Set the flag to capitalize the next letter
|
||||
}
|
||||
}
|
||||
return result.String()
|
||||
}
|
||||
|
||||
func CapitalizeFirstLetter(input string) string {
|
||||
if input == "" {
|
||||
return input // Return empty string if input is empty
|
||||
}
|
||||
|
||||
runes := []rune(input) // Convert string to rune slice for Unicode support
|
||||
firstRune := runes[0]
|
||||
|
||||
if !unicode.IsLetter(firstRune) {
|
||||
return input // Return original string if first char is not a letter
|
||||
}
|
||||
|
||||
capitalizedFirstRune := unicode.ToUpper(firstRune)
|
||||
runes[0] = capitalizedFirstRune
|
||||
|
||||
return string(runes) // Convert rune slice back to string
|
||||
}
|
||||
func MapIter[K string, V any](m map[K]V, fn func(K, V)) {
|
||||
for p, d := range m {
|
||||
fn(p, d)
|
||||
}
|
||||
}
|
||||
func Clear() {
|
||||
if runtime.GOOS == "windows" {
|
||||
cmd := exec.Command("cmd", "/c", "cls")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Run()
|
||||
} else {
|
||||
cmd := exec.Command("clear")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Run()
|
||||
}
|
||||
}
|
||||
|
||||
func RandStringByBits(nBits int) string {
|
||||
b := make([]byte, nBits/8)
|
||||
if _, err := io.ReadFull(rand.Reader, b); err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
return base64.RawURLEncoding.EncodeToString(b)
|
||||
}
|
||||
|
||||
func StructToMap(obj interface{}) map[string]any {
|
||||
|
||||
val := reflect.ValueOf(obj)
|
||||
typ := reflect.TypeOf(obj)
|
||||
result := make(map[string]any)
|
||||
|
||||
for i := 0; i < val.NumField(); i++ {
|
||||
field := typ.Field(i)
|
||||
result[field.Name] = val.Field(i).Interface()
|
||||
}
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func FetchGetJson(u string) (map[string]any, error) {
|
||||
resp, err := http.Get(u)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
respByes, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
data := map[string]any{}
|
||||
json.Unmarshal(respByes, &data)
|
||||
return data, nil
|
||||
}
|
||||
|
||||
func GetLastChar(str string) string {
|
||||
return str[len(str)-1:]
|
||||
}
|
||||
|
||||
func IsLastChar(str string, chr string) bool {
|
||||
if l := GetLastChar(str); l == chr {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
|
||||
}
|
||||
|
||||
func IsLastCharDo(str string, chr string, f func(r bool)) bool {
|
||||
result := IsLastChar(str, chr)
|
||||
|
||||
func(bool) {
|
||||
f(result)
|
||||
}(result)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
func IsFetchOK[V any](u string, funcName string, f func(string) (*http.Response, error)) *http.Response {
|
||||
// errCntr := 0
|
||||
errCntrTotal := 0
|
||||
var (
|
||||
a V
|
||||
resp *http.Response
|
||||
err error
|
||||
)
|
||||
for {
|
||||
resp, err = f(u)
|
||||
if err == nil && resp.StatusCode == 200 {
|
||||
// ClearScreen()
|
||||
break
|
||||
} else {
|
||||
// errCntr++
|
||||
// errCntrTotal++
|
||||
// if errCntr == 10 {
|
||||
// errCntr = 0
|
||||
// ClearScreen()
|
||||
// }
|
||||
// if errCntr == 1 {
|
||||
// ClearScreen()
|
||||
// }
|
||||
errCntrTotalStr := strconv.Itoa(errCntrTotal)
|
||||
pkg := reflect.TypeOf(a).PkgPath()
|
||||
dt := time.Now()
|
||||
dateTime := dt.Format("02-01-2006 15:04:05")
|
||||
fmt.Println("ERROR: " + dateTime + ">---------<" + errCntrTotalStr + ">")
|
||||
fmt.Println(" :")
|
||||
fmt.Println(" Package name: " + pkg)
|
||||
fmt.Println("Function name: " + funcName)
|
||||
fmt.Println(" URL: " + u)
|
||||
fmt.Println("Error message: " + err.Error())
|
||||
fmt.Println("--------------------------------------")
|
||||
time.Sleep(5 * time.Second)
|
||||
}
|
||||
|
||||
}
|
||||
return resp
|
||||
}
|
||||
|
||||
func IsSliceContains[T comparable](sliceVals []T, valueToSearch T) bool {
|
||||
for _, v := range sliceVals {
|
||||
if v == valueToSearch {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func AddDotBetween(k ...string) string {
|
||||
constructedKey := ""
|
||||
for i, key := range k {
|
||||
constructedKey += key
|
||||
if i < len(k)-1 {
|
||||
constructedKey += "."
|
||||
}
|
||||
}
|
||||
return constructedKey
|
||||
}
|
||||
|
||||
func RemoveSliceDuplicates(elements []string) []string {
|
||||
encountered := map[string]bool{}
|
||||
result := []string{}
|
||||
|
||||
for v := range elements {
|
||||
if encountered[elements[v]] {
|
||||
// Do not add duplicate.
|
||||
} else {
|
||||
// Record this element as an encountered element.
|
||||
encountered[elements[v]] = true
|
||||
// Append to result slice.
|
||||
result = append(result, elements[v])
|
||||
}
|
||||
}
|
||||
// Return the new slice.
|
||||
return result
|
||||
}
|
||||
|
||||
func IfLast[T any](counter int, someVar map[string]T, fn func()) {
|
||||
counter++
|
||||
if len(someVar) == counter {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *IsStruct) Pointer(v any) bool {
|
||||
|
||||
valueOfP := reflect.ValueOf(v)
|
||||
if valueOfP.Kind() == reflect.Ptr {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func AppRunner(shoudClear bool, runApp func()) {
|
||||
|
||||
if shoudClear {
|
||||
Clear()
|
||||
}
|
||||
wg := sync.WaitGroup{}
|
||||
ctx, stop := signal.NotifyContext(context.Background(), syscall.SIGINT, syscall.SIGTERM)
|
||||
defer stop()
|
||||
|
||||
defer func() {
|
||||
v := recover()
|
||||
fmt.Println("Recovered:", v)
|
||||
}()
|
||||
|
||||
runApp()
|
||||
|
||||
<-ctx.Done()
|
||||
//do stuff after ending
|
||||
wg.Wait()
|
||||
log.Println("AppRunner stopped")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func StartTestHTTPServer(port int, name string) {
|
||||
p := strconv.Itoa(port)
|
||||
go func() {
|
||||
fmt.Println("Test server (" + name + "): listening port " + p)
|
||||
// fmt.Println("Test server is running at http://" + GetIP() + ":" + p)
|
||||
r := mux.NewRouter()
|
||||
r.Path("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, r.Host+"/ OK")
|
||||
})
|
||||
r.Path("/test1").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, r.Host+"/test1 OK")
|
||||
})
|
||||
r.Path("/test2").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, r.Host+"/test2 OK")
|
||||
})
|
||||
err := http.ListenAndServe(":"+p, r)
|
||||
if err != nil {
|
||||
log.Println(err.Error())
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func GetIP(prefix ...string) string {
|
||||
prfx := "192.168."
|
||||
if len(prefix) > 0 {
|
||||
prfx = prefix[0]
|
||||
}
|
||||
|
||||
addrs, err := net.InterfaceAddrs()
|
||||
if err != nil {
|
||||
fmt.Println("Error getting interface addresses:", err)
|
||||
return ""
|
||||
}
|
||||
|
||||
for _, addr := range addrs {
|
||||
// Check if the address is an IP address
|
||||
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
|
||||
a := strings.HasPrefix(ipnet.IP.String(), prfx)
|
||||
c := ipnet.IP.To4() != nil
|
||||
if c && a { //check for ipv4
|
||||
prfx = ipnet.IP.String()
|
||||
break
|
||||
}
|
||||
// } else if ipnet.IP.To16() != nil { //check for ipv6
|
||||
// fmt.Println("Local IPv6 address:", ipnet.IP.String())
|
||||
// }
|
||||
}
|
||||
}
|
||||
return prfx
|
||||
}
|
||||
|
||||
func AppendToPointer[T any](slc *[]T, val T) {
|
||||
*slc = append(*slc, val)
|
||||
}
|
||||
|
||||
func LoadCertificate(certFile, keyFile string) (tls.Certificate, error) {
|
||||
cert, err := tls.LoadX509KeyPair(certFile, keyFile)
|
||||
if err != nil {
|
||||
return tls.Certificate{}, err
|
||||
}
|
||||
return cert, nil
|
||||
}
|
||||
|
||||
func IsStringEmpty(str string) bool {
|
||||
return str == ""
|
||||
}
|
||||
|
||||
func IsKeyExistsInMap[V comparable](mp map[string]V, key string) (V, bool, error) {
|
||||
if val, ok := mp[key]; !ok {
|
||||
err := errors.New("Error: missing \"" + key + "\" key")
|
||||
log.Println(err.Error())
|
||||
return val, ok, err
|
||||
} else {
|
||||
return val, ok, nil
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue