diff --git a/utils/conversions.go b/utils/conversions.go new file mode 100644 index 00000000..f8b1542b --- /dev/null +++ b/utils/conversions.go @@ -0,0 +1,47 @@ +package utils + +import ( + "strconv" +) + +/* -------------------- Map Conversion -------------------- */ + +// MapToStrs takes a map of interfaces and returns a map of strings +func MapToStrs(aMap map[string]interface{}) map[string]string { + results := make(map[string]string) + + for key, val := range aMap { + results[key] = val.(string) + } + + return results +} + +/* -------------------- Slice Conversion -------------------- */ + +// ToInts takes a slice of interfaces and returns a slice of ints +func ToInts(slice []interface{}) []int { + results := []int{} + + for _, val := range slice { + results = append(results, val.(int)) + } + + return results +} + +// ToStrs takes a slice of interfaces and returns a slice of strings +func ToStrs(slice []interface{}) []string { + results := []string{} + + for _, val := range slice { + switch val.(type) { + case int: + results = append(results, strconv.Itoa(val.(int))) + case string: + results = append(results, val.(string)) + } + } + + return results +} diff --git a/utils/conversions_test.go b/utils/conversions_test.go new file mode 100644 index 00000000..ab27a76a --- /dev/null +++ b/utils/conversions_test.go @@ -0,0 +1,44 @@ +package utils + +import ( + "testing" + + . "github.com/stretchr/testify/assert" +) + +func Test_MapToStrs(t *testing.T) { + expected := map[string]string{ + "a": "a", + "b": "b", + "c": "c", + } + + source := make(map[string]interface{}) + for _, val := range expected { + source[val] = val + } + + Equal(t, expected, MapToStrs(source)) +} + +func Test_ToInts(t *testing.T) { + expected := []int{1, 2, 3} + + source := make([]interface{}, len(expected)) + for idx, val := range expected { + source[idx] = val + } + + Equal(t, expected, ToInts(source)) +} + +func Test_ToStrs(t *testing.T) { + expected := []string{"cat", "dog", "rat"} + + source := make([]interface{}, len(expected)) + for idx, val := range expected { + source[idx] = val + } + + Equal(t, expected, ToStrs(source)) +} diff --git a/utils/email_addresses.go b/utils/email_addresses.go new file mode 100644 index 00000000..e6273f63 --- /dev/null +++ b/utils/email_addresses.go @@ -0,0 +1,35 @@ +package utils + +import ( + "strings" +) + +// NameFromEmail takes an email address and returns the part that comes before the @ symbol +// +// Example: +// +// NameFromEmail("test_user@example.com") +// > "Test_user" +// +func NameFromEmail(email string) string { + parts := strings.Split(email, "@") + return strings.Title(strings.Replace(parts[0], ".", " ", -1)) +} + +// NamesFromEmails takes a slice of email addresses and returns a slice of the parts that +// come before the @ symbol +// +// Example: +// +// NamesFromEmail("test_user@example.com", "other_user@example.com") +// > []string{"Test_user", "Other_user"} +// +func NamesFromEmails(emails []string) []string { + names := []string{} + + for _, email := range emails { + names = append(names, NameFromEmail(email)) + } + + return names +} diff --git a/utils/email_addresses_test.go b/utils/email_addresses_test.go new file mode 100644 index 00000000..6076c3a3 --- /dev/null +++ b/utils/email_addresses_test.go @@ -0,0 +1,22 @@ +package utils + +import ( + "testing" + + . "github.com/stretchr/testify/assert" +) + +func Test_NameFromEmail(t *testing.T) { + Equal(t, "", NameFromEmail("")) + Equal(t, "Chris Cummer", NameFromEmail("chris.cummer@me.com")) +} + +func Test_NamesFromEmails(t *testing.T) { + var result []string + + result = NamesFromEmails([]string{}) + Equal(t, []string{}, result) + + result = NamesFromEmails([]string{"chris.cummer@me.com", "chriscummer@me.com"}) + Equal(t, []string{"Chris Cummer", "Chriscummer"}, result) +} diff --git a/utils/help_parser.go b/utils/help_parser.go index 7ff0b12e..9f713f04 100644 --- a/utils/help_parser.go +++ b/utils/help_parser.go @@ -10,47 +10,7 @@ import ( "github.com/wtfutil/wtf/cfg" ) -func lowercaseTitle(title string) string { - if title == "" { - return "" - } - r, n := utf8.DecodeRuneInString(title) - return string(unicode.ToLower(r)) + title[n:] -} - -var ( - openColorRegex = regexp.MustCompile(`\[.*?\]`) -) - -func StripColorTags(input string) string { - return openColorRegex.ReplaceAllString(input, "") -} - -func helpFromValue(field reflect.StructField) string { - result := "" - - optional, err := strconv.ParseBool(field.Tag.Get("optional")) - if err != nil { - optional = false - } - - help := field.Tag.Get("help") - if optional { - help = "Optional " + help - } - - values := field.Tag.Get("values") - if help != "" { - result += "\n\n " + lowercaseTitle(field.Name) - result += "\n " + help - - if values != "" { - result += "\n Values: " + values - } - } - - return result -} +/* -------------------- Exported Functions -------------------- */ func HelpFromInterface(item interface{}) string { result := "" @@ -78,3 +38,45 @@ func HelpFromInterface(item interface{}) string { return result } + +// StripColorTags removes tcell color tags from a given string +func StripColorTags(input string) string { + openColorRegex := regexp.MustCompile(`\[.*?\]`) + return openColorRegex.ReplaceAllString(input, "") +} + +/* -------------------- Unexported Functions -------------------- */ + +func helpFromValue(field reflect.StructField) string { + result := "" + + optional, err := strconv.ParseBool(field.Tag.Get("optional")) + if err != nil { + optional = false + } + + help := field.Tag.Get("help") + if optional { + help = "Optional " + help + } + + values := field.Tag.Get("values") + if help != "" { + result += "\n\n " + lowercaseTitle(field.Name) + result += "\n " + help + + if values != "" { + result += "\n Values: " + values + } + } + + return result +} + +func lowercaseTitle(title string) string { + if title == "" { + return "" + } + r, n := utf8.DecodeRuneInString(title) + return string(unicode.ToLower(r)) + title[n:] +} diff --git a/utils/homedir.go b/utils/homedir.go index 7c208de7..9a67a7b0 100644 --- a/utils/homedir.go +++ b/utils/homedir.go @@ -10,21 +10,7 @@ import ( "path/filepath" ) -// Dir returns the home directory for the executing user. -// An error is returned if a home directory cannot be detected. -func Home() (string, error) { - currentUser, err := user.Current() - if err != nil { - return "", err - } - if currentUser.HomeDir == "" { - return "", errors.New("cannot find user-specific home dir") - } - - return currentUser.HomeDir, nil -} - -// Expand expands the path to include the home directory if the path +// ExpandHomeDir expands the path to include the home directory if the path // is prefixed with `~`. If it isn't prefixed with `~`, the path is // returned as-is. func ExpandHomeDir(path string) (string, error) { @@ -47,3 +33,18 @@ func ExpandHomeDir(path string) (string, error) { return filepath.Join(dir, path[1:]), nil } + +// Home returns the home directory for the executing user. +// An error is returned if a home directory cannot be detected. +func Home() (string, error) { + currentUser, err := user.Current() + if err != nil { + return "", err + } + + if currentUser.HomeDir == "" { + return "", errors.New("cannot find user-specific home dir") + } + + return currentUser.HomeDir, nil +} diff --git a/utils/init.go b/utils/init.go index 86267333..7ee5b65e 100644 --- a/utils/init.go +++ b/utils/init.go @@ -1,5 +1,6 @@ package utils +// OpenFileUtil defines the system utility to use to open files var OpenFileUtil = "open" // Init initializes global settings in the wtf package diff --git a/utils/init_test.go b/utils/init_test.go new file mode 100644 index 00000000..853654ac --- /dev/null +++ b/utils/init_test.go @@ -0,0 +1,12 @@ +package utils + +import ( + "testing" + + . "github.com/stretchr/testify/assert" +) + +func Test_Init(t *testing.T) { + Init("cats") + Equal(t, OpenFileUtil, "cats") +} diff --git a/utils/text.go b/utils/text.go new file mode 100644 index 00000000..1e986e77 --- /dev/null +++ b/utils/text.go @@ -0,0 +1,48 @@ +package utils + +import ( + "fmt" + "strings" + + "github.com/rivo/tview" +) + +// CenterText takes a string and a width and pads the left and right of the string with +// empty spaces to ensure that the string is in the middle of the returned value +// +// Example: +// +// x := CenterText("cat", 11) +// > " cat " +// +func CenterText(str string, width int) string { + if width < 0 { + width = 0 + } + + return fmt.Sprintf("%[1]*s", -width, fmt.Sprintf("%[1]*s", (width+len(str))/2, str)) +} + +// HighlightableHelper pads the given text with blank spaces to the width of the view +// containing it. This is helpful for extending row highlighting across the entire width +// of the view +func HighlightableHelper(view *tview.TextView, input string, idx, offset int) string { + fmtStr := fmt.Sprintf(`["%d"][""]`, idx) + _, _, w, _ := view.GetInnerRect() + fmtStr += input + fmtStr += PadRow(offset, w+1) + fmtStr += `[""]` + "\n" + return fmtStr +} + +// PadRow returns a padding for a row to make it the full width of the containing widget. +// Useful for ensurig row highlighting spans the full width (I suspect tcell has a better +// way to do this, but I haven't yet found it) +func PadRow(offset int, max int) string { + padSize := max - offset + if padSize < 0 { + padSize = 0 + } + + return strings.Repeat(" ", padSize) +} diff --git a/utils/text_test.go b/utils/text_test.go new file mode 100644 index 00000000..e26baeec --- /dev/null +++ b/utils/text_test.go @@ -0,0 +1,19 @@ +package utils + +import ( + "testing" + + . "github.com/stretchr/testify/assert" +) + +func Test_CenterText(t *testing.T) { + Equal(t, "cat", CenterText("cat", -9)) + Equal(t, "cat", CenterText("cat", 0)) + Equal(t, " cat ", CenterText("cat", 9)) +} + +func Test_PadRow(t *testing.T) { + Equal(t, "", PadRow(0, 0)) + Equal(t, "", PadRow(5, 2)) + Equal(t, " ", PadRow(1, 2)) +} diff --git a/utils/utils.go b/utils/utils.go index 5adda457..52969c70 100644 --- a/utils/utils.go +++ b/utils/utils.go @@ -6,10 +6,7 @@ import ( "os/exec" "regexp" "runtime" - "strconv" "strings" - - "github.com/rivo/tview" ) const ( @@ -24,22 +21,6 @@ const ( TimestampFormat = "2006-01-02T15:04:05-0700" ) -// CenterText takes a string and a width and pads the left and right of the string with -// empty spaces to ensure that the string is in the middle of the returned value -// -// Example: -// -// x := CenterText("cat", 11) -// > " cat " -// -func CenterText(str string, width int) string { - if width < 0 { - width = 0 - } - - return fmt.Sprintf("%[1]*s", -width, fmt.Sprintf("%[1]*s", (width+len(str))/2, str)) -} - // ExecuteCommand executes an external command on the local machine as the current user func ExecuteCommand(cmd *exec.Cmd) string { if cmd == nil { @@ -92,36 +73,6 @@ func FindMatch(pattern string, data string) [][]string { return r.FindAllStringSubmatch(data, -1) } -// NameFromEmail takes an email address and returns the part that comes before the @ symbol -// -// Example: -// -// NameFromEmail("test_user@example.com") -// > "Test_user" -// -func NameFromEmail(email string) string { - parts := strings.Split(email, "@") - return strings.Title(strings.Replace(parts[0], ".", " ", -1)) -} - -// NamesFromEmails takes a slice of email addresses and returns a slice of the parts that -// come before the @ symbol -// -// Example: -// -// NamesFromEmail("test_user@example.com", "other_user@example.com") -// > []string{"Test_user", "Other_user"} -// -func NamesFromEmails(emails []string) []string { - names := []string{} - - for _, email := range emails { - names = append(names, NameFromEmail(email)) - } - - return names -} - // OpenFile opens the file defined in `path` via the operating system func OpenFile(path string) { if (strings.HasPrefix(path, "http://")) || (strings.HasPrefix(path, "https://")) { @@ -141,18 +92,6 @@ func OpenFile(path string) { } } -// PadRow returns a padding for a row to make it the full width of the containing widget. -// Useful for ensurig row highlighting spans the full width (I suspect tcell has a better -// way to do this, but I haven't yet found it) -func PadRow(offset int, max int) string { - padSize := max - offset - if padSize < 0 { - padSize = 0 - } - - return strings.Repeat(" ", padSize) -} - // ReadFileBytes reads the contents of a file and returns those contents as a slice of bytes func ReadFileBytes(filePath string) ([]byte, error) { fileData, err := ioutil.ReadFile(filePath) @@ -162,54 +101,3 @@ func ReadFileBytes(filePath string) ([]byte, error) { return fileData, nil } - -/* -------------------- Map Conversion -------------------- */ - -// MapToStrs takes a map of interfaces and returns a map of strings -func MapToStrs(aMap map[string]interface{}) map[string]string { - results := make(map[string]string) - - for key, val := range aMap { - results[key] = val.(string) - } - - return results -} - -/* -------------------- Slice Conversion -------------------- */ - -// ToInts takes a slice of interfaces and returns a slice of ints -func ToInts(slice []interface{}) []int { - results := []int{} - - for _, val := range slice { - results = append(results, val.(int)) - } - - return results -} - -// ToStrs takes a slice of interfaces and returns a slice of strings -func ToStrs(slice []interface{}) []string { - results := []string{} - - for _, val := range slice { - switch val.(type) { - case int: - results = append(results, strconv.Itoa(val.(int))) - case string: - results = append(results, val.(string)) - } - } - - return results -} - -func HighlightableHelper(view *tview.TextView, input string, idx, offset int) string { - fmtStr := fmt.Sprintf(`["%d"][""]`, idx) - _, _, w, _ := view.GetInnerRect() - fmtStr += input - fmtStr += PadRow(offset, w+1) - fmtStr += `[""]` + "\n" - return fmtStr -} diff --git a/utils/utils_test.go b/utils/utils_test.go index 208583e0..50446827 100644 --- a/utils/utils_test.go +++ b/utils/utils_test.go @@ -7,17 +7,6 @@ import ( . "github.com/stretchr/testify/assert" ) -func Test_Init(t *testing.T) { - Init("cats") - Equal(t, OpenFileUtil, "cats") -} - -func Test_CenterText(t *testing.T) { - Equal(t, "cat", CenterText("cat", -9)) - Equal(t, "cat", CenterText("cat", 0)) - Equal(t, " cat ", CenterText("cat", 9)) -} - func Test_ExecuteCommand(t *testing.T) { tests := []struct { name string @@ -59,61 +48,3 @@ func Test_ExcludeWhenTrue(t *testing.T) { Equal(t, true, Exclude([]string{"cat", "dog", "rat"}, "bat")) Equal(t, false, Exclude([]string{"cat", "dog", "rat"}, "dog")) } - -func Test_NameFromEmail(t *testing.T) { - Equal(t, "", NameFromEmail("")) - Equal(t, "Chris Cummer", NameFromEmail("chris.cummer@me.com")) -} - -func Test_NamesFromEmails(t *testing.T) { - var result []string - - result = NamesFromEmails([]string{}) - Equal(t, []string{}, result) - - result = NamesFromEmails([]string{"chris.cummer@me.com", "chriscummer@me.com"}) - Equal(t, []string{"Chris Cummer", "Chriscummer"}, result) -} - -func Test_PadRow(t *testing.T) { - Equal(t, "", PadRow(0, 0)) - Equal(t, "", PadRow(5, 2)) - Equal(t, " ", PadRow(1, 2)) -} - -func Test_MapToStrs(t *testing.T) { - expected := map[string]string{ - "a": "a", - "b": "b", - "c": "c", - } - - source := make(map[string]interface{}) - for _, val := range expected { - source[val] = val - } - - Equal(t, expected, MapToStrs(source)) -} - -func Test_ToInts(t *testing.T) { - expected := []int{1, 2, 3} - - source := make([]interface{}, len(expected)) - for idx, val := range expected { - source[idx] = val - } - - Equal(t, expected, ToInts(source)) -} - -func Test_ToStrs(t *testing.T) { - expected := []string{"cat", "dog", "rat"} - - source := make([]interface{}, len(expected)) - for idx, val := range expected { - source[idx] = val - } - - Equal(t, expected, ToStrs(source)) -}