Skip to content

Commit 8d3fa09

Browse files
authored
feat: introduce tracing (#33)
1 parent aac3f95 commit 8d3fa09

2 files changed

Lines changed: 92 additions & 55 deletions

File tree

src/arrayHandling.ts

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,74 +14,85 @@
1414
* limitations under the License.
1515
*/
1616

17-
import type { RealTestFunc, TestOptions } from "./index"
17+
import type { TestOptions, TestWithPathFunc } from "./index"
1818

1919
const fillHashtags = (count: number): string => "#".repeat(count)
2020

2121
/**
2222
* Handles `$any`, `$all`, and `$inarray`. Works with nested loops!
23-
* @param realTest The realTest function.
23+
* @param testWithPath The testWithPath function.
2424
* @param input The state machine.
2525
* @param variables The variables.
2626
* @param op The operation being performed.
2727
* @param options The test options.
2828
* @internal
2929
*/
3030
export function handleArrayLogic<Variables>(
31-
realTest: RealTestFunc,
31+
testWithPath: TestWithPathFunc,
3232
input: any,
3333
variables: Variables,
3434
op: string,
35-
options: TestOptions
35+
options: TestOptions,
3636
): boolean {
3737
const inValue = input[op]["in"]
3838
const depth = (options._currentLoopDepth || 0) + 1
3939

4040
if (inValue.includes("#")) {
41-
throw new TypeError("Nested array nodes cannot use current iteration (`$.#`) as an `in` value", {
42-
cause: options._path
43-
})
41+
throw new TypeError(
42+
"Nested array nodes cannot use current iteration (`$.#`) as an `in` value",
43+
{
44+
cause: options._path,
45+
},
46+
)
4447
}
4548

4649
// find the array
47-
const array = realTest(inValue, variables, {
48-
...options,
49-
_currentLoopDepth: depth,
50-
_path: `${options._path}.${op}.in`
51-
}) as unknown as unknown[]
50+
const array = testWithPath(
51+
inValue,
52+
variables,
53+
{
54+
...options,
55+
_currentLoopDepth: depth,
56+
},
57+
`${op}.in`,
58+
) as unknown as unknown[]
5259

5360
const itemConditions = input[op]["?"]
5461

5562
for (const item of array) {
56-
const test = realTest(itemConditions, variables, {
57-
...options,
58-
_currentLoopDepth: depth,
59-
_path: `${options._path}.${op}.?`,
60-
findNamedChild(reference, variables) {
61-
// NOTE: if we have a multi-layered loop, this should one-by-one fall back until the targeted loop is hit
62-
const hashtags = fillHashtags(depth)
63+
const test = testWithPath(
64+
itemConditions,
65+
variables,
66+
{
67+
...options,
68+
_currentLoopDepth: depth,
69+
findNamedChild(reference, variables) {
70+
// NOTE: if we have a multi-layered loop, this should one-by-one fall back until the targeted loop is hit
71+
const hashtags = fillHashtags(depth)
6372

64-
// a little future-proofing, as sometimes the $ is there, and other times it isn't.
65-
// we strip it out somewhere, but it shouldn't matter too much.
66-
if (
67-
reference === `$.${hashtags}` ||
68-
reference === `.${hashtags}`
69-
) {
70-
return item
71-
}
73+
// a little future-proofing, as sometimes the $ is there, and other times it isn't.
74+
// we strip it out somewhere, but it shouldn't matter too much.
75+
if (
76+
reference === `$.${hashtags}` ||
77+
reference === `.${hashtags}`
78+
) {
79+
return item
80+
}
7281

73-
// handle properties of an object
74-
if (typeof item === "object") {
75-
const newReference = `$${reference.substring(
76-
reference.indexOf("#.") + 1
77-
)}`
78-
const found = options.findNamedChild(newReference, item)
79-
if (found !== newReference) return found
80-
}
82+
// handle properties of an object
83+
if (typeof item === "object") {
84+
const newReference = `$${reference.substring(
85+
reference.indexOf("#.") + 1,
86+
)}`
87+
const found = options.findNamedChild(newReference, item)
88+
if (found !== newReference) return found
89+
}
8190

82-
return options.findNamedChild(reference, variables)
83-
}
84-
})
91+
return options.findNamedChild(reference, variables)
92+
},
93+
},
94+
`${op}.?`,
95+
)
8596

8697
if (test && (op === "$inarray" || op === "$any")) {
8798
return true

src/index.ts

Lines changed: 43 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -46,25 +46,41 @@ export function test<Context = Record<string, unknown>>(
4646

4747
const opts = options || {}
4848

49-
return realTest(input, context, {
50-
findNamedChild: opts.findNamedChild || findNamedChild,
51-
...opts,
52-
_path: opts._path || "ROOTOBJ",
53-
_currentLoopDepth: 0,
54-
logger: opts.logger || (() => {}),
55-
})
49+
return testWithPath(
50+
input,
51+
context,
52+
{
53+
findNamedChild: opts.findNamedChild || findNamedChild,
54+
...opts,
55+
_path: "",
56+
_currentLoopDepth: 0,
57+
logger: opts.logger || (() => {}),
58+
},
59+
"",
60+
)
5661
}
5762

5863
/**
5964
* Tiny wrapper function that calls {@link realTest} with a path specified.
6065
* The benefit of using this is that it's a single, inline call, instead of 4
6166
* lines per call.
6267
*/
63-
function testWithPath(input: any, context, options: TestOptions, name: string) {
64-
return realTest(input, context, {
68+
function testWithPath<Context>(
69+
input: any,
70+
context: Context,
71+
options: TestOptions,
72+
name: string,
73+
) {
74+
// the top-most call
75+
const thePath = options._path ? `${options._path}.${name}` : name
76+
const displayPath = thePath || "(root)"
77+
options.logger?.("visit", `Visiting ${displayPath}`)
78+
const result = realTest(input, context, {
6579
...options,
66-
_path: `${options._path}.${name}`,
80+
_path: thePath,
6781
})
82+
options.logger?.("trace", `${displayPath} evaluated to: ${result}`)
83+
return result
6884
}
6985

7086
function realTest<Variables>(
@@ -74,8 +90,6 @@ function realTest<Variables>(
7490
): Variables | boolean {
7591
const log = options.logger
7692

77-
log("visit", `Visiting ${options._path}`)
78-
7993
if (
8094
typeof input === "number" ||
8195
typeof input === "boolean" ||
@@ -169,7 +183,7 @@ function realTest<Variables>(
169183

170184
if (input.$inarray) {
171185
return handleArrayLogic(
172-
realTest,
186+
testWithPath,
173187
input,
174188
variables,
175189
"$inarray",
@@ -178,11 +192,23 @@ function realTest<Variables>(
178192
}
179193

180194
if (input.$any) {
181-
return handleArrayLogic(realTest, input, variables, "$any", options)
195+
return handleArrayLogic(
196+
testWithPath,
197+
input,
198+
variables,
199+
"$any",
200+
options,
201+
)
182202
}
183203

184204
if (input.$all) {
185-
return handleArrayLogic(realTest, input, variables, "$all", options)
205+
return handleArrayLogic(
206+
testWithPath,
207+
input,
208+
variables,
209+
"$all",
210+
options,
211+
)
186212
}
187213

188214
if (input.$after) {
@@ -264,7 +290,7 @@ function realTest<Variables>(
264290
)
265291

266292
if (typeof first === "string") {
267-
return first.includes(second)
293+
return first.includes(second as string)
268294
}
269295

270296
return false
@@ -279,7 +305,7 @@ function realTest<Variables>(
279305
/**
280306
* @internal
281307
*/
282-
export type RealTestFunc = typeof realTest
308+
export type TestWithPathFunc = typeof testWithPath
283309

284310
/**
285311
* Handles a group of action nodes (a.k.a. side-effect nodes).

0 commit comments

Comments
 (0)