parsePath function

List<PathSegment> parsePath(
  1. String path
)

Parses a jq-inspired path string (e.g. .data.user.id, .data.users[0], .data.users[]) into a list of PathSegments by walking characters directly — no regex, no intermediate string splitting.

Implementation

List<PathSegment> parsePath(String path) {
  if (path.isEmpty) {
    throw const JsonQuerySyntaxException('Path must not be empty');
  }

  final firstUnit = path.codeUnitAt(0);
  if (firstUnit != _dot && firstUnit != _openBracket) {
    throw JsonQuerySyntaxException('Path "$path" must start with "." or "["');
  }

  final segments = <PathSegment>[];
  final len = path.length;
  var i = 0;

  while (i < len) {
    final unit = path.codeUnitAt(i);
    if (unit == _dot) {
      i++;
      final start = i;
      while (i < len && path.codeUnitAt(i) != _dot && path.codeUnitAt(i) != _openBracket) {
        i++;
      }
      if (i == start) {
        throw JsonQuerySyntaxException('Path "$path" has an empty field name at position $start');
      }
      segments.add(FieldSegment(path.substring(start, i)));
    } else if (unit == _openBracket) {
      i++;
      final start = i;
      while (i < len && path.codeUnitAt(i) != _closeBracket) {
        i++;
      }
      if (i >= len) {
        throw JsonQuerySyntaxException('Path "$path" has an unterminated "["');
      }
      final content = path.substring(start, i);
      i++; // consume ']'
      if (content.isEmpty) {
        segments.add(const WildcardSegment());
      } else {
        final index = int.tryParse(content);
        if (index == null || index < 0) {
          throw JsonQuerySyntaxException('Path "$path" has an invalid array index "$content"');
        }
        segments.add(IndexSegment(index));
      }
    } else {
      throw JsonQuerySyntaxException(
        'Path "$path" has an unexpected character "${path[i]}" at position $i; expected "." or "["',
      );
    }
  }

  return segments;
}