* Wraps a logic function such that it returns the special `IGNORED` sentinel value if any of the * given predicates evaluates to false. * * @param predicates A list of bound predicates to apply to the logic function * @param logicFn The logic function to wrap * @returns A wrapped version of the
( predicates: ReadonlyArray<BoundPredicate>, logicFn: LogicFn<TValue, TReturn>, )
| 219 | * @returns A wrapped version of the logic function that may return `IGNORED`. |
| 220 | */ |
| 221 | function wrapWithPredicates<TValue, TReturn>( |
| 222 | predicates: ReadonlyArray<BoundPredicate>, |
| 223 | logicFn: LogicFn<TValue, TReturn>, |
| 224 | ): LogicFn<TValue, TReturn | typeof IGNORED> { |
| 225 | if (predicates.length === 0) { |
| 226 | return logicFn; |
| 227 | } |
| 228 | return (arg: FieldContext<any>): TReturn | typeof IGNORED => { |
| 229 | for (const predicate of predicates) { |
| 230 | let predicateField = arg.stateOf(predicate.path) as FieldNode; |
| 231 | // Check the depth of the current field vs the depth this predicate is supposed to be |
| 232 | // evaluated at. If necessary, walk up the field tree to grab the correct context field. |
| 233 | // We can check the pathKeys as an untracked read since we know the structure of our fields |
| 234 | // doesn't change. |
| 235 | const depthDiff = untracked(predicateField.structure.pathKeys).length - predicate.depth; |
| 236 | for (let i = 0; i < depthDiff; i++) { |
| 237 | predicateField = predicateField.structure.parent!; |
| 238 | } |
| 239 | // If any of the predicates don't match, don't actually run the logic function, just return |
| 240 | // the default value. |
| 241 | if (!predicate.fn(predicateField.context)) { |
| 242 | return IGNORED; |
| 243 | } |
| 244 | } |
| 245 | return logicFn(arg); |
| 246 | }; |
| 247 | } |
| 248 | |
| 249 | /** |
| 250 | * Container for all the different types of logic that can be applied to a field |