-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathutils.go
144 lines (124 loc) · 3.11 KB
/
utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// general purpose utilities
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io"
"log/slog"
"os"
"reflect"
"sort"
"strings"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
// cannot continue, exit immediately without a stacktrace.
// just use `panic` if you do need a stracktrace.
func fatal() {
fmt.Printf("cannot continue, ") // "cannot continue, exit status 1"
os.Exit(1)
}
// when `b` is true, log error `msg` and die quietly.
func die(b bool, msg string) {
if b {
slog.Error(msg)
fatal()
}
}
// assert `b` is true, otherwise panic with message `msg`.
func ensure(b bool, msg string) {
if !b {
panic(msg)
}
}
// returns `true` if tests are being run.
func is_testing() bool {
// https://stackoverflow.com/questions/14249217/how-do-i-know-im-running-within-go-test
return strings.HasSuffix(os.Args[0], ".test")
}
// "title case" => "Title Case"
// `strings.ToTitle` behaves strangely and isn't safe with unicode.
func title_case(s string) string {
caser := cases.Title(language.English)
return caser.String(s)
}
// returns just the unique items in `list`.
// order is preserved.
func unique[T comparable](list []T) []T {
idx := make(map[T]bool)
var result []T
for _, item := range list {
_, present := idx[item]
if !present {
idx[item] = true
result = append(result, item)
}
}
return result
}
// takes N lists of things `T` and returns a single list of them.
func flatten[T any](tll ...[]T) []T {
final_tl := []T{}
for _, tl := range tll {
final_tl = append(final_tl, tl...)
}
return final_tl
}
// pretty-print a json blob
func quick_json(blob string) string {
// convert into a simple map then
var foo map[string]any
json.Unmarshal([]byte(blob), &foo)
b, err := json.MarshalIndent(foo, "", "\t")
if err != nil {
slog.Error("failed to coerce string blob to json", "blob", blob, "error", err)
fatal()
}
return string(b)
}
// pretty-print any `thing`.
func pprint(thing any) {
s, _ := json.MarshalIndent(thing, "", "\t")
fmt.Println(string(s))
}
func path_exists(path string) bool {
_, err := os.Stat(path)
return !errors.Is(err, os.ErrNotExist)
}
// detect if a string has a byte-order mark,
// removing it and returning the remaining bytes if so.
// returns an error if bytes cannot be read.
// - https://stackoverflow.com/questions/21371673/reading-files-with-a-bom-in-go#answer-21375405
func elide_bom(b []byte) ([]byte, error) {
br := bytes.NewReader(b)
r, _, err := br.ReadRune()
if err != nil {
return b, err
}
if r != '\uFEFF' {
br.UnreadRune() // Not a BOM -- put the rune back
}
return io.ReadAll(br)
}
func keys[K comparable, V any](some_map map[K]V) []K {
key_list := []K{}
for key, _ := range some_map {
key_list = append(key_list, key)
}
return key_list
}
// https://stackoverflow.com/questions/36000487/check-for-equality-on-slices-without-order#answer-36001228
func array_sorted_equal(a, b []string) bool {
if len(a) != len(b) {
return false
}
a_copy := make([]string, len(a))
b_copy := make([]string, len(b))
copy(a_copy, a)
copy(b_copy, b)
sort.Strings(a_copy)
sort.Strings(b_copy)
return reflect.DeepEqual(a_copy, b_copy)
}