diff --git a/src/arrayHandling.ts b/src/arrayHandling.ts index bdcd065..551a29a 100644 --- a/src/arrayHandling.ts +++ b/src/arrayHandling.ts @@ -14,13 +14,13 @@ * limitations under the License. */ -import type { RealTestFunc, TestOptions } from "./index" +import type { TestOptions, TestWithPathFunc } from "./index" const fillHashtags = (count: number): string => "#".repeat(count) /** * Handles `$any`, `$all`, and `$inarray`. Works with nested loops! - * @param realTest The realTest function. + * @param testWithPath The testWithPath function. * @param input The state machine. * @param variables The variables. * @param op The operation being performed. @@ -28,60 +28,71 @@ const fillHashtags = (count: number): string => "#".repeat(count) * @internal */ export function handleArrayLogic( - realTest: RealTestFunc, + testWithPath: TestWithPathFunc, input: any, variables: Variables, op: string, - options: TestOptions + options: TestOptions, ): boolean { const inValue = input[op]["in"] const depth = (options._currentLoopDepth || 0) + 1 if (inValue.includes("#")) { - throw new TypeError("Nested array nodes cannot use current iteration (`$.#`) as an `in` value", { - cause: options._path - }) + throw new TypeError( + "Nested array nodes cannot use current iteration (`$.#`) as an `in` value", + { + cause: options._path, + }, + ) } // find the array - const array = realTest(inValue, variables, { - ...options, - _currentLoopDepth: depth, - _path: `${options._path}.${op}.in` - }) as unknown as unknown[] + const array = testWithPath( + inValue, + variables, + { + ...options, + _currentLoopDepth: depth, + }, + `${op}.in`, + ) as unknown as unknown[] const itemConditions = input[op]["?"] for (const item of array) { - const test = realTest(itemConditions, variables, { - ...options, - _currentLoopDepth: depth, - _path: `${options._path}.${op}.?`, - findNamedChild(reference, variables) { - // NOTE: if we have a multi-layered loop, this should one-by-one fall back until the targeted loop is hit - const hashtags = fillHashtags(depth) + const test = testWithPath( + itemConditions, + variables, + { + ...options, + _currentLoopDepth: depth, + findNamedChild(reference, variables) { + // NOTE: if we have a multi-layered loop, this should one-by-one fall back until the targeted loop is hit + const hashtags = fillHashtags(depth) - // a little future-proofing, as sometimes the $ is there, and other times it isn't. - // we strip it out somewhere, but it shouldn't matter too much. - if ( - reference === `$.${hashtags}` || - reference === `.${hashtags}` - ) { - return item - } + // a little future-proofing, as sometimes the $ is there, and other times it isn't. + // we strip it out somewhere, but it shouldn't matter too much. + if ( + reference === `$.${hashtags}` || + reference === `.${hashtags}` + ) { + return item + } - // handle properties of an object - if (typeof item === "object") { - const newReference = `$${reference.substring( - reference.indexOf("#.") + 1 - )}` - const found = options.findNamedChild(newReference, item) - if (found !== newReference) return found - } + // handle properties of an object + if (typeof item === "object") { + const newReference = `$${reference.substring( + reference.indexOf("#.") + 1, + )}` + const found = options.findNamedChild(newReference, item) + if (found !== newReference) return found + } - return options.findNamedChild(reference, variables) - } - }) + return options.findNamedChild(reference, variables) + }, + }, + `${op}.?`, + ) if (test && (op === "$inarray" || op === "$any")) { return true diff --git a/src/index.ts b/src/index.ts index ff65228..30cac9d 100644 --- a/src/index.ts +++ b/src/index.ts @@ -46,13 +46,18 @@ export function test>( const opts = options || {} - return realTest(input, context, { - findNamedChild: opts.findNamedChild || findNamedChild, - ...opts, - _path: opts._path || "ROOTOBJ", - _currentLoopDepth: 0, - logger: opts.logger || (() => {}), - }) + return testWithPath( + input, + context, + { + findNamedChild: opts.findNamedChild || findNamedChild, + ...opts, + _path: "", + _currentLoopDepth: 0, + logger: opts.logger || (() => {}), + }, + "", + ) } /** @@ -60,11 +65,22 @@ export function test>( * The benefit of using this is that it's a single, inline call, instead of 4 * lines per call. */ -function testWithPath(input: any, context, options: TestOptions, name: string) { - return realTest(input, context, { +function testWithPath( + input: any, + context: Context, + options: TestOptions, + name: string, +) { + // the top-most call + const thePath = options._path ? `${options._path}.${name}` : name + const displayPath = thePath || "(root)" + options.logger?.("visit", `Visiting ${displayPath}`) + const result = realTest(input, context, { ...options, - _path: `${options._path}.${name}`, + _path: thePath, }) + options.logger?.("trace", `${displayPath} evaluated to: ${result}`) + return result } function realTest( @@ -74,8 +90,6 @@ function realTest( ): Variables | boolean { const log = options.logger - log("visit", `Visiting ${options._path}`) - if ( typeof input === "number" || typeof input === "boolean" || @@ -169,7 +183,7 @@ function realTest( if (input.$inarray) { return handleArrayLogic( - realTest, + testWithPath, input, variables, "$inarray", @@ -178,11 +192,23 @@ function realTest( } if (input.$any) { - return handleArrayLogic(realTest, input, variables, "$any", options) + return handleArrayLogic( + testWithPath, + input, + variables, + "$any", + options, + ) } if (input.$all) { - return handleArrayLogic(realTest, input, variables, "$all", options) + return handleArrayLogic( + testWithPath, + input, + variables, + "$all", + options, + ) } if (input.$after) { @@ -264,7 +290,7 @@ function realTest( ) if (typeof first === "string") { - return first.includes(second) + return first.includes(second as string) } return false @@ -279,7 +305,7 @@ function realTest( /** * @internal */ -export type RealTestFunc = typeof realTest +export type TestWithPathFunc = typeof testWithPath /** * Handles a group of action nodes (a.k.a. side-effect nodes).