@voxgig/sdkgen 0.26.1 → 0.28.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (157) hide show
  1. package/bin/voxgig-sdkgen +1 -1
  2. package/dist/cmp/Feature.js +11 -9
  3. package/dist/cmp/Feature.js.map +1 -1
  4. package/dist/cmp/Main.js +6 -2
  5. package/dist/cmp/Main.js.map +1 -1
  6. package/model/sdkgen.jsonic +1 -1
  7. package/package.json +3 -3
  8. package/project/.sdk/model/feature/log.jsonic +1 -1
  9. package/project/.sdk/model/feature/test.jsonic +1 -1
  10. package/project/.sdk/model/target/go.jsonic +19 -4
  11. package/project/.sdk/model/target/ts.jsonic +3 -3
  12. package/project/.sdk/src/cmp/go/Config_go.ts +119 -0
  13. package/project/.sdk/src/cmp/go/EntityOperation_go.ts +48 -0
  14. package/project/.sdk/src/cmp/go/Entity_go.ts +67 -0
  15. package/project/.sdk/src/cmp/go/MainEntity_go.ts +22 -0
  16. package/project/.sdk/src/cmp/go/Main_go.ts +209 -0
  17. package/project/.sdk/src/cmp/go/Package_go.ts +89 -0
  18. package/project/.sdk/src/cmp/go/TestDirect_go.ts +373 -0
  19. package/project/.sdk/src/cmp/go/TestEntity_go.ts +341 -0
  20. package/project/.sdk/src/cmp/go/Test_go.ts +37 -0
  21. package/project/.sdk/src/cmp/go/fragment/Entity.fragment.go +146 -0
  22. package/project/.sdk/src/cmp/go/fragment/EntityCreateOp.fragment.go +35 -0
  23. package/project/.sdk/src/cmp/go/fragment/EntityListOp.fragment.go +26 -0
  24. package/project/.sdk/src/cmp/go/fragment/EntityLoadOp.fragment.go +38 -0
  25. package/project/.sdk/src/cmp/go/fragment/EntityRemoveOp.fragment.go +38 -0
  26. package/project/.sdk/src/cmp/go/fragment/EntityUpdateOp.fragment.go +38 -0
  27. package/project/.sdk/src/cmp/go/fragment/Main.fragment.go +250 -0
  28. package/project/.sdk/src/cmp/go/fragment/SdkError.fragment.go +22 -0
  29. package/project/.sdk/src/cmp/go/tsconfig.json +15 -0
  30. package/project/.sdk/src/cmp/go/utility_go.ts +88 -0
  31. package/project/.sdk/src/cmp/js/fragment/EntityCreateOp.fragment.js +6 -6
  32. package/project/.sdk/src/cmp/js/fragment/EntityListOp.fragment.js +6 -6
  33. package/project/.sdk/src/cmp/js/fragment/EntityLoadOp.fragment.js +6 -6
  34. package/project/.sdk/src/cmp/js/fragment/EntityRemoveOp.fragment.js +6 -6
  35. package/project/.sdk/src/cmp/js/fragment/EntityUpdateOp.fragment.js +6 -6
  36. package/project/.sdk/src/cmp/js/fragment/Main.fragment.js +85 -1
  37. package/project/.sdk/src/cmp/ts/EntityOperation_ts.ts +1 -1
  38. package/project/.sdk/src/cmp/ts/Main_ts.ts +4 -4
  39. package/project/.sdk/src/cmp/ts/SdkError_ts.ts +42 -0
  40. package/project/.sdk/src/cmp/ts/TestDirect_ts.ts +288 -0
  41. package/project/.sdk/src/cmp/ts/TestEntity_ts.ts +14 -6
  42. package/project/.sdk/src/cmp/ts/Test_ts.ts +2 -0
  43. package/project/.sdk/src/cmp/ts/fragment/Direct.test.fragment.ts +30 -0
  44. package/project/.sdk/src/cmp/ts/fragment/EntityCreateOp.fragment.ts +12 -12
  45. package/project/.sdk/src/cmp/ts/fragment/EntityListOp.fragment.ts +12 -12
  46. package/project/.sdk/src/cmp/ts/fragment/EntityLoadOp.fragment.ts +12 -12
  47. package/project/.sdk/src/cmp/ts/fragment/EntityRemoveOp.fragment.ts +13 -13
  48. package/project/.sdk/src/cmp/ts/fragment/EntityUpdateOp.fragment.ts +12 -12
  49. package/project/.sdk/src/cmp/ts/fragment/Main.fragment.ts +102 -6
  50. package/project/.sdk/src/cmp/ts/fragment/SdkError.fragment.ts +25 -0
  51. package/project/.sdk/tm/go/Makefile +10 -0
  52. package/project/.sdk/tm/go/core/context.go +267 -0
  53. package/project/.sdk/tm/go/core/control.go +7 -0
  54. package/project/.sdk/tm/go/core/error.go +25 -0
  55. package/project/.sdk/tm/go/core/helpers.go +33 -0
  56. package/project/.sdk/tm/go/core/operation.go +61 -0
  57. package/project/.sdk/tm/go/core/response.go +55 -0
  58. package/project/.sdk/tm/go/core/result.go +73 -0
  59. package/project/.sdk/tm/go/core/spec.go +97 -0
  60. package/project/.sdk/tm/go/core/target.go +102 -0
  61. package/project/.sdk/tm/go/core/types.go +44 -0
  62. package/project/.sdk/tm/go/core/utility_type.go +54 -0
  63. package/project/.sdk/tm/go/feature/base_feature.go +40 -0
  64. package/project/.sdk/tm/go/feature/test_feature.go +196 -0
  65. package/project/.sdk/tm/go/src/feature/README.md +1 -0
  66. package/project/.sdk/tm/go/src/feature/base/.gitkeep +0 -0
  67. package/project/.sdk/tm/go/src/feature/test/.gitkeep +0 -0
  68. package/project/.sdk/tm/go/test/custom_utility_test.go +80 -0
  69. package/project/.sdk/tm/go/test/exists_test.go +16 -0
  70. package/project/.sdk/tm/go/test/primary_utility_test.go +899 -0
  71. package/project/.sdk/tm/go/test/runner_test.go +428 -0
  72. package/project/.sdk/tm/go/test/struct_runner_test.go +1094 -0
  73. package/project/.sdk/tm/go/test/struct_utility_test.go +1423 -0
  74. package/project/.sdk/tm/go/utility/clean.go +7 -0
  75. package/project/.sdk/tm/go/utility/done.go +20 -0
  76. package/project/.sdk/tm/go/utility/feature_add.go +10 -0
  77. package/project/.sdk/tm/go/utility/feature_hook.go +30 -0
  78. package/project/.sdk/tm/go/utility/feature_init.go +30 -0
  79. package/project/.sdk/tm/go/utility/fetcher.go +102 -0
  80. package/project/.sdk/tm/go/utility/make_context.go +7 -0
  81. package/project/.sdk/tm/go/utility/make_error.go +69 -0
  82. package/project/.sdk/tm/go/utility/make_fetch_def.go +44 -0
  83. package/project/.sdk/tm/go/utility/make_options.go +130 -0
  84. package/project/.sdk/tm/go/utility/make_request.go +59 -0
  85. package/project/.sdk/tm/go/utility/make_response.go +46 -0
  86. package/project/.sdk/tm/go/utility/make_result.go +55 -0
  87. package/project/.sdk/tm/go/utility/make_spec.go +68 -0
  88. package/project/.sdk/tm/go/utility/make_target.go +95 -0
  89. package/project/.sdk/tm/go/utility/make_url.go +41 -0
  90. package/project/.sdk/tm/go/utility/param.go +66 -0
  91. package/project/.sdk/tm/go/utility/prepare_auth.go +40 -0
  92. package/project/.sdk/tm/go/utility/prepare_body.go +14 -0
  93. package/project/.sdk/tm/go/utility/prepare_headers.go +22 -0
  94. package/project/.sdk/tm/go/utility/prepare_method.go +21 -0
  95. package/project/.sdk/tm/go/utility/prepare_params.go +41 -0
  96. package/project/.sdk/tm/go/utility/prepare_path.go +21 -0
  97. package/project/.sdk/tm/go/utility/prepare_query.go +47 -0
  98. package/project/.sdk/tm/go/utility/register.go +39 -0
  99. package/project/.sdk/tm/go/utility/result_basic.go +31 -0
  100. package/project/.sdk/tm/go/utility/result_body.go +17 -0
  101. package/project/.sdk/tm/go/utility/result_headers.go +22 -0
  102. package/project/.sdk/tm/go/utility/struct/go.mod +3 -0
  103. package/project/.sdk/tm/go/utility/struct/voxgigstruct.go +4891 -0
  104. package/project/.sdk/tm/go/utility/transform_request.go +32 -0
  105. package/project/.sdk/tm/go/utility/transform_response.go +45 -0
  106. package/project/.sdk/tm/js/src/feature/log/LogFeature.js +2 -2
  107. package/project/.sdk/tm/ts/src/Context.ts +144 -0
  108. package/project/.sdk/tm/ts/src/Control.ts +20 -0
  109. package/project/.sdk/tm/ts/src/Operation.ts +24 -0
  110. package/project/.sdk/tm/ts/src/Response.ts +26 -0
  111. package/project/.sdk/tm/ts/src/Result.ts +30 -0
  112. package/project/.sdk/tm/ts/src/Spec.ts +40 -0
  113. package/project/.sdk/tm/ts/src/Target.ts +36 -0
  114. package/project/.sdk/tm/ts/src/feature/base/BaseFeature.ts +1 -1
  115. package/project/.sdk/tm/ts/src/feature/log/LogFeature.ts +2 -2
  116. package/project/.sdk/tm/ts/src/feature/test/TestFeature.ts +7 -7
  117. package/project/.sdk/tm/ts/src/types.ts +15 -205
  118. package/project/.sdk/tm/ts/src/utility/DoneUtility.ts +2 -3
  119. package/project/.sdk/tm/ts/src/utility/{AddfeatureUtility.ts → FeatureAddUtility.ts} +2 -2
  120. package/project/.sdk/tm/ts/src/utility/{InitfeatureUtility.ts → FeatureInitUtility.ts} +2 -2
  121. package/project/.sdk/tm/ts/src/utility/FetcherUtility.ts +5 -3
  122. package/project/.sdk/tm/ts/src/utility/{ErrorUtility.ts → MakeErrorUtility.ts} +4 -4
  123. package/project/.sdk/tm/ts/src/utility/MakeFetchDefUtility.ts +46 -0
  124. package/project/.sdk/tm/ts/src/utility/{OptionsUtility.ts → MakeOptionsUtility.ts} +10 -7
  125. package/project/.sdk/tm/ts/src/utility/MakeRequestUtility.ts +66 -0
  126. package/project/.sdk/tm/ts/src/utility/MakeResponseUtility.ts +61 -0
  127. package/project/.sdk/tm/ts/src/utility/{ResultUtility.ts → MakeResultUtility.ts} +6 -6
  128. package/project/.sdk/tm/ts/src/utility/MakeSpecUtility.ts +61 -0
  129. package/project/.sdk/tm/ts/src/utility/{SelectionUtility.ts → MakeTargetUtility.ts} +22 -24
  130. package/project/.sdk/tm/ts/src/utility/{FullurlUtility.ts → MakeUrlUtility.ts} +7 -8
  131. package/project/.sdk/tm/ts/src/utility/{FindparamUtility.ts → ParamUtility.ts} +21 -8
  132. package/project/.sdk/tm/ts/src/utility/{AuthUtility.ts → PrepareAuthUtility.ts} +4 -4
  133. package/project/.sdk/tm/ts/src/utility/{BodyUtility.ts → PrepareBodyUtility.ts} +7 -7
  134. package/project/.sdk/tm/ts/src/utility/{HeadersUtility.ts → PrepareHeadersUtility.ts} +5 -7
  135. package/project/.sdk/tm/ts/src/utility/{MethodUtility.ts → PrepareMethodUtility.ts} +6 -6
  136. package/project/.sdk/tm/ts/src/utility/PrepareParamsUtility.ts +37 -0
  137. package/project/.sdk/tm/ts/src/utility/PreparePathUtility.ts +3 -4
  138. package/project/.sdk/tm/ts/src/utility/PrepareQueryUtility.ts +30 -0
  139. package/project/.sdk/tm/ts/src/utility/{ResbasicUtility.ts → ResultBasicUtility.ts} +11 -6
  140. package/project/.sdk/tm/ts/src/utility/{ResbodyUtility.ts → ResultBodyUtility.ts} +4 -3
  141. package/project/.sdk/tm/ts/src/utility/{ResheadersUtility.ts → ResultHeadersUtility.ts} +4 -3
  142. package/project/.sdk/tm/ts/src/utility/{ReqformUtility.ts → TransformRequestUtility.ts} +9 -7
  143. package/project/.sdk/tm/ts/src/utility/{ResformUtility.ts → TransformResponseUtility.ts} +10 -7
  144. package/project/.sdk/tm/ts/src/utility/Utility.ts +47 -62
  145. package/project/.sdk/tm/ts/test/runner.ts +25 -4
  146. package/project/.sdk/tm/ts/test/utility/PrimaryUtility.test.ts +386 -175
  147. package/src/cmp/Feature.ts +11 -9
  148. package/src/cmp/Main.ts +8 -2
  149. package/project/.sdk/tm/ts/src/utility/JoinurlUtility.ts +0 -15
  150. package/project/.sdk/tm/ts/src/utility/OperationUtility.ts +0 -23
  151. package/project/.sdk/tm/ts/src/utility/ParamsUtility.ts +0 -37
  152. package/project/.sdk/tm/ts/src/utility/QueryUtility.ts +0 -27
  153. package/project/.sdk/tm/ts/src/utility/RequestUtility.ts +0 -83
  154. package/project/.sdk/tm/ts/src/utility/ResponseUtility.ts +0 -61
  155. package/project/.sdk/tm/ts/src/utility/SpecUtility.ts +0 -68
  156. /package/project/.sdk/tm/ts/src/utility/{FeaturehookUtility.ts → FeatureHookUtility.ts} +0 -0
  157. /package/project/.sdk/tm/ts/src/utility/{ContextUtility.ts → MakeContextUtility.ts} +0 -0
@@ -0,0 +1,1423 @@
1
+ // Vendored from github.com/voxgig/struct/go/voxgigstruct_test.go
2
+ // RUN: go test
3
+ // RUN-SOME: go test -v -run=TestStructUtility/getpath
4
+
5
+ package sdktest
6
+
7
+ import (
8
+ "fmt"
9
+ "math"
10
+ "reflect"
11
+ "strings"
12
+ "testing"
13
+
14
+ voxgigstruct "github.com/voxgig/struct"
15
+ )
16
+
17
+ const STRUCT_TEST_JSON = "../../.sdk/test/test.json"
18
+
19
+ // NOTE: tests are in order of increasing dependence.
20
+ func TestStructUtility(t *testing.T) {
21
+
22
+ store := make(map[string]any)
23
+
24
+ // Create an SDK client for the runner
25
+ sdk, err := MakeTestStructSDK(nil)
26
+ if err != nil {
27
+ t.Fatalf("Failed to create SDK: %v", err)
28
+ }
29
+
30
+ runnerFunc := MakeRunner(STRUCT_TEST_JSON, sdk)
31
+ runnerMap, err := runnerFunc("struct", store)
32
+ if err != nil {
33
+ t.Fatalf("Failed to create runner struct: %v", err)
34
+ }
35
+
36
+ var spec map[string]any = runnerMap.Spec
37
+ var structRunSet RunSet = runnerMap.RunSet
38
+ var structRunSetFlags RunSetFlags = runnerMap.RunSetFlags
39
+
40
+ var minorSpec = spec["minor"].(map[string]any)
41
+ var walkSpec = spec["walk"].(map[string]any)
42
+ var mergeSpec = spec["merge"].(map[string]any)
43
+ var getpathSpec = spec["getpath"].(map[string]any)
44
+ var injectSpec = spec["inject"].(map[string]any)
45
+ var transformSpec = spec["transform"].(map[string]any)
46
+ var validateSpec = spec["validate"].(map[string]any)
47
+ var selectSpec = spec["select"].(map[string]any)
48
+
49
+ // minor tests
50
+ // ===========
51
+
52
+ t.Run("minor-exists", func(t *testing.T) {
53
+ checks := map[string]any{
54
+ "clone": voxgigstruct.Clone,
55
+ "delprop": voxgigstruct.DelProp,
56
+ "escre": voxgigstruct.EscRe,
57
+ "escurl": voxgigstruct.EscUrl,
58
+ "getelem": voxgigstruct.GetElem,
59
+ "getprop": voxgigstruct.GetProp,
60
+
61
+ "getpath": voxgigstruct.GetPath,
62
+ "haskey": voxgigstruct.HasKey,
63
+ "inject": voxgigstruct.Inject,
64
+ "isempty": voxgigstruct.IsEmpty,
65
+ "isfunc": voxgigstruct.IsFunc,
66
+
67
+ "iskey": voxgigstruct.IsKey,
68
+ "islist": voxgigstruct.IsList,
69
+ "ismap": voxgigstruct.IsMap,
70
+ "isnode": voxgigstruct.IsNode,
71
+ "items": voxgigstruct.Items,
72
+
73
+ "joinurl": voxgigstruct.JoinUrl,
74
+ "jsonify": voxgigstruct.Jsonify,
75
+ "keysof": voxgigstruct.KeysOf,
76
+ "merge": voxgigstruct.Merge,
77
+ "pad": voxgigstruct.Pad,
78
+ "pathify": voxgigstruct.Pathify,
79
+
80
+ "select": voxgigstruct.Select,
81
+ "setpath": voxgigstruct.SetPath,
82
+ "size": voxgigstruct.Size,
83
+ "slice": voxgigstruct.Slice,
84
+ "setprop": voxgigstruct.SetProp,
85
+
86
+ "strkey": voxgigstruct.StrKey,
87
+ "stringify": voxgigstruct.Stringify,
88
+ "transform": voxgigstruct.Transform,
89
+ "typify": voxgigstruct.Typify,
90
+ "validate": voxgigstruct.Validate,
91
+
92
+ "walk": voxgigstruct.Walk,
93
+ }
94
+ for name, fn := range checks {
95
+ if fnVal := reflect.ValueOf(fn); fnVal.Kind() != reflect.Func {
96
+ t.Errorf("%s should be a function, but got %s", name, fnVal.Kind().String())
97
+ }
98
+ }
99
+ })
100
+
101
+ t.Run("minor-isnode", func(t *testing.T) {
102
+ structRunSet(t, minorSpec["isnode"], voxgigstruct.IsNode)
103
+ })
104
+
105
+ t.Run("minor-ismap", func(t *testing.T) {
106
+ structRunSet(t, minorSpec["ismap"], voxgigstruct.IsMap)
107
+ })
108
+
109
+ t.Run("minor-islist", func(t *testing.T) {
110
+ structRunSet(t, minorSpec["islist"], voxgigstruct.IsList)
111
+ })
112
+
113
+ t.Run("minor-iskey", func(t *testing.T) {
114
+ structRunSetFlags(t, minorSpec["iskey"], map[string]bool{"null": false}, voxgigstruct.IsKey)
115
+ })
116
+
117
+ t.Run("minor-strkey", func(t *testing.T) {
118
+ structRunSetFlags(t, minorSpec["strkey"], map[string]bool{"null": false}, voxgigstruct.StrKey)
119
+ })
120
+
121
+ t.Run("minor-isempty", func(t *testing.T) {
122
+ structRunSetFlags(t, minorSpec["isempty"], map[string]bool{"null": false}, voxgigstruct.IsEmpty)
123
+ })
124
+
125
+ t.Run("minor-isfunc", func(t *testing.T) {
126
+ structRunSet(t, minorSpec["isfunc"], voxgigstruct.IsFunc)
127
+
128
+ f0 := func() any {
129
+ return nil
130
+ }
131
+
132
+ if !voxgigstruct.IsFunc(f0) {
133
+ t.Errorf("IsFunc failed on function f0")
134
+ }
135
+
136
+ if !voxgigstruct.IsFunc(func() any { return nil }) {
137
+ t.Errorf("IsFunc failed on anonymous function")
138
+ }
139
+ })
140
+
141
+ t.Run("minor-clone", func(t *testing.T) {
142
+ structRunSetFlags(
143
+ t,
144
+ minorSpec["clone"],
145
+ map[string]bool{"null": false},
146
+ voxgigstruct.Clone,
147
+ )
148
+
149
+ f0 := func() any { return nil }
150
+ expected0 := map[string]any{"a": f0}
151
+ result0 := voxgigstruct.Clone(map[string]any{"a": f0})
152
+ if !reflect.DeepEqual(Fdt(expected0), Fdt(result0)) {
153
+ t.Errorf("Expected: %v, Got: %v", expected0, result0)
154
+ }
155
+ })
156
+
157
+ t.Run("minor-escre", func(t *testing.T) {
158
+ structRunSet(t, minorSpec["escre"], voxgigstruct.EscRe)
159
+ })
160
+
161
+ t.Run("minor-escurl", func(t *testing.T) {
162
+ structRunSet(t, minorSpec["escurl"], func(in string) string {
163
+ return strings.ReplaceAll(voxgigstruct.EscUrl(fmt.Sprint(in)), "+", "%20")
164
+ })
165
+ })
166
+
167
+ t.Run("minor-stringify", func(t *testing.T) {
168
+ structRunSet(t, minorSpec["stringify"], func(v any) any {
169
+ m := v.(map[string]any)
170
+ val := m["val"]
171
+
172
+ if "__NULL__" == val {
173
+ val = "null"
174
+ }
175
+
176
+ max, hasMax := m["max"]
177
+ if !hasMax || nil == max {
178
+ return voxgigstruct.Stringify(val)
179
+ } else {
180
+ return voxgigstruct.Stringify(val, int(max.(int)))
181
+ }
182
+ })
183
+ })
184
+
185
+ t.Run("minor-pathify", func(t *testing.T) {
186
+ structRunSetFlags(
187
+ t,
188
+ minorSpec["pathify"],
189
+ map[string]bool{"null": true},
190
+ func(v any) any {
191
+ m := v.(map[string]any)
192
+ path := m["path"]
193
+ from, hasFrom := m["from"]
194
+
195
+ if "__NULL__" == path {
196
+ path = nil
197
+ }
198
+
199
+ pathstr := ""
200
+
201
+ if !hasFrom || nil == from {
202
+ pathstr = voxgigstruct.Pathify(path)
203
+ } else {
204
+ pathstr = voxgigstruct.Pathify(path, int(from.(int)))
205
+ }
206
+
207
+ if "__NULL__" == m["path"] {
208
+ pathstr = strings.ReplaceAll(pathstr, ">", ":null>")
209
+ }
210
+
211
+ pathstr = strings.ReplaceAll(pathstr, "__NULL__.", "")
212
+
213
+ return pathstr
214
+ },
215
+ )
216
+ })
217
+
218
+ t.Run("minor-items", func(t *testing.T) {
219
+ structRunSet(t, minorSpec["items"], voxgigstruct.Items)
220
+ })
221
+
222
+ t.Run("minor-getprop", func(t *testing.T) {
223
+ structRunSetFlags(
224
+ t,
225
+ minorSpec["getprop"],
226
+ map[string]bool{"null": false},
227
+ func(v any) any {
228
+ m := v.(map[string]any)
229
+ store := m["val"]
230
+ key := m["key"]
231
+ alt, hasAlt := m["alt"]
232
+ if !hasAlt || alt == nil {
233
+ return voxgigstruct.GetProp(store, key)
234
+ }
235
+ return voxgigstruct.GetProp(store, key, alt)
236
+ },
237
+ )
238
+ })
239
+
240
+ t.Run("minor-edge-getprop", func(t *testing.T) {
241
+ strarr := []string{"a", "b", "c", "d", "e"}
242
+ expectedA := "c"
243
+
244
+ result0 := voxgigstruct.GetProp(strarr, 2)
245
+ if !reflect.DeepEqual(expectedA, result0) {
246
+ t.Errorf("Expected: %v, Got: %v", expectedA, result0)
247
+ }
248
+
249
+ result1 := voxgigstruct.GetProp(strarr, "2")
250
+ if !reflect.DeepEqual(expectedA, result1) {
251
+ t.Errorf("Expected: %v, Got: %v", expectedA, result1)
252
+ }
253
+
254
+ intarr := []int{2, 3, 5, 7, 11}
255
+ expectedB := 5
256
+
257
+ result2 := voxgigstruct.GetProp(intarr, 2)
258
+ if !reflect.DeepEqual(expectedB, result2) {
259
+ t.Errorf("Expected: %v, Got: %v", expectedB, result2)
260
+ }
261
+
262
+ result3 := voxgigstruct.GetProp(intarr, "2")
263
+ if !reflect.DeepEqual(expectedB, result3) {
264
+ t.Errorf("Expected: %v, Got: %v", expectedB, result3)
265
+ }
266
+ })
267
+
268
+ t.Run("minor-setprop", func(t *testing.T) {
269
+ structRunSetFlags(
270
+ t,
271
+ minorSpec["setprop"],
272
+ map[string]bool{"null": true},
273
+ func(v any) any {
274
+ m := v.(map[string]any)
275
+ parent := m["parent"]
276
+ key := m["key"]
277
+ val := m["val"]
278
+ res := voxgigstruct.SetProp(parent, key, val)
279
+ return res
280
+ })
281
+ })
282
+
283
+ t.Run("minor-edge-setprop", func(t *testing.T) {
284
+ strarr0 := []string{"a", "b", "c", "d", "e"}
285
+ strarr1 := []string{"a", "b", "c", "d", "e"}
286
+
287
+ expected0 := []string{"a", "b", "C", "d", "e"}
288
+ gotstrarr := voxgigstruct.SetProp(strarr0, 2, "C").([]string)
289
+ if !reflect.DeepEqual(gotstrarr, expected0) {
290
+ t.Errorf("Expected: %v, Got: %v", expected0, gotstrarr)
291
+ }
292
+
293
+ expected1 := []string{"a", "b", "CC", "d", "e"}
294
+ gotstrarr = voxgigstruct.SetProp(strarr1, "2", "CC").([]string)
295
+ if !reflect.DeepEqual(gotstrarr, expected1) {
296
+ t.Errorf("Expected: %v, Got: %v", expected0, gotstrarr)
297
+ }
298
+
299
+ intarr0 := []int{2, 3, 5, 7, 11}
300
+ intarr1 := []int{2, 3, 5, 7, 11}
301
+
302
+ expected2 := []int{2, 3, 55, 7, 11}
303
+ gotintarr := voxgigstruct.SetProp(intarr0, 2, 55).([]int)
304
+ if !reflect.DeepEqual(gotintarr, expected2) {
305
+ t.Errorf("Expected: %v, Got: %v", expected2, gotintarr)
306
+ }
307
+
308
+ expected3 := []int{2, 3, 555, 7, 11}
309
+ gotintarr = voxgigstruct.SetProp(intarr1, "2", 555).([]int)
310
+ if !reflect.DeepEqual(gotintarr, expected3) {
311
+ t.Errorf("Expected: %v, Got: %v", expected3, gotintarr)
312
+ }
313
+ })
314
+
315
+ t.Run("minor-haskey", func(t *testing.T) {
316
+ structRunSetFlags(t, minorSpec["haskey"], map[string]bool{"null": false}, func(v any) any {
317
+ m := v.(map[string]any)
318
+ src := m["src"]
319
+ key := m["key"]
320
+ return voxgigstruct.HasKey(src, key)
321
+ })
322
+ })
323
+
324
+ t.Run("minor-keysof", func(t *testing.T) {
325
+ structRunSet(t, minorSpec["keysof"], voxgigstruct.KeysOf)
326
+ })
327
+
328
+ t.Run("minor-filter", func(t *testing.T) {
329
+ checkmap := map[string]func([2]any) bool{
330
+ "gt3": func(n [2]any) bool {
331
+ if v, ok := n[1].(int); ok {
332
+ return v > 3
333
+ }
334
+ return false
335
+ },
336
+ "lt3": func(n [2]any) bool {
337
+ if v, ok := n[1].(int); ok {
338
+ return v < 3
339
+ }
340
+ return false
341
+ },
342
+ }
343
+ structRunSet(t, minorSpec["filter"], func(v any) any {
344
+ m := v.(map[string]any)
345
+ val := m["val"]
346
+ checkName := m["check"].(string)
347
+ return voxgigstruct.Filter(val, checkmap[checkName])
348
+ })
349
+ })
350
+
351
+ t.Run("minor-flatten", func(t *testing.T) {
352
+ structRunSet(t, minorSpec["flatten"], func(v any) any {
353
+ m := v.(map[string]any)
354
+ val := m["val"]
355
+ depth := m["depth"]
356
+ if depth == nil {
357
+ return voxgigstruct.Flatten(val)
358
+ }
359
+ return voxgigstruct.Flatten(val, int(depth.(int)))
360
+ })
361
+ })
362
+
363
+ t.Run("minor-join", func(t *testing.T) {
364
+ structRunSetFlags(t, minorSpec["join"], map[string]bool{"null": false}, func(v any) any {
365
+ m := v.(map[string]any)
366
+ val := m["val"]
367
+ sep := m["sep"]
368
+ urlMode := m["url"]
369
+ arr, ok := val.([]any)
370
+ if !ok {
371
+ arr = []any{}
372
+ }
373
+ return voxgigstruct.Join(arr, sep, urlMode)
374
+ })
375
+ })
376
+
377
+ t.Run("minor-typename", func(t *testing.T) {
378
+ structRunSet(t, minorSpec["typename"], voxgigstruct.Typename)
379
+ })
380
+
381
+ t.Run("minor-typify", func(t *testing.T) {
382
+ structRunSetFlags(t, minorSpec["typify"], map[string]bool{"null": false}, voxgigstruct.Typify)
383
+ })
384
+
385
+ t.Run("minor-size", func(t *testing.T) {
386
+ structRunSetFlags(t, minorSpec["size"], map[string]bool{"null": false}, voxgigstruct.Size)
387
+ })
388
+
389
+ t.Run("minor-slice", func(t *testing.T) {
390
+ structRunSetFlags(t, minorSpec["slice"], map[string]bool{"null": false}, func(v any) any {
391
+ m := v.(map[string]any)
392
+ val := m["val"]
393
+ start := m["start"]
394
+ end := m["end"]
395
+ return voxgigstruct.Slice(val, start, end)
396
+ })
397
+ })
398
+
399
+ t.Run("minor-pad", func(t *testing.T) {
400
+ structRunSetFlags(t, minorSpec["pad"], map[string]bool{"null": false}, func(v any) any {
401
+ m := v.(map[string]any)
402
+ val := m["val"]
403
+ pad := m["pad"]
404
+ char := m["char"]
405
+ return voxgigstruct.Pad(val, pad, char)
406
+ })
407
+ })
408
+
409
+ t.Run("minor-getelem", func(t *testing.T) {
410
+ structRunSetFlags(t, minorSpec["getelem"], map[string]bool{"null": false}, func(v any) any {
411
+ m := v.(map[string]any)
412
+ val := m["val"]
413
+ key := m["key"]
414
+ alt, hasAlt := m["alt"]
415
+ if !hasAlt || alt == nil {
416
+ return voxgigstruct.GetElem(val, key)
417
+ }
418
+ return voxgigstruct.GetElem(val, key, alt)
419
+ })
420
+ })
421
+
422
+ t.Run("minor-edge-getelem", func(t *testing.T) {
423
+ result := voxgigstruct.GetElem([]any{}, 1, func() int { return 2 })
424
+ if result != 2 {
425
+ t.Errorf("Expected: 2, Got: %v", result)
426
+ }
427
+ })
428
+
429
+ t.Run("minor-delprop", func(t *testing.T) {
430
+ structRunSet(t, minorSpec["delprop"], func(v any) any {
431
+ m := v.(map[string]any)
432
+ parent := m["parent"]
433
+ key := m["key"]
434
+ return voxgigstruct.DelProp(parent, key)
435
+ })
436
+ })
437
+
438
+ t.Run("minor-edge-delprop", func(t *testing.T) {
439
+ strarr0 := []any{"a", "b", "c", "d", "e"}
440
+ strarr1 := []any{"a", "b", "c", "d", "e"}
441
+ expected0 := []any{"a", "b", "d", "e"}
442
+ result0 := voxgigstruct.DelProp(strarr0, 2)
443
+ if !reflect.DeepEqual(result0, expected0) {
444
+ t.Errorf("Expected: %v, Got: %v", expected0, result0)
445
+ }
446
+
447
+ result1 := voxgigstruct.DelProp(strarr1, "2")
448
+ if !reflect.DeepEqual(result1, expected0) {
449
+ t.Errorf("Expected: %v, Got: %v", expected0, result1)
450
+ }
451
+
452
+ intarr0 := []any{2, 3, 5, 7, 11}
453
+ intarr1 := []any{2, 3, 5, 7, 11}
454
+ expected1 := []any{2, 3, 7, 11}
455
+ result2 := voxgigstruct.DelProp(intarr0, 2)
456
+ if !reflect.DeepEqual(result2, expected1) {
457
+ t.Errorf("Expected: %v, Got: %v", expected1, result2)
458
+ }
459
+
460
+ result3 := voxgigstruct.DelProp(intarr1, "2")
461
+ if !reflect.DeepEqual(result3, expected1) {
462
+ t.Errorf("Expected: %v, Got: %v", expected1, result3)
463
+ }
464
+ })
465
+
466
+ t.Run("minor-setpath", func(t *testing.T) {
467
+ structRunSetFlags(t, minorSpec["setpath"], map[string]bool{"null": false}, func(v any) any {
468
+ m := v.(map[string]any)
469
+ store := m["store"]
470
+ path := m["path"]
471
+ val := m["val"]
472
+ return voxgigstruct.SetPath(store, path, val)
473
+ })
474
+ })
475
+
476
+ t.Run("minor-edge-setpath", func(t *testing.T) {
477
+ x := map[string]any{"y": map[string]any{"z": 1, "q": 2}}
478
+ result := voxgigstruct.SetPath(x, "y.q", voxgigstruct.DELETE)
479
+ expected := map[string]any{"z": 1}
480
+ if !reflect.DeepEqual(result, expected) {
481
+ t.Errorf("Expected: %v, Got: %v", expected, result)
482
+ }
483
+ expectedX := map[string]any{"y": map[string]any{"z": 1}}
484
+ if !reflect.DeepEqual(x, expectedX) {
485
+ t.Errorf("Expected x: %v, Got: %v", expectedX, x)
486
+ }
487
+ })
488
+
489
+ t.Run("minor-jsonify", func(t *testing.T) {
490
+ structRunSetFlags(t, minorSpec["jsonify"], map[string]bool{"null": false}, func(v any) any {
491
+ m := v.(map[string]any)
492
+ val := m["val"]
493
+ if flags, ok := m["flags"].(map[string]any); ok {
494
+ return voxgigstruct.Jsonify(val, flags)
495
+ }
496
+ return voxgigstruct.Jsonify(val)
497
+ })
498
+ })
499
+
500
+ t.Run("minor-edge-jsonify", func(t *testing.T) {
501
+ result := voxgigstruct.Jsonify(func() int { return 1 })
502
+ if result != "null" {
503
+ t.Errorf("Expected: null, Got: %v", result)
504
+ }
505
+ })
506
+
507
+ t.Run("minor-edge-stringify", func(t *testing.T) {
508
+ a := make(map[string]any)
509
+ a["a"] = a
510
+ result := voxgigstruct.Stringify(a)
511
+ if result != "__STRINGIFY_FAILED__" {
512
+ t.Errorf("Expected: __STRINGIFY_FAILED__, Got: %v", result)
513
+ }
514
+ })
515
+
516
+ t.Run("minor-edge-clone", func(t *testing.T) {
517
+ // Functions are preserved (same reference, not deep-cloned).
518
+ f0 := func() int { return 22 }
519
+ src := map[string]any{"a": 1, "f": f0}
520
+ cloned := voxgigstruct.Clone(src).(map[string]any)
521
+
522
+ if cloned["a"] != 1 {
523
+ t.Errorf("Expected cloned a=1, Got: %v", cloned["a"])
524
+ }
525
+
526
+ clonedF, ok := cloned["f"].(func() int)
527
+ if !ok {
528
+ t.Errorf("Expected cloned f to be a function")
529
+ } else if clonedF() != 22 {
530
+ t.Errorf("Expected cloned f() = 22, Got: %v", clonedF())
531
+ }
532
+
533
+ cloned["a"] = 2
534
+ if src["a"] != 1 {
535
+ t.Errorf("Expected original a=1 after clone modification, Got: %v", src["a"])
536
+ }
537
+
538
+ nested := map[string]any{"b": map[string]any{"c": 3}}
539
+ clonedNested := voxgigstruct.Clone(nested).(map[string]any)
540
+ innerClone := clonedNested["b"].(map[string]any)
541
+ innerClone["c"] = 99
542
+ origInner := nested["b"].(map[string]any)
543
+ if origInner["c"] != 3 {
544
+ t.Errorf("Expected original nested c=3 after clone modification, Got: %v", origInner["c"])
545
+ }
546
+ })
547
+
548
+ t.Run("minor-edge-typify", func(t *testing.T) {
549
+ tNil := voxgigstruct.Typify(nil)
550
+ expected0 := voxgigstruct.T_scalar | voxgigstruct.T_null
551
+ if tNil != expected0 {
552
+ t.Errorf("Typify(nil): Expected: %v, Got: %v", expected0, tNil)
553
+ }
554
+
555
+ tNaN := voxgigstruct.Typify(math.NaN())
556
+ expected1 := voxgigstruct.T_noval
557
+ if tNaN != expected1 {
558
+ t.Errorf("Typify(NaN): Expected: %v, Got: %v", expected1, tNaN)
559
+ }
560
+
561
+ tFunc := voxgigstruct.Typify(func() {})
562
+ expected2 := voxgigstruct.T_scalar | voxgigstruct.T_function
563
+ if tFunc != expected2 {
564
+ t.Errorf("Typify(func): Expected: %v, Got: %v", expected2, tFunc)
565
+ }
566
+ })
567
+
568
+ // walk tests
569
+ // ==========
570
+
571
+ t.Run("walk-exists", func(t *testing.T) {
572
+ fnVal := reflect.ValueOf(voxgigstruct.Walk)
573
+ if fnVal.Kind() != reflect.Func {
574
+ t.Errorf("walk should be a function, but got %s", fnVal.Kind().String())
575
+ }
576
+ })
577
+
578
+ t.Run("walk-log", func(t *testing.T) {
579
+ test := voxgigstruct.Clone(walkSpec["log"]).(map[string]any)
580
+
581
+ walklog := func(k *string, v any, p any, t []string) any {
582
+ var ks string
583
+ if nil == k {
584
+ ks = ""
585
+ } else {
586
+ ks = *k
587
+ }
588
+ entry := "k=" + voxgigstruct.Stringify(ks) +
589
+ ", v=" + voxgigstruct.Stringify(v) +
590
+ ", p=" + voxgigstruct.Stringify(p) +
591
+ ", t=" + voxgigstruct.Pathify(t)
592
+ return entry
593
+ }
594
+
595
+ outMap := test["out"].(map[string]any)
596
+
597
+ // Test after (post-order): Walk(val, nil, walklog)
598
+ var logAfter []any
599
+ walklogAfter := func(k *string, v any, p any, t []string) any {
600
+ entry := walklog(k, v, p, t)
601
+ logAfter = append(logAfter, entry)
602
+ return v
603
+ }
604
+ voxgigstruct.Walk(test["in"], nil, walklogAfter)
605
+
606
+ if !reflect.DeepEqual(logAfter, outMap["after"]) {
607
+ t.Errorf("after log mismatch:\n got: %v\n want: %v\n", logAfter, outMap["after"])
608
+ }
609
+
610
+ // Test before (pre-order): Walk(val, walklog)
611
+ var logBefore []any
612
+ walklogBefore := func(k *string, v any, p any, t []string) any {
613
+ entry := walklog(k, v, p, t)
614
+ logBefore = append(logBefore, entry)
615
+ return v
616
+ }
617
+ voxgigstruct.Walk(test["in"], walklogBefore)
618
+
619
+ if !reflect.DeepEqual(logBefore, outMap["before"]) {
620
+ t.Errorf("before log mismatch:\n got: %v\n want: %v\n", logBefore, outMap["before"])
621
+ }
622
+
623
+ // Test both: Walk(val, walklog, walklog)
624
+ var logBoth []any
625
+ walklogBoth := func(k *string, v any, p any, t []string) any {
626
+ entry := walklog(k, v, p, t)
627
+ logBoth = append(logBoth, entry)
628
+ return v
629
+ }
630
+ voxgigstruct.Walk(test["in"], walklogBoth, walklogBoth)
631
+
632
+ if !reflect.DeepEqual(logBoth, outMap["both"]) {
633
+ t.Errorf("both log mismatch:\n got: %v\n want: %v\n", logBoth, outMap["both"])
634
+ }
635
+ })
636
+
637
+ t.Run("walk-basic", func(t *testing.T) {
638
+ walkpath := func(k *string, val any, parent any, path []string) any {
639
+ if str, ok := val.(string); ok {
640
+ return str + "~" + strings.Join(path, ".")
641
+ }
642
+ return val
643
+ }
644
+
645
+ structRunSet(t, walkSpec["basic"], func(v any) any {
646
+ if "__NULL__" == v {
647
+ v = nil
648
+ }
649
+ return voxgigstruct.Walk(v, walkpath)
650
+ })
651
+ })
652
+
653
+ t.Run("walk-depth", func(t *testing.T) {
654
+ structRunSetFlags(t, walkSpec["depth"], map[string]bool{"null": false}, func(v any) any {
655
+ m := v.(map[string]any)
656
+ src := m["src"]
657
+ maxdepth := m["maxdepth"]
658
+
659
+ var top any
660
+ var cur any
661
+
662
+ copy := func(key *string, val any, _parent any, _path []string) any {
663
+ if voxgigstruct.IsNode(val) {
664
+ var child any
665
+ if voxgigstruct.IsList(val) {
666
+ child = []any{}
667
+ } else {
668
+ child = map[string]any{}
669
+ }
670
+ if nil == key {
671
+ top = child
672
+ cur = child
673
+ } else {
674
+ voxgigstruct.SetProp(cur, *key, child)
675
+ cur = child
676
+ }
677
+ } else if nil != key {
678
+ voxgigstruct.SetProp(cur, *key, val)
679
+ }
680
+ return val
681
+ }
682
+
683
+ if maxdepth == nil {
684
+ voxgigstruct.Walk(src, copy)
685
+ } else {
686
+ md := int(maxdepth.(int))
687
+ voxgigstruct.Walk(src, copy, nil, md)
688
+ }
689
+ return top
690
+ })
691
+ })
692
+
693
+ t.Run("walk-copy", func(t *testing.T) {
694
+ structRunSet(t, walkSpec["copy"], func(v any) any {
695
+ var cur []any
696
+ var keys []string
697
+
698
+ walkcopy := func(key *string, val any, _parent any, path []string) any {
699
+ if nil == key {
700
+ cur = make([]any, 33)
701
+ keys = make([]string, 33)
702
+ if voxgigstruct.IsMap(val) {
703
+ cur[0] = map[string]any{}
704
+ } else if voxgigstruct.IsList(val) {
705
+ cur[0] = []any{}
706
+ } else {
707
+ cur[0] = val
708
+ }
709
+ return val
710
+ }
711
+
712
+ v := val
713
+ i := voxgigstruct.Size(path)
714
+ keys[i] = *key
715
+
716
+ if voxgigstruct.IsNode(v) {
717
+ if voxgigstruct.IsMap(v) {
718
+ cur[i] = map[string]any{}
719
+ } else {
720
+ cur[i] = []any{}
721
+ }
722
+ v = cur[i]
723
+ }
724
+
725
+ cur[i-1] = voxgigstruct.SetProp(cur[i-1], *key, v)
726
+
727
+ // Re-link parent chain up for slice reference stability
728
+ for j := i - 1; j > 0; j-- {
729
+ cur[j-1] = voxgigstruct.SetProp(cur[j-1], keys[j], cur[j])
730
+ }
731
+
732
+ return val
733
+ }
734
+
735
+ voxgigstruct.Walk(v, walkcopy)
736
+ return cur[0]
737
+ })
738
+ })
739
+
740
+ // merge tests
741
+ // ===========
742
+
743
+ t.Run("merge-exists", func(t *testing.T) {
744
+ fnVal := reflect.ValueOf(voxgigstruct.Merge)
745
+ if fnVal.Kind() != reflect.Func {
746
+ t.Errorf("merge should be a function, but got %s", fnVal.Kind().String())
747
+ }
748
+ })
749
+
750
+ t.Run("merge-basic", func(t *testing.T) {
751
+ test := mergeSpec["basic"].(map[string]any)
752
+ inVal := test["in"]
753
+ outVal := test["out"]
754
+ result := voxgigstruct.Merge(inVal)
755
+ if !reflect.DeepEqual(result, outVal) {
756
+ t.Errorf("Expected: %v, Got: %v", outVal, result)
757
+ }
758
+ })
759
+
760
+ t.Run("merge-cases", func(t *testing.T) {
761
+ structRunSet(t, mergeSpec["cases"], func(v any) any {
762
+ return voxgigstruct.Merge(v)
763
+ })
764
+ })
765
+
766
+ t.Run("merge-array", func(t *testing.T) {
767
+ structRunSet(t, mergeSpec["array"], func(v any) any {
768
+ return voxgigstruct.Merge(v)
769
+ })
770
+ })
771
+
772
+ t.Run("merge-integrity", func(t *testing.T) {
773
+ structRunSet(t, mergeSpec["integrity"], func(v any) any {
774
+ return voxgigstruct.Merge(v)
775
+ })
776
+ })
777
+
778
+ t.Run("merge-special", func(t *testing.T) {
779
+ f0 := func() int { return 11 }
780
+
781
+ result0 := voxgigstruct.Merge([]any{f0})
782
+ var fr0 = result0.(func() int)
783
+
784
+ if f0() != fr0() {
785
+ t.Errorf("Expected same function reference (A)")
786
+ }
787
+
788
+ result1 := voxgigstruct.Merge([]any{nil, f0})
789
+ var fr1 = result1.(func() int)
790
+ if f0() != fr1() {
791
+ t.Errorf("Expected same function reference (B)")
792
+ }
793
+
794
+ result2 := voxgigstruct.Merge([]any{map[string]any{"a": f0}}).(map[string]any)
795
+ var fr2 = result2["a"].(func() int)
796
+ if f0() != fr2() {
797
+ t.Errorf("Expected object with function reference")
798
+ }
799
+
800
+ result3 := voxgigstruct.Merge([]any{[]any{f0}}).([]any)
801
+ var fr3 = result3[0].(func() int)
802
+ if f0() != fr3() {
803
+ t.Errorf("Expected array with function reference")
804
+ }
805
+
806
+ result4 := voxgigstruct.Merge([]any{map[string]any{"a": map[string]any{"b": f0}}})
807
+ var b = result4.(map[string]any)["a"].(map[string]any)
808
+ var fr4 = b["b"].(func() int)
809
+
810
+ if f0() != fr4() {
811
+ t.Errorf("Expected deep object with function reference")
812
+ }
813
+ })
814
+
815
+ t.Run("merge-depth", func(t *testing.T) {
816
+ structRunSet(t, mergeSpec["depth"], func(v any) any {
817
+ m := v.(map[string]any)
818
+ val := m["val"]
819
+ depth := m["depth"]
820
+ if depth == nil {
821
+ return voxgigstruct.Merge(val)
822
+ }
823
+ return voxgigstruct.Merge(val, int(depth.(int)))
824
+ })
825
+ })
826
+
827
+ // getpath tests
828
+ // =============
829
+
830
+ t.Run("getpath-exists", func(t *testing.T) {
831
+ fnVal := reflect.ValueOf(voxgigstruct.GetPath)
832
+ if fnVal.Kind() != reflect.Func {
833
+ t.Errorf("getpath should be a function, but got %s", fnVal.Kind().String())
834
+ }
835
+ })
836
+
837
+ t.Run("getpath-basic", func(t *testing.T) {
838
+ structRunSet(t, getpathSpec["basic"], func(v any) any {
839
+ m := v.(map[string]any)
840
+ path := m["path"]
841
+ store := m["store"]
842
+
843
+ return voxgigstruct.GetPath(path, store)
844
+ })
845
+ })
846
+
847
+ t.Run("getpath-relative", func(t *testing.T) {
848
+ structRunSet(t, getpathSpec["relative"], func(v any) any {
849
+ m := v.(map[string]any)
850
+ path := m["path"]
851
+ store := m["store"]
852
+ dparent := m["dparent"]
853
+
854
+ dpathStr, _ := m["dpath"].(string)
855
+ var dpath []string
856
+ if dpathStr != "" {
857
+ dpath = strings.Split(dpathStr, ".")
858
+ }
859
+
860
+ inj := &voxgigstruct.Injection{
861
+ Dparent: dparent,
862
+ Dpath: dpath,
863
+ }
864
+
865
+ return voxgigstruct.GetPath(path, store, inj)
866
+ })
867
+ })
868
+
869
+ t.Run("getpath-special", func(t *testing.T) {
870
+ structRunSet(t, getpathSpec["special"], func(v any) any {
871
+ m := v.(map[string]any)
872
+ path := m["path"]
873
+ store := m["store"]
874
+ inj := m["inj"]
875
+
876
+ if inj != nil {
877
+ injMap, _ := inj.(map[string]any)
878
+ inj := &voxgigstruct.Injection{}
879
+ if key, ok := injMap["key"]; ok {
880
+ inj.Key = fmt.Sprint(key)
881
+ }
882
+ if meta, ok := injMap["meta"]; ok {
883
+ if metaMap, ok := meta.(map[string]any); ok {
884
+ inj.Meta = metaMap
885
+ }
886
+ }
887
+ return voxgigstruct.GetPath(path, store, inj)
888
+ }
889
+
890
+ return voxgigstruct.GetPath(path, store)
891
+ })
892
+ })
893
+
894
+ // inject tests
895
+ // ============
896
+
897
+ t.Run("inject-exists", func(t *testing.T) {
898
+ fnVal := reflect.ValueOf(voxgigstruct.Inject)
899
+ if fnVal.Kind() != reflect.Func {
900
+ t.Errorf("inject should be a function, but got %s", fnVal.Kind().String())
901
+ }
902
+ })
903
+
904
+ t.Run("inject-basic", func(t *testing.T) {
905
+ subtest := injectSpec["basic"].(map[string]any)
906
+ inVal := subtest["in"].(map[string]any)
907
+ val, store := inVal["val"], inVal["store"]
908
+ outVal := subtest["out"]
909
+ result := voxgigstruct.Inject(val, store)
910
+ if !reflect.DeepEqual(result, outVal) {
911
+ t.Errorf("Expected: %v, Got: %v", outVal, result)
912
+ }
913
+ })
914
+
915
+ t.Run("inject-string", func(t *testing.T) {
916
+ structRunSet(t, injectSpec["string"], func(v any) any {
917
+ m := v.(map[string]any)
918
+ val := m["val"]
919
+ store := m["store"]
920
+
921
+ return voxgigstruct.Inject(val, store, &voxgigstruct.Injection{Modify: NullModifier})
922
+ })
923
+ })
924
+
925
+ t.Run("inject-deep", func(t *testing.T) {
926
+ structRunSet(t, injectSpec["deep"], func(v any) any {
927
+ m := v.(map[string]any)
928
+ val := m["val"]
929
+ store := m["store"]
930
+ return voxgigstruct.Inject(val, store)
931
+ })
932
+ })
933
+
934
+ // transform tests
935
+ // ===============
936
+
937
+ t.Run("transform-exists", func(t *testing.T) {
938
+ fnVal := reflect.ValueOf(voxgigstruct.Transform)
939
+ if fnVal.Kind() != reflect.Func {
940
+ t.Errorf("transform should be a function, but got %s", fnVal.Kind().String())
941
+ }
942
+ })
943
+
944
+ t.Run("transform-basic", func(t *testing.T) {
945
+ subtest := transformSpec["basic"].(map[string]any)
946
+ inVal := subtest["in"].(map[string]any)
947
+ data := inVal["data"]
948
+ spec := inVal["spec"]
949
+ outVal := subtest["out"]
950
+ result := voxgigstruct.Transform(data, spec)
951
+ if !reflect.DeepEqual(result, outVal) {
952
+ t.Errorf("Expected: %v, Got: %v", outVal, result)
953
+ }
954
+ })
955
+
956
+ t.Run("transform-paths", func(t *testing.T) {
957
+ structRunSet(t, transformSpec["paths"], func(v any) any {
958
+ m := v.(map[string]any)
959
+ data := m["data"]
960
+ spec := m["spec"]
961
+ return voxgigstruct.Transform(data, spec)
962
+ })
963
+ })
964
+
965
+ t.Run("transform-cmds", func(t *testing.T) {
966
+ structRunSet(t, transformSpec["cmds"], func(v any) any {
967
+ m := v.(map[string]any)
968
+ data := m["data"]
969
+ spec := m["spec"]
970
+ return voxgigstruct.Transform(data, spec)
971
+ })
972
+ })
973
+
974
+ t.Run("transform-each", func(t *testing.T) {
975
+ structRunSet(t, transformSpec["each"], func(v any) any {
976
+ m := v.(map[string]any)
977
+ data := m["data"]
978
+ spec := m["spec"]
979
+ return voxgigstruct.Transform(data, spec)
980
+ })
981
+ })
982
+
983
+ t.Run("transform-pack", func(t *testing.T) {
984
+ structRunSet(t, transformSpec["pack"], func(v any) any {
985
+ m := v.(map[string]any)
986
+ data := m["data"]
987
+ spec := m["spec"]
988
+ return voxgigstruct.Transform(data, spec)
989
+ })
990
+ })
991
+
992
+ t.Run("transform-ref", func(t *testing.T) {
993
+ structRunSet(t, transformSpec["ref"], func(v any) any {
994
+ m := v.(map[string]any)
995
+ data := m["data"]
996
+ spec := m["spec"]
997
+ return voxgigstruct.Transform(data, spec)
998
+ })
999
+ })
1000
+
1001
+ t.Run("transform-format", func(t *testing.T) {
1002
+ structRunSetFlags(t, transformSpec["format"], map[string]bool{"null": false}, func(v any) any {
1003
+ m := v.(map[string]any)
1004
+ data := m["data"]
1005
+ spec := m["spec"]
1006
+ return voxgigstruct.Transform(data, spec)
1007
+ })
1008
+ })
1009
+
1010
+ t.Run("transform-apply", func(t *testing.T) {
1011
+ structRunSet(t, transformSpec["apply"], func(v any) (any, error) {
1012
+ m := v.(map[string]any)
1013
+ data := m["data"]
1014
+ spec := m["spec"]
1015
+ result, errs := voxgigstruct.TransformCollect(data, spec)
1016
+ if len(errs) > 0 {
1017
+ return result, fmt.Errorf("%s", errs[0])
1018
+ }
1019
+ return result, nil
1020
+ })
1021
+ })
1022
+
1023
+ t.Run("transform-edge-apply", func(t *testing.T) {
1024
+ result := voxgigstruct.Transform(
1025
+ map[string]any{},
1026
+ []any{"`$APPLY`", func(v any) any { return 1 + v.(int) }, 1},
1027
+ )
1028
+ if result != 2 {
1029
+ t.Errorf("Expected: 2, Got: %v", result)
1030
+ }
1031
+ })
1032
+
1033
+ t.Run("transform-modify", func(t *testing.T) {
1034
+ structRunSet(t, transformSpec["modify"], func(v any) any {
1035
+ m := v.(map[string]any)
1036
+ data := m["data"]
1037
+ spec := m["spec"]
1038
+ return voxgigstruct.TransformModify(data, spec, nil, func(
1039
+ val any,
1040
+ key any,
1041
+ parent any,
1042
+ inj *voxgigstruct.Injection,
1043
+ store any,
1044
+ ) {
1045
+ if key != nil && parent != nil {
1046
+ if strval, ok := val.(string); ok {
1047
+ newVal := "@" + strval
1048
+ if pm, isMap := parent.(map[string]any); isMap {
1049
+ pm[fmt.Sprint(key)] = newVal
1050
+ }
1051
+ }
1052
+ }
1053
+ })
1054
+ })
1055
+ })
1056
+
1057
+ t.Run("transform-extra", func(t *testing.T) {
1058
+ data := map[string]any{"a": 1}
1059
+ spec := map[string]any{
1060
+ "x": "`a`",
1061
+ "b": "`$COPY`",
1062
+ "c": "`$UPPER`",
1063
+ }
1064
+
1065
+ upper := voxgigstruct.Injector(func(
1066
+ s *voxgigstruct.Injection,
1067
+ val any,
1068
+ ref *string,
1069
+ store any,
1070
+ ) any {
1071
+ p := s.Path
1072
+ if len(p.List) == 0 {
1073
+ return ""
1074
+ }
1075
+ last := p.List[len(p.List)-1]
1076
+ if len(last) > 0 {
1077
+ return string(last[0]-32) + last[1:]
1078
+ }
1079
+
1080
+ return last
1081
+ })
1082
+
1083
+ extra := map[string]any{
1084
+ "b": 2,
1085
+ "$UPPER": upper,
1086
+ }
1087
+
1088
+ output := map[string]any{
1089
+ "x": 1,
1090
+ "b": 2,
1091
+ "c": "C",
1092
+ }
1093
+
1094
+ result := voxgigstruct.TransformModify(data, spec, extra, nil)
1095
+ if !reflect.DeepEqual(result, output) {
1096
+ t.Errorf("Expected: %s, \nGot: %s \nExpected JSON: %s \nGot JSON: %s",
1097
+ Fdt(output),
1098
+ Fdt(result),
1099
+ ToJSONString(output),
1100
+ ToJSONString(result),
1101
+ )
1102
+ }
1103
+ })
1104
+
1105
+ t.Run("transform-funcval", func(t *testing.T) {
1106
+ f0 := func() int { return 22 }
1107
+
1108
+ result1 := voxgigstruct.Transform(map[string]any{}, map[string]any{"x": 1})
1109
+ expected1 := map[string]any{"x": 1}
1110
+ if !reflect.DeepEqual(expected1, result1) {
1111
+ t.Errorf("Expected simple value transform result")
1112
+ }
1113
+
1114
+ result2 := voxgigstruct.Transform(map[string]any{}, map[string]any{"x": f0})
1115
+ var fr0 = result2.(map[string]any)["x"].(func() int)
1116
+ if f0() != fr0() {
1117
+ t.Errorf("Expected x to be f0")
1118
+ }
1119
+
1120
+ result3 := voxgigstruct.Transform(map[string]any{"a": 1}, map[string]any{"x": "`a`"})
1121
+ expected3 := map[string]any{"x": 1}
1122
+ if !reflect.DeepEqual(expected3, result3) {
1123
+ t.Errorf("Expected value lookup transform to work")
1124
+ }
1125
+
1126
+ result4 := voxgigstruct.Transform(map[string]any{"f0": f0}, map[string]any{"x": "`f0`"})
1127
+ var fr4 = result4.(map[string]any)["x"].(func() int)
1128
+ if 22 != fr4() {
1129
+ t.Errorf("Expected function to be preserved")
1130
+ }
1131
+ })
1132
+
1133
+ // validate tests
1134
+ // ===============
1135
+
1136
+ t.Run("validate-exists", func(t *testing.T) {
1137
+ fnVal := reflect.ValueOf(voxgigstruct.Validate)
1138
+ if fnVal.Kind() != reflect.Func {
1139
+ t.Errorf("validate should be a function, but got %s", fnVal.Kind().String())
1140
+ }
1141
+ })
1142
+
1143
+ t.Run("validate-basic", func(t *testing.T) {
1144
+ structRunSetFlags(t, validateSpec["basic"], map[string]bool{"null": false}, func(v any) (any, error) {
1145
+ m := v.(map[string]any)
1146
+ data := m["data"]
1147
+ spec := m["spec"]
1148
+ return voxgigstruct.Validate(data, spec)
1149
+ })
1150
+ })
1151
+
1152
+ t.Run("validate-child", func(t *testing.T) {
1153
+ structRunSet(t, validateSpec["child"], func(v any) (any, error) {
1154
+ m := v.(map[string]any)
1155
+ data := m["data"]
1156
+ spec := m["spec"]
1157
+ out, err := voxgigstruct.Validate(data, spec)
1158
+ return out, err
1159
+ })
1160
+ })
1161
+
1162
+ t.Run("validate-one", func(t *testing.T) {
1163
+ structRunSet(t, validateSpec["one"], func(v any) (any, error) {
1164
+ m := v.(map[string]any)
1165
+ data := m["data"]
1166
+ spec := m["spec"]
1167
+ return voxgigstruct.Validate(data, spec)
1168
+ })
1169
+ })
1170
+
1171
+ t.Run("validate-exact", func(t *testing.T) {
1172
+ structRunSet(t, validateSpec["exact"], func(v any) (any, error) {
1173
+ m := v.(map[string]any)
1174
+ data := m["data"]
1175
+ spec := m["spec"]
1176
+ return voxgigstruct.Validate(data, spec)
1177
+ })
1178
+ })
1179
+
1180
+ t.Run("validate-invalid", func(t *testing.T) {
1181
+ structRunSetFlags(t, validateSpec["invalid"], map[string]bool{"null": false}, func(v any) (any, error) {
1182
+ m := v.(map[string]any)
1183
+ return voxgigstruct.Validate(m["data"], m["spec"])
1184
+ })
1185
+ })
1186
+
1187
+ t.Run("validate-special", func(t *testing.T) {
1188
+ structRunSet(t, validateSpec["special"], func(v any) (any, error) {
1189
+ m := v.(map[string]any)
1190
+ data := m["data"]
1191
+ spec := m["spec"]
1192
+
1193
+ if inj, ok := m["inj"]; ok && inj != nil {
1194
+ injMap := inj.(map[string]any)
1195
+ extra := make(map[string]any)
1196
+
1197
+ if meta, ok := injMap["meta"]; ok {
1198
+ extra["meta"] = meta
1199
+ }
1200
+
1201
+ return voxgigstruct.Validate(data, spec, &voxgigstruct.Injection{Extra: extra})
1202
+ }
1203
+
1204
+ return voxgigstruct.Validate(data, spec)
1205
+ })
1206
+ })
1207
+
1208
+ t.Run("validate-custom", func(t *testing.T) {
1209
+ errs := voxgigstruct.ListRefCreate[any]()
1210
+
1211
+ integerCheck := voxgigstruct.Injector(func(
1212
+ inj *voxgigstruct.Injection,
1213
+ val any,
1214
+ ref *string,
1215
+ store any,
1216
+ ) any {
1217
+ out := voxgigstruct.GetProp(inj.Dparent, inj.Key)
1218
+
1219
+ switch x := out.(type) {
1220
+ case int:
1221
+ return x
1222
+ default:
1223
+ msg := fmt.Sprintf("Not an integer at %s: %v",
1224
+ voxgigstruct.Pathify(inj.Path.List, 1), out)
1225
+ inj.Errs.Append(msg)
1226
+ return nil
1227
+ }
1228
+ })
1229
+
1230
+ extra := map[string]any{
1231
+ "$INTEGER": integerCheck,
1232
+ }
1233
+
1234
+ shape := map[string]any{
1235
+ "a": "`$INTEGER`",
1236
+ }
1237
+
1238
+ out, err := voxgigstruct.Validate(
1239
+ map[string]any{"a": 1},
1240
+ shape,
1241
+ &voxgigstruct.Injection{Extra: extra, Errs: errs},
1242
+ )
1243
+ if nil != err {
1244
+ t.Error(err)
1245
+ }
1246
+
1247
+ expected0 := map[string]any{"a": 1}
1248
+ if !reflect.DeepEqual(out, expected0) {
1249
+ t.Errorf("Expected: %v, Got: %v", expected0, out)
1250
+ }
1251
+ errs0 := []any{}
1252
+ if !reflect.DeepEqual(errs.List, errs0) {
1253
+ t.Errorf("Expected Error: %v, Got: %v", errs0, errs.List)
1254
+ }
1255
+
1256
+ out, err = voxgigstruct.Validate(
1257
+ map[string]any{"a": "A"},
1258
+ shape,
1259
+ &voxgigstruct.Injection{Extra: extra, Errs: errs},
1260
+ )
1261
+ if nil != err {
1262
+ t.Error(err)
1263
+ }
1264
+
1265
+ expected1 := map[string]any{"a": "A"}
1266
+ if !reflect.DeepEqual(out, expected1) {
1267
+ t.Errorf("Expected: %v, Got: %v", expected1, out)
1268
+ }
1269
+
1270
+ errs1 := []any{"Not an integer at a: A"}
1271
+ if !reflect.DeepEqual(errs.List, errs1) {
1272
+ t.Errorf("Expected Error: %v, Got: %v", errs1, errs.List)
1273
+ }
1274
+
1275
+ })
1276
+
1277
+ t.Run("validate-edge", func(t *testing.T) {
1278
+ // $INSTANCE validator should fail for integer, map, and list values.
1279
+ spec := map[string]any{"x": "`$INSTANCE`"}
1280
+
1281
+ out0, err0 := voxgigstruct.Validate(map[string]any{"x": 1}, spec)
1282
+ if err0 == nil {
1283
+ t.Errorf("Expected error for $INSTANCE with integer, Got: %v", out0)
1284
+ }
1285
+ if err0 != nil && !strings.Contains(err0.Error(), "instance") {
1286
+ t.Errorf("Expected instance error message, Got: %v", err0.Error())
1287
+ }
1288
+
1289
+ out1, err1 := voxgigstruct.Validate(map[string]any{"x": map[string]any{"a": 1}}, spec)
1290
+ if err1 == nil {
1291
+ t.Errorf("Expected error for $INSTANCE with map, Got: %v", out1)
1292
+ }
1293
+ if err1 != nil && !strings.Contains(err1.Error(), "instance") {
1294
+ t.Errorf("Expected instance error message, Got: %v", err1.Error())
1295
+ }
1296
+
1297
+ out2, err2 := voxgigstruct.Validate(map[string]any{"x": []any{1, 2}}, spec)
1298
+ if err2 == nil {
1299
+ t.Errorf("Expected error for $INSTANCE with list, Got: %v", out2)
1300
+ }
1301
+ if err2 != nil && !strings.Contains(err2.Error(), "instance") {
1302
+ t.Errorf("Expected instance error message, Got: %v", err2.Error())
1303
+ }
1304
+ })
1305
+
1306
+ // select tests
1307
+ // ============
1308
+
1309
+ t.Run("select-basic", func(t *testing.T) {
1310
+ structRunSet(t, selectSpec["basic"], func(v any) any {
1311
+ m := v.(map[string]any)
1312
+ obj := m["obj"]
1313
+ query := m["query"]
1314
+ return voxgigstruct.Select(obj, query)
1315
+ })
1316
+ })
1317
+
1318
+ t.Run("select-operators", func(t *testing.T) {
1319
+ structRunSet(t, selectSpec["operators"], func(v any) any {
1320
+ m := v.(map[string]any)
1321
+ obj := m["obj"]
1322
+ query := m["query"]
1323
+ return voxgigstruct.Select(obj, query)
1324
+ })
1325
+ })
1326
+
1327
+ t.Run("select-edge", func(t *testing.T) {
1328
+ structRunSet(t, selectSpec["edge"], func(v any) any {
1329
+ m := v.(map[string]any)
1330
+ obj := m["obj"]
1331
+ query := m["query"]
1332
+ return voxgigstruct.Select(obj, query)
1333
+ })
1334
+ })
1335
+
1336
+ t.Run("select-alts", func(t *testing.T) {
1337
+ structRunSet(t, selectSpec["alts"], func(v any) any {
1338
+ m := v.(map[string]any)
1339
+ obj := m["obj"]
1340
+ query := m["query"]
1341
+ return voxgigstruct.Select(obj, query)
1342
+ })
1343
+ })
1344
+
1345
+ // JSON Builder
1346
+ // ============
1347
+
1348
+ t.Run("json-builder", func(t *testing.T) {
1349
+ expected0 := "{\n \"a\": 1\n}"
1350
+ result0 := voxgigstruct.Jsonify(voxgigstruct.Jo("a", 1))
1351
+ if result0 != expected0 {
1352
+ t.Errorf("Expected: %v, Got: %v", expected0, result0)
1353
+ }
1354
+
1355
+ expected1 := "[\n \"b\",\n 2\n]"
1356
+ result1 := voxgigstruct.Jsonify(voxgigstruct.Ja("b", 2))
1357
+ if result1 != expected1 {
1358
+ t.Errorf("Expected: %v, Got: %v", expected1, result1)
1359
+ }
1360
+
1361
+ expected2 := "{\n \"c\": \"C\",\n \"d\": {\n \"x\": true\n },\n \"e\": [\n null,\n false\n ]\n}"
1362
+ result2 := voxgigstruct.Jsonify(voxgigstruct.Jo(
1363
+ "c", "C",
1364
+ "d", voxgigstruct.Jo("x", true),
1365
+ "e", voxgigstruct.Ja(nil, false),
1366
+ ))
1367
+ if result2 != expected2 {
1368
+ t.Errorf("Expected:\n%v\nGot:\n%v", expected2, result2)
1369
+ }
1370
+ })
1371
+
1372
+ // getpath-handler test
1373
+ // ====================
1374
+
1375
+ t.Run("getpath-handler", func(t *testing.T) {
1376
+ structRunSet(t, getpathSpec["handler"], func(v any) any {
1377
+ m := v.(map[string]any)
1378
+ path := m["path"]
1379
+ innerStore := m["store"]
1380
+
1381
+ store := map[string]any{
1382
+ "$TOP": innerStore,
1383
+ "$FOO": func() string { return "foo" },
1384
+ }
1385
+
1386
+ inj := &voxgigstruct.Injection{
1387
+ Handler: func(
1388
+ s *voxgigstruct.Injection,
1389
+ val any,
1390
+ ref *string,
1391
+ st any,
1392
+ ) any {
1393
+ if fn, ok := val.(func() string); ok {
1394
+ return fn()
1395
+ }
1396
+ return val
1397
+ },
1398
+ }
1399
+
1400
+ return voxgigstruct.GetPath(path, store, inj)
1401
+ })
1402
+ })
1403
+ }
1404
+
1405
+ // joinPath joins a string slice with "."
1406
+ func joinPath(path []string) string {
1407
+ result := ""
1408
+ for i, p := range path {
1409
+ if i > 0 {
1410
+ result += "."
1411
+ }
1412
+ result += p
1413
+ }
1414
+ return result
1415
+ }
1416
+
1417
+ func IsSameFunc(target any, candidate any) bool {
1418
+ if reflect.TypeOf(target).Kind() != reflect.Func || reflect.TypeOf(candidate).Kind() != reflect.Func {
1419
+ return false
1420
+ }
1421
+
1422
+ return reflect.ValueOf(target).Pointer() == reflect.ValueOf(candidate).Pointer()
1423
+ }