All files / json-expression/src/operators input.ts

98% Statements 49/50
90% Branches 9/10
100% Functions 5/5
100% Lines 46/46

Press n or j to go to the next uncovered block, b, p or k for the previous block.

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 763x 3x 3x   3x     3x 553x 552x 552x 552x 552x 552x 552x     3x           530x 530x 530x 530x     518x 518x 518x 515x 515x 515x 362x 362x 362x 362x 362x 362x 362x   3x 3x                   19x 19x 18x     19x 19x 18x 17x 17x 17x 17x 17x 17x   1x 1x          
import {Expression, type ExpressionResult, Literal} from '../codegen-steps';
import * as util from '../util';
import * as jsonPointer from '@jsonjoy.com/json-pointer';
import type {Vars} from '../Vars';
import {$$find} from '@jsonjoy.com/json-pointer/lib/codegen/find';
import type * as types from '../types';
 
const get = (vars: Vars, varname: unknown) => {
  if (typeof varname !== 'string') throw new Error('varname must be a string.');
  const [name, pointer] = util.parseVar(varname);
  jsonPointer.validateJsonPointer(pointer);
  const data = vars.get(name);
  const path = jsonPointer.toPath(pointer);
  const value = jsonPointer.get(data, path);
  return value;
};
 
export const inputOperators: types.OperatorDefinition<any>[] = [
  [
    '$',
    ['get'],
    [1, 2],
    (expr: types.ExprGet, ctx: types.OperatorEvalCtx) => {
      const varname = ctx.eval(expr[1], ctx);
      const defval = ctx.eval(expr[2], ctx);
      const value = get(ctx.vars, varname);
      return util.throwOnUndef(value, defval);
    },
    (ctx: types.OperatorCodegenCtx<types.ExprGet>): ExpressionResult => {
      ctx.link(util.throwOnUndef, 'throwOnUndef');
      const varname = ctx.operands[0];
      if (varname instanceof Literal) {
        Iif (typeof varname.val !== 'string') throw new Error('varname must be a string.');
        const [name, pointer] = util.parseVar(varname.val);
        if (!pointer) return new Expression(!name ? 'vars.env' : `vars.get(${JSON.stringify(name)})`);
        jsonPointer.validateJsonPointer(pointer);
        const hasDefaultValue = ctx.expr.length === 3;
        const defaultValue = hasDefaultValue ? ctx.operands[1] : undefined;
        const fn = $$find(jsonPointer.toPath(pointer));
        const find = ctx.const(fn);
        const data = `vars.get(${JSON.stringify(name)})`;
        return new Expression(`throwOnUndef(${find}(${data}),(${defaultValue}))`);
      }
      ctx.link(get, 'get');
      return new Expression(`throwOnUndef(get(vars,(${varname})),(${ctx.operands[1]}))`);
    },
    /* has side-effects */ true,
  ] as types.OperatorDefinition<types.ExprGet>,
 
  [
    '$?',
    ['get?'],
    1,
    (expr: types.ExprDefined, ctx: types.OperatorEvalCtx) => {
      const varname = ctx.eval(expr[1], ctx);
      const value = get(ctx.vars, varname);
      return value !== undefined;
    },
    (ctx: types.OperatorCodegenCtx<types.ExprDefined>): ExpressionResult => {
      const varname = ctx.operands[0];
      if (varname instanceof Literal) {
        if (typeof varname.val !== 'string') throw new Error('varname must be a string.');
        const [name, pointer] = util.parseVar(varname.val);
        jsonPointer.validateJsonPointer(pointer);
        const fn = $$find(jsonPointer.toPath(pointer));
        const find = ctx.const(fn);
        const data = `vars.get(${JSON.stringify(name)})`;
        return new Expression(`${find}(${data})!==undefined`);
      }
      ctx.link(get, 'get');
      return new Expression(`get(vars,(${varname}))!==undefined`);
    },
    /* has side-effects */ true,
  ] as types.OperatorDefinition<types.ExprDefined>,
];