parsePath function
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;
}