parseSelector function

Selector parseSelector(
  1. IPLDNode node
)

Parse an IPLDNode (decoded from DAG-CBOR or DAG-JSON) into a typed Selector.

Accepts the compact spec keys (., a, f, i, r, R, |, &, @, ~) and, leniently, their long-form names (matcher, exploreAll, ...). Malformed shapes are rejected with SelectorParseError.

Implementation

Selector parseSelector(IPLDNode node) {
  if (node.kind != Kind.MAP) {
    throw SelectorParseError('Selector must be a map');
  }
  final entries = node.mapValue.entries;
  if (entries.length != 1) {
    throw SelectorParseError(
      'Selector map must contain exactly one key, found ${entries.length}',
    );
  }
  final key = entries.first.key;
  final value = entries.first.value;

  switch (key) {
    case '.':
    case 'matcher':
      return _parseMatcher(value);
    case 'a':
    case 'exploreAll':
      final body = _requireMap(value, 'body of "exploreAll"');
      return ExploreAll(next: _parseSelectorField(body, const ['>', 'next']));
    case 'f':
    case 'exploreFields':
      final body = _requireMap(value, 'body of "exploreFields"');
      final fieldsNode = _requireFieldAny(body, const ['f>', 'fields']);
      final fieldsMap = _requireMap(fieldsNode, 'exploreFields fields');
      final fields = <String, Selector>{};
      for (final entry in fieldsMap.entries) {
        fields[entry.key] = parseSelector(entry.value);
      }
      return ExploreFields(fields: fields);
    case 'i':
    case 'exploreIndex':
      final body = _requireMap(value, 'body of "exploreIndex"');
      return ExploreIndex(
        index: _requireInt(
          _requireFieldAny(body, const ['i', 'index']),
          'exploreIndex index',
        ),
        next: _parseSelectorField(body, const ['>', 'next']),
      );
    case 'r':
    case 'exploreRange':
      final body = _requireMap(value, 'body of "exploreRange"');
      return ExploreRange(
        start: _requireInt(
          _requireFieldAny(body, const ['^', 'start']),
          'exploreRange start',
        ),
        end: _requireInt(
          _requireFieldAny(body, const ['\$', 'end']),
          'exploreRange end',
        ),
        next: _parseSelectorField(body, const ['>', 'next']),
      );
    case 'R':
    case 'exploreRecursive':
      final body = _requireMap(value, 'body of "exploreRecursive"');
      final limitNode = _requireFieldAny(body, const ['l', 'limit']);
      final sequence = _parseSelectorField(body, const [':>', 'sequence']);
      Condition? stopAt;
      final stopAtNode = _optionalFieldAny(body, const ['!', 'stopAt']);
      if (stopAtNode != null) {
        stopAt = parseCondition(stopAtNode);
      }
      return ExploreRecursive(
        limit: _parseRecursionLimit(limitNode),
        sequence: sequence,
        stopAt: stopAt,
      );
    case '@':
    case 'exploreRecursiveEdge':
      return const ExploreRecursiveEdge();
    case '|':
    case 'exploreUnion':
      final membersList = _requireList(value, 'exploreUnion members');
      return ExploreUnion(
        members: membersList.values.map(parseSelector).toList(),
      );
    case '&':
    case 'exploreConditional':
      final body = _requireMap(value, 'body of "exploreConditional"');
      Condition? condition;
      final conditionNode = _optionalFieldAny(body, const ['&', 'condition']);
      if (conditionNode != null) {
        condition = parseCondition(conditionNode);
      }
      Selector? next;
      final nextNode = _optionalFieldAny(body, const ['>', 'next']);
      if (nextNode != null) {
        next = parseSelector(nextNode);
      }
      return ExploreConditional(condition: condition, next: next);
    case '~':
    case 'exploreInterpretAs':
    case 'interpretAs':
      final body = _requireMap(value, 'body of "interpretAs"');
      return ExploreInterpretAs(
        adl: _requireString(
          _requireFieldAny(body, const ['as', 'adl']),
          'interpretAs as',
        ),
        next: _parseSelectorField(body, const ['>', 'next']),
      );
    default:
      throw SelectorParseError('Unknown selector key: "$key"');
  }
}