summaryrefslogtreecommitdiff
path: root/util.go
diff options
context:
space:
mode:
authorGeorge Abbott <george@gabbott.dev>2023-11-01 22:31:09 +0000
committerGeorge Abbott <george@gabbott.dev>2023-11-01 22:31:09 +0000
commitd7ba131e756057307499fb2c3349c43e8e2fd38c (patch)
tree4d040e821f32f0c5fb3a4a7eadf57a4eeae5667c /util.go
Commit allHEADmaster
Diffstat (limited to 'util.go')
-rw-r--r--util.go175
1 files changed, 175 insertions, 0 deletions
diff --git a/util.go b/util.go
new file mode 100644
index 0000000..3db88f0
--- /dev/null
+++ b/util.go
@@ -0,0 +1,175 @@
+package saggytrousers
+
+import (
+ "fmt"
+ "strings"
+ "errors"
+ "reflect"
+ "math"
+ "strconv"
+)
+
+// Given a map, return all the values as an array.
+func GetMapValues[K comparable, V any](m map[K]V) []V {
+ vs := []V{}
+ for _, v := range m {
+ vs = append(vs, v)
+ }
+ return vs
+}
+
+// Check if a given value is found in a slice.
+func InSlice[T comparable](s []T, v T) bool {
+ for _, sv := range s {
+ if sv == v {
+ return true
+ }
+ }
+ return false
+}
+
+// A concrete instantiation for any since any does not implement comparable.
+func InSliceAny(s []any, v any) bool {
+ for _, sv := range s {
+ if sv == v {
+ return true
+ }
+ }
+ return false
+}
+func SliceEq[T comparable](fst, snd []T) bool {
+ if len(fst) != len(snd) {
+ return false
+ }
+
+ for i := 0; i < len(fst); i++ {
+ if fst[i] != snd[i] {
+ return false
+ }
+ }
+ return true
+}
+
+
+// Create a filepath, consisting of a directory, a subdirectory within that
+// called the `saveDir`, a filename which may contain an instance of `%s`, and
+// a key which replaces the %s in the filename.
+func CreateFilepath(dir, saveDir, filename, key string) string {
+ name := CreateFilename(filename, key)
+ // Add directory
+ ret := fmt.Sprintf("%s/%s/%s", dir, saveDir, name)
+ return ret
+}
+
+func CreateFilename(filename, key string) string {
+ name := fmt.Sprintf(filename, key)
+
+ // Remove illegal characters
+ name = strings.TrimRight(name, "\n")
+ name = strings.ReplaceAll(name, " ", "-")
+ name = strings.ReplaceAll(name, "/", "-")
+ name = strings.ReplaceAll(name, "\\", "-")
+ name = strings.ReplaceAll(name, ":", "-")
+ name = strings.ReplaceAll(name, ";", "-")
+ name = strings.ReplaceAll(name, "!", "-")
+ name = strings.ReplaceAll(name, "?", "-")
+
+ return name
+}
+
+// Take input of CreateFilename or something similar, and trim it to 20 for
+// making a sheet name.
+func MakeValidSheetName(s string) string {
+ var max int
+ if len(s) < 20 {
+ max = len(s)
+ } else {
+ max = 20
+ }
+ return s[0:max]
+}
+
+// Downcasts a []any to a []T. Returns error if any fail.
+func Downcast[T any](vs []any) ([]T, error) {
+ var ret []T
+ for i, v := range vs {
+ cv, ok := v.(T)
+ if !ok {
+ errmesg := fmt.Sprintf("Downcast: failed conv at idx %v for value %v type %T", i, v, v)
+ return ret, errors.New(errmesg)
+ }
+ ret = append(ret, cv)
+ }
+ return ret, nil
+}
+
+func DowncastF64(vs []any) ([]float64, error) {
+ var ret []float64
+ for i, v := range vs {
+ cv, err := ConvertF64(v)
+ if err != nil {
+ errmesg := fmt.Sprintf("Downcast: failed conv at idx %v for value %v type %T", i, v, v)
+ return ret, errors.New(errmesg)
+ }
+ ret = append(ret, cv)
+ }
+ return ret, nil
+}
+
+// A conversion function to f64 from any. Why this isn't in the standard god
+// knows. From StackOverflow question 20767724.
+var floatType = reflect.TypeOf(float64(0))
+var stringType = reflect.TypeOf("")
+func ConvertF64(unkn any) (float64, error) {
+ switch i := unkn.(type) {
+ case float64:
+ return i, nil
+ case float32:
+ return float64(i), nil
+ case int64:
+ return float64(i), nil
+ case int32:
+ return float64(i), nil
+ case int:
+ return float64(i), nil
+ case uint64:
+ return float64(i), nil
+ case uint32:
+ return float64(i), nil
+ case uint:
+ return float64(i), nil
+ case string:
+ return strconv.ParseFloat(i, 64)
+ default:
+ v := reflect.ValueOf(unkn)
+ v = reflect.Indirect(v)
+ if v.Type().ConvertibleTo(floatType) {
+ fv := v.Convert(floatType)
+ return fv.Float(), nil
+ } else if v.Type().ConvertibleTo(stringType) {
+ sv := v.Convert(stringType)
+ s := sv.String()
+ return strconv.ParseFloat(s, 64)
+ } else {
+ return math.NaN(), fmt.Errorf("Can't convert %v to float64", v.Type())
+ }
+ }
+}
+
+// Get a given _column_ from a set of rows. This is used to get columns from
+// GetRows, which is potentially faster than calling GetColumns.
+func TransmogrifyRows(rows [][]any, index int) []any {
+ var ret []any
+ for _, row := range rows {
+ ret = append(ret, row[index])
+ }
+ return ret
+}
+
+// Process a string, removing spaces, making all lowercase, etc. so that we get
+// easily `switch`-able strings.
+func ProcessStr(str string) string {
+ ret := strings.TrimSuffix(str, " ")
+ ret = strings.ToLower(str)
+ return ret
+}