// Package monutil contains functions for file operations used with dashman and alertman package monutil import ( "bufio" "errors" "fmt" "io/fs" "log" "os" "os/exec" "path/filepath" "regexp" "runtime" "strconv" "strings" gptext "github.com/jedib0t/go-pretty/v6/text" ) const ( foremanRegex = `ret-edge-(dev|stage|prod)\d-foreman(-\w+|)` ) // TODO: these functions were copeied from dashboardmanager.go . dashman should // be updated to use these functions // FileExists checks if file exists. func FileExists(filePath string) bool { if _, err := os.Stat(filePath); err != nil && os.IsNotExist(err) { return false } return true } // determines if a file represented by `path` is a directory or not func IsDirectory(path string) bool { fileInfo, err := os.Stat(path) if err != nil { return false } return fileInfo.IsDir() } // FilterString returns a filtered string func FilterString(filter string, str string) string { r := regexp.MustCompile(filter) var submatches []string matches := r.FindAllStringSubmatch(str, -1) for _, v := range matches { submatches = append(submatches, strings.Join(v, "")) } return strings.Join(submatches, "") } // ReconcileFileNames creates a new iterated duplicate file name. func ReconcileFileNames(names []string) []string { var fileNames []string for _, n := range names { if !InStrList(n, fileNames) { fileNames = append(fileNames, n) continue } // find the next available iterator for the duplicate name iter := getNextIter(fileNames, n+`_(Duplicate_Name_([\d]+)).json`) fileNames = append(fileNames, n+"_(Duplicate_Name_"+strconv.Itoa(iter)+").json") } return fileNames } // returns a slice of strings from the second slice that are not present in the first slice func DiffStrSlice(s1 []string, s2 []string) []string { var diff []string if InSlice(s1, s2) { // slice values already in s1 return diff } for _, v := range s2 { if !InStrList(v, s1) { diff = append(diff, v) } } return diff } // removes the second slice of strings from the first slice of strings, returns (s1 - s2) func RemoveStrSlice(s1 []string, s2 []string) []string { remSlice := s1 for i := 0; i < len(s2); i++ { remSlice = RemoveString(s2[i], remSlice) } return remSlice } // removes specified string from a slice of strings func RemoveString(str string, slice []string) []string { if !InStrList(str, slice) { return slice } var newSlice []string for i := 0; i < len(slice); i++ { if slice[i] != str { newSlice = append(newSlice, slice[i]) } } return newSlice } // InList searches slice for string func InStrList(s string, l []string) bool { for i := 0; i < len(l); i++ { if l[i] == s { return true } } return false } // inIntList searches slice for int func InIntList(s int, l []int) bool { for i := 0; i < len(l); i++ { if l[i] == s { return true } } return false } // getNextIter returns the next available iterator based on a specified filter. // It's used to add iterators to a rename operation to prevent duplicates func getNextIter(list []string, filter string) int { var iter []int for _, l := range list { r, _ := regexp.Compile(l + filter) match := r.FindStringSubmatch(l) if len(match) != 2 { continue } mi, _ := strconv.Atoi(match[1]) iter = append(iter, mi) } // return next unused iterator var newIter = 1 for i := 0; i < len(iter); i++ { if InIntList(newIter, iter) { newIter++ } } return newIter } // checks if the second slice is contained within the first slice func InSlice(s1 []string, s2 []string) bool { // larger slice cannot be contained in a smaller slice if len(s1) < len(s2) { return false } // empty slice should not be considered a part of a slice with values if len(s2) == 0 && !(len(s1) == 0) { return false } // check that each value in slice 2 is present in slice 1 for _, v := range s2 { if !InStrList(v, s1) { return false } } return true } // checks if the provided string contains a project ID that matches the foreman regex func IsForeman(p string) bool { r := regexp.MustCompile(foremanRegex) return r.MatchString(p) } // determines if the provided string is a list func IsList(s string) bool { return len(strings.Split(s, ",")) > 1 } // returns slice of strings that don't match between two slices func CompareStrSlice(a1 []string, a2 []string) []string { return append(DiffStrSlice(a1, a2), DiffStrSlice(a2, a1)...) } // returns a mapped object of named regex func RegexNamedMatches(regex *regexp.Regexp, str string) map[string]string { match := regex.FindStringSubmatch(str) results := map[string]string{} for i, name := range match { results[regex.SubexpNames()[i]] = name } return results } // returns a mapped string for a given string slice func SliceToMap(s []string) map[string]string { m := make(map[string]string) for i := 0; i < len(s); i++ { m[s[i]] = "" } return m } // returns a mapped string slice func MapSlice(strSlice []string) *map[string][]string { strMap := make(map[string][]string) for i := 0; i < len(strSlice); i++ { strMap[strSlice[i]] = []string{} } return &strMap } // returns slice of keys of labelMap func Keys(m map[string]string) []string { var keys []string for k := range m { keys = append(keys, k) } return keys } // Parse the rune as a bool. // Characters y or Y return true, n or N return false. // Everything else returns an error. func parsePrompt(s string) (bool, error) { switch strings.ToLower(s) { case "y": return true, nil case "n": return false, nil case "c": return false, errors.New("cancel") } return false, errors.New("invalid keypress response") } // provides a yes/no/cancel prompt for the provided string func PromptYesNoCancel(s string) bool { reader := bufio.NewReader(os.Stdin) yes := White("[") + Magenta("Y") + White("es/") no := Magenta("N") + White("o/") cancel := Magenta("C") + White("ancel] : ") fmt.Printf("%s %s%s%s", White(s), yes, no, cancel) b := make([]byte, 1) _, err := reader.Read(b) if err != nil { log.Fatal(err) } r, err := parsePrompt(string(b[0])) if err != nil { defer os.Exit(0) } return r } func runCmd(name string, arg ...string) { cmd := exec.Command(name, arg...) cmd.Stdout = os.Stdout if err := cmd.Run(); err != nil { fmt.Println(err.Error()) } } // returns a list of filepaths for a given file suffix found in a directory func ListFiles(rootPath string, fileSuffix string) ([]string, error) { var files []string if err := filepath.WalkDir(rootPath, func(path string, d fs.DirEntry, err error) error { if err != nil { return err } if !d.IsDir() && strings.HasSuffix(path, fileSuffix) { files = append(files, path) } return nil }); err != nil { return nil, err } return files, nil } // clears the terminal screen func ClearTerminal() { switch runtime.GOOS { case "darwin": runCmd("clear") case "linux": runCmd("clear") case "windows": runCmd("cmd", "/c", "cls") default: runCmd("clear") } } func Bold(v interface{}) string { return gptext.Bold.Sprint(v) } func Italic(v interface{}) string { return gptext.Italic.Sprint(v) } func Underline(v interface{}) string { return gptext.Underline.Sprint(v) } func Red(v interface{}) string { return gptext.FgHiRed.Sprint(v) } func Blue(v interface{}) string { return gptext.FgBlue.Sprint(v) } func Green(v interface{}) string { return gptext.FgHiGreen.Sprint(v) } func Yellow(v interface{}) string { return gptext.FgHiYellow.Sprint(v) } func Cyan(v interface{}) string { return gptext.FgHiCyan.Sprint(v) } func White(v interface{}) string { return gptext.FgHiWhite.Sprint(v) } func Magenta(v interface{}) string { return gptext.FgHiMagenta.Sprint(v) } // Get the values from a map // func Values(items map[string]string) []string { // var values []string // for _, v := range items { // values = append(values, v) // } // return values // } // Splits a word string with format as key:value or only keys // to a slice. Then turns the slice into a map. // If string is only keys, the values in the map with be empty strings. func StringToMap(str string) map[string]string { labelsMap := make(map[string]string) splitString := regexp.MustCompile(`[\\,]+`).Split(str, -1) for i := 0; i < len(splitString); i++ { splitString[i] = strings.TrimSpace(splitString[i]) } for i := 0; i < len(splitString); i++ { if strings.Contains(splitString[i], ":") { substr := regexp.MustCompile(`[\\:]+`).Split(splitString[i], -1) labelsMap[substr[0]] = substr[1] continue } labelsMap[splitString[i]] = "" } return labelsMap } // Check if a string array has duplicates func ContainsDuplicates(s []string) bool { var l []string for i := 0; i < len(s); i++ { if InStrList(s[i], l) { return true } l = append(l, s[i]) } return false }