mirror of
https://github.com/taigrr/wtf
synced 2025-01-18 04:03:14 -08:00
* Upgrade godo to latest * Fix a bunch of issues found by * Running staticcheck on a codebase for the first time is a sobering experience * go mod tidy * More static improvements Signed-off-by: Chris Cummer <chriscummer@me.com>
224 lines
5.3 KiB
Go
224 lines
5.3 KiB
Go
package utils
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"os/exec"
|
|
"regexp"
|
|
"runtime"
|
|
"strings"
|
|
|
|
"github.com/logrusorgru/aurora"
|
|
"github.com/olebedev/config"
|
|
)
|
|
|
|
const (
|
|
SimpleDateFormat = "Jan 2"
|
|
SimpleTimeFormat = "15:04 MST"
|
|
MinimumTimeFormat12 = "3:04 PM"
|
|
MinimumTimeFormat24 = "15:04"
|
|
|
|
FullDateFormat = "Monday, Jan 2"
|
|
FriendlyDateFormat = "Mon, Jan 2"
|
|
FriendlyDateTimeFormat = "Mon, Jan 2, 15:04"
|
|
|
|
TimestampFormat = "2006-01-02T15:04:05-0700"
|
|
)
|
|
|
|
// DoesNotInclude takes a slice of strings and a target string and returns
|
|
// TRUE if the slice does not include the target, FALSE if it does
|
|
//
|
|
// Example:
|
|
//
|
|
// x := DoesNotInclude([]string{"cat", "dog", "rat"}, "dog")
|
|
// > false
|
|
//
|
|
// x := DoesNotInclude([]string{"cat", "dog", "rat"}, "pig")
|
|
// > true
|
|
//
|
|
func DoesNotInclude(strs []string, val string) bool {
|
|
return !Includes(strs, val)
|
|
}
|
|
|
|
// ExecuteCommand executes an external command on the local machine as the current user
|
|
func ExecuteCommand(cmd *exec.Cmd) string {
|
|
if cmd == nil {
|
|
return ""
|
|
}
|
|
|
|
buf := &bytes.Buffer{}
|
|
cmd.Stdout = buf
|
|
|
|
if err := cmd.Run(); err != nil {
|
|
return err.Error()
|
|
}
|
|
|
|
return buf.String()
|
|
}
|
|
|
|
// FindMatch takes a regex pattern and a string of data and returns back all the matches
|
|
// in that string
|
|
func FindMatch(pattern string, data string) [][]string {
|
|
r := regexp.MustCompile(pattern)
|
|
return r.FindAllStringSubmatch(data, -1)
|
|
}
|
|
|
|
// Includes takes a slice of strings and a target string and returns
|
|
// TRUE if the slice includes the target, FALSE if it does not
|
|
//
|
|
// Example:
|
|
//
|
|
// x := Includes([]string{"cat", "dog", "rat"}, "dog")
|
|
// > true
|
|
//
|
|
// x := Includes([]string{"cat", "dog", "rat"}, "pig")
|
|
// > false
|
|
//
|
|
func Includes(strs []string, val string) bool {
|
|
for _, str := range strs {
|
|
if val == str {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
// OpenFile opens the file defined in `path` via the operating system
|
|
func OpenFile(path string) {
|
|
if (strings.HasPrefix(path, "http://")) || (strings.HasPrefix(path, "https://")) {
|
|
if len(OpenUrlUtil) > 0 {
|
|
commands := append(OpenUrlUtil, path)
|
|
exec.Command(commands[0], commands[1:]...).Start()
|
|
return
|
|
}
|
|
|
|
switch runtime.GOOS {
|
|
case "linux":
|
|
exec.Command("xdg-open", path).Start()
|
|
case "windows":
|
|
exec.Command("rundll32", "url.dll,FileProtocolHandler", path).Start()
|
|
case "darwin":
|
|
exec.Command("open", path).Start()
|
|
default:
|
|
// for the BSDs
|
|
exec.Command("xdg-open", path).Start()
|
|
}
|
|
} else {
|
|
filePath, _ := ExpandHomeDir(path)
|
|
cmd := exec.Command(OpenFileUtil, filePath)
|
|
ExecuteCommand(cmd)
|
|
}
|
|
}
|
|
|
|
// 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)
|
|
if err != nil {
|
|
return []byte{}, err
|
|
}
|
|
|
|
return fileData, nil
|
|
}
|
|
|
|
// ParseJSON is a standard JSON reader from text
|
|
func ParseJSON(obj interface{}, text io.Reader) error {
|
|
d := json.NewDecoder(text)
|
|
return d.Decode(obj)
|
|
}
|
|
|
|
// CalculateDimensions reads the module dimensions from the module and global config. The border is already substracted.
|
|
func CalculateDimensions(moduleConfig, globalConfig *config.Config) (int, int, error) {
|
|
grid, err := globalConfig.Get("wtf.grid")
|
|
if err != nil {
|
|
return 0, 0, err
|
|
}
|
|
|
|
cols := ToInts(grid.UList("columns"))
|
|
rows := ToInts(grid.UList("rows"))
|
|
|
|
// If they're defined in the config, they cannot be empty
|
|
if len(cols) == 0 || len(rows) == 0 {
|
|
displayGridConfigError()
|
|
os.Exit(1)
|
|
}
|
|
|
|
// Read the source data from the config
|
|
left := moduleConfig.UInt("position.left", 0)
|
|
top := moduleConfig.UInt("position.top", 0)
|
|
width := moduleConfig.UInt("position.width", 0)
|
|
height := moduleConfig.UInt("position.height", 0)
|
|
|
|
// Make sure the values are in bounds
|
|
left = Clamp(left, 0, len(cols)-1)
|
|
top = Clamp(top, 0, len(rows)-1)
|
|
width = Clamp(width, 0, len(cols)-left)
|
|
height = Clamp(height, 0, len(rows)-top)
|
|
|
|
// Start with the border subtracted and add all the spanned rows and cols
|
|
w, h := -2, -2
|
|
for _, x := range cols[left : left+width] {
|
|
w += x
|
|
}
|
|
for _, y := range rows[top : top+height] {
|
|
h += y
|
|
}
|
|
|
|
// The usable space may be empty
|
|
w = MaxInt(w, 0)
|
|
h = MaxInt(h, 0)
|
|
|
|
return w, h, nil
|
|
}
|
|
|
|
// MaxInt returns the larger of x or y
|
|
//
|
|
// Examples:
|
|
//
|
|
// MaxInt(3, 2) => 3
|
|
// MaxInt(2, 3) => 3
|
|
//
|
|
func MaxInt(x, y int) int {
|
|
if x > y {
|
|
return x
|
|
}
|
|
return y
|
|
}
|
|
|
|
// Clamp restricts values to a minimum and maximum value
|
|
//
|
|
// Examples:
|
|
//
|
|
// clamp(6, 3, 8) => 4
|
|
// clamp(1, 3, 8) => 3
|
|
// clamp(9, 3, 8) => 8
|
|
//
|
|
func Clamp(x, a, b int) int {
|
|
if a > x {
|
|
return a
|
|
}
|
|
if b < x {
|
|
return b
|
|
}
|
|
return x
|
|
}
|
|
|
|
/* -------------------- Unexported Functions -------------------- */
|
|
|
|
func displayGridConfigError() {
|
|
fmt.Printf("\n%s 'grid' config values are invalid. 'columns' and 'rows' cannot be empty.\n", aurora.Red("ERROR"))
|
|
fmt.Println()
|
|
fmt.Println("This is invalid:")
|
|
fmt.Println()
|
|
fmt.Println(" grid:")
|
|
fmt.Println(" columns: []")
|
|
fmt.Println(" rows: []")
|
|
fmt.Println()
|
|
fmt.Printf("%s If you want the columns and rows to be dynamically-determined, remove the 'grid' key and child keys from your config file.\n", aurora.Yellow("*"))
|
|
fmt.Printf("%s If you want explicit widths and heights, add integer values to the 'columns' and 'rows' arrays.\n", aurora.Yellow("*"))
|
|
fmt.Println()
|
|
}
|