forked from phR0ze/n
-
Notifications
You must be signed in to change notification settings - Fork 0
/
n.go
237 lines (211 loc) · 7.19 KB
/
n.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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
// Package n provides a set of types with convenience methods for Go akin to rapid
// development languages.
//
// n was created to reduce the friction I had adopting Go as my primary language of choice.
// It does this by reducing the coding verbosity Go normally requires. n types wrap various
// Go types to provide generic convenience methods reminiscent of C#'s Queryable interface,
// removing the need to implement the 'Contains' function, on basic list primitives for the
// millionth time. The intent is at a minimum to have support for YAML primitive scalars
// (Null, String, Integer, Boolean, Float, Timestamp), lists of the scalar types and maps
// of the scalar types with reflection based fallbacks for un-optimized types. I'll be
// using the terms 'n types' or 'queryable' interchangeably in the documentation and
// examples.
//
// # Conventions used across n types and pkgs
//
// • In order to deal with Golang's decision to not support function overloading or special
// characters in their function names n makes use of a variety of prefix/suffix capital
// letters to indicate different function variations. The function/method that contains no
// suffix is referred to as the base function/method.
//
// • Function names suffixed with 'A' indicates the function is a variation to the function
// without the 'A' but either accepts a string as input or returns a string.
//
// • Function names suffixed with 'E' indicates the function is a variation to the function
// without the 'E' but returns an Error while the base function does not.
//
// • Function names suffixed with 'M' indicates the function is a variation to the function
// without the 'M' but modifies the n type directly rather than a copy.
//
// • Function names suffixed with 'R' indicates the function is a variation to the function
// without the 'R' but reverses the order of operations.
//
// • Function names suffixed with 'S' indicates the function is a variation to the function
// without the 'S' but either accepts a ISlice as input or returns a ISlice.
//
// • Function names suffixed with 'V' indicates the function is a variation to the function
// • Function names suffixed with 'V' indicates the function is a variation to the function
// without the 'V' but accepts variadic input.
//
// • Function names suffixed with 'W' indicates the function is a variation to the function
// without the 'W' but accepts a lambda expression as input.
//
// • Documentation should be thorough and relied upon for guidance as, for a love of
// brevity, some functions are named with a single capital letter only. 'G' is being used
// to export the underlying Go type. 'O' is being used to indicate the interface{} type or
// to export the underlying Go type as an interface{}. 'S' is used to refer to slice types,
// 'M' refers to map types, 'A' refers to string types, 'I' ints types, 'R' rune types and
// combinations may be used to indicate complex types. The documentation will always call
// out what exactly they mean, but the function name may be cryptic until understood.
//
// # Summary of Types
//
// • Char
// • FloatSlice
// • IntSlice
// • InterSlice
// • Object
// • RefSlice
// • Str
// • StringSlice
// • StringMap
package n
import (
"encoding/json"
"os"
yaml "github.com/phR0ze/yaml/v2"
"github.com/pkg/errors"
)
// Misc convenience type/functions
//--------------------------------------------------------------------------------------------------
// O is an alias for interface{} used in lambda expresssions for brevity.
type O interface{}
// Break is a brevity helper for breaking out of lambda loops
var Break = errors.New("break")
// EitherStr returns the first string if not empty else the second
func EitherStr(first, second string) string {
if first != "" {
return first
}
return second
}
// ExB avoids Go's gastly 4 line monstrosity required to implement this providing
// instead a single clean line of code for lambdas.
func ExB(exp bool) bool {
if exp {
return true
}
return false
}
// Range creates slice of the given range of numbers inclusive
func Range(min, max int) []int {
result := make([]int, max-min+1)
for i := range result {
result[i] = min + i
}
return result
}
// SetOnFalseB only updates the result to the 'value' if the exp is false
func SetOnFalseB(result *bool, value, exp bool) bool {
if result != nil {
if !exp {
*result = value
}
return *result
}
return false
}
// SetOnEmpty updates the given result string to the given value if it is empty
func SetOnEmpty(result *string, value string) string {
if result != nil {
if *result == "" {
*result = value
}
return *result
}
return value
}
// SetOnTrueA updates the given result string to the given value if the exp is true
func SetOnTrueA(result *string, value string, exp bool) string {
if result != nil {
if *result == "" {
*result = value
}
return *result
}
return value
}
// SetOnTrueB only updates the result to the 'value' if the exp is true
func SetOnTrueB(result *bool, value, exp bool) bool {
if result != nil {
if exp {
*result = value
}
return *result
}
return false
}
// Load and From helper functions
//--------------------------------------------------------------------------------------------------
// LoadJSON reads in a json file and converts it to a *StringMap
func LoadJSON(filepath string) (m *StringMap) {
m, _ = LoadJSONE(filepath)
return m
}
// LoadJSONE reads in a json file and converts it to a *StringMap
func LoadJSONE(filepath string) (m *StringMap, err error) {
// Read in the yaml file
var data []byte
if data, err = os.ReadFile(filepath); err != nil {
err = errors.Wrapf(err, "failed to read in the yaml file %s", filepath)
return
}
// Unmarshal the json into a *StringMap
buff := map[string]interface{}{}
if err = json.Unmarshal(data, &buff); err != nil {
err = errors.Wrapf(err, "failed to unmarshal json file %s into a *StringMap", filepath)
return
}
m = MV(buff)
return
}
// LoadYAML reads in a yaml file and converts it to a *StringMap
func LoadYAML(filepath string) (m *StringMap) {
m, _ = LoadYAMLE(filepath)
return m
}
// LoadYAMLE reads in a yaml file and converts it to a *StringMap
func LoadYAMLE(filepath string) (m *StringMap, err error) {
m = NewStringMapV()
// Read in the yaml file
var data []byte
if data, err = os.ReadFile(filepath); err != nil {
err = errors.Wrapf(err, "failed to read in the yaml file %s", filepath)
return
}
// Unmarshal the yaml into a *StringMap
m, err = ToStringMapE(data)
return
}
// YAMLCont checks if the given value is a valid YAML container
func YAMLCont(obj interface{}) bool {
o := DeReference(obj)
switch o.(type) {
case map[string]interface{}, StringMap, yaml.MapSlice:
return true
case []interface{}, []string, []int:
return true
default:
return false
}
}
// YAMLMap checks if the given value is map compatible
func YAMLMap(obj interface{}) bool {
o := DeReference(obj)
switch o.(type) {
case map[string]interface{}, StringMap, yaml.MapSlice:
return true
default:
return false
}
}
// YAMLArray checks if the given value is array compatible
func YAMLArray(obj interface{}) bool {
o := DeReference(obj)
switch o.(type) {
case []interface{}, []string, []int:
return true
default:
return false
}
}