All files / json-path util.ts

79.68% Statements 51/64
54.83% Branches 17/31
100% Functions 5/5
77.96% Lines 46/59

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 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138                  2x 7x   7x 15x 12x 12x     3x       7x             19x   11x   5x   2x 2x 2x 2x 2x 2x 2x     1x                         2x 4x 1x     3x 9x 9x   9x       9x       9x 9x 1x         2x             9x       9x   7x   2x                                             2x 5x   5x 10x 10x 6x 4x 1x 1x 1x           5x    
/**
 * JSONPath utility functions
 */
 
import type {JSONPath, AnySelector} from './types';
 
/**
 * Convert a JSONPath to a human-readable string representation
 */
export function jsonPathToString(jsonPath: JSONPath): string {
  let result = '$';
 
  for (const segment of jsonPath.segments) {
    if (segment.selectors.length === 1) {
      const selector = segment.selectors[0];
      result += selectorToString(selector);
    } else {
      // Multiple selectors (union) - not fully implemented
      result += '[' + segment.selectors.map(selectorToString).join(',') + ']';
    }
  }
 
  return result;
}
 
/**
 * Convert a selector to string representation
 */
function selectorToString(selector: AnySelector): string {
  switch (selector.type) {
    case 'name':
      return `.${selector.name}`;
    case 'index':
      return `[${selector.index}]`;
    case 'slice': {
      let sliceStr = '[';
      if (selector.start !== undefined) sliceStr += selector.start;
      sliceStr += ':';
      if (selector.end !== undefined) sliceStr += selector.end;
      if (selector.step !== undefined) sliceStr += `:${selector.step}`;
      sliceStr += ']';
      return sliceStr;
    }
    case 'wildcard':
      return '.*';
    case 'recursive-descent':
      return `..${selectorToString(selector.selector as AnySelector)}`;
    case 'filter':
      return '[?(...)]'; // Simplified representation
    default:
      return '';
  }
}
 
/**
 * Check if two JSONPath expressions are equivalent
 */
export function jsonPathEquals(path1: JSONPath, path2: JSONPath): boolean {
  if (path1.segments.length !== path2.segments.length) {
    return false;
  }
 
  for (let i = 0; i < path1.segments.length; i++) {
    const seg1 = path1.segments[i];
    const seg2 = path2.segments[i];
 
    Iif (seg1.recursive !== seg2.recursive) {
      return false;
    }
 
    Iif (seg1.selectors.length !== seg2.selectors.length) {
      return false;
    }
 
    for (let j = 0; j < seg1.selectors.length; j++) {
      if (!selectorEquals(seg1.selectors[j], seg2.selectors[j])) {
        return false;
      }
    }
  }
 
  return true;
}
 
/**
 * Check if two selectors are equivalent
 */
function selectorEquals(sel1: AnySelector, sel2: AnySelector): boolean {
  Iif (sel1.type !== sel2.type) {
    return false;
  }
 
  switch (sel1.type) {
    case 'name':
      return sel1.name === (sel2 as typeof sel1).name;
    case 'index':
      return sel1.index === (sel2 as typeof sel1).index;
    case 'slice': {
      const s2 = sel2 as typeof sel1;
      return sel1.start === s2.start && sel1.end === s2.end && sel1.step === s2.step;
    }
    case 'wildcard':
      return true;
    case 'recursive-descent': {
      const rdSel = sel2 as typeof sel1;
      return selectorEquals(sel1.selector as AnySelector, rdSel.selector as AnySelector);
    }
    case 'filter':
      // For now, just return true - would need deep comparison of filter expressions
      return true;
    default:
      return false;
  }
}
 
/**
 * Get all property names that could be accessed by a JSONPath
 * This is a simple analysis that doesn't handle complex filters
 */
export function getAccessedProperties(jsonPath: JSONPath): string[] {
  const properties: string[] = [];
 
  for (const segment of jsonPath.segments) {
    for (const selector of segment.selectors) {
      if (selector.type === 'name') {
        properties.push(selector.name);
      } else if (selector.type === 'recursive-descent') {
        if (selector.selector.type === 'name') {
          const nameSelector = selector.selector as Extract<AnySelector, {type: 'name'}>;
          properties.push(nameSelector.name);
        }
      }
    }
  }
 
  return properties;
}