commit e8a06e8edd2620d0ade977929414e672750362b5 Author: Zeev Diukman Date: Thu Mar 6 22:53:59 2025 +0000 1st diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..6e413cf --- /dev/null +++ b/go.mod @@ -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 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e49b834 --- /dev/null +++ b/go.sum @@ -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= diff --git a/helper.go b/helper.go new file mode 100644 index 0000000..4fb9413 --- /dev/null +++ b/helper.go @@ -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 + } +}