PathTemplate.parse constructor

PathTemplate.parse(
  1. String source
)

Parses static segments, :name, and final **:name captures.

Implementation

factory PathTemplate.parse(String source) {
  final normalized = source.trim();
  if (normalized.isEmpty || normalized == '/') {
    return PathTemplate._(source, const <_Part>[], const <String>{});
  }
  final text = normalized.startsWith('/')
      ? normalized.substring(1)
      : normalized;
  final path = text.endsWith('/') ? text.substring(0, text.length - 1) : text;
  final segments = path.split('/');
  final parts = <_Part>[];
  final names = <String>{};

  for (var index = 0; index < segments.length; index++) {
    final segment = segments[index];
    if (segment.isEmpty) {
      throw PathSyntaxException('Path contains an empty segment.', source);
    }
    if (segment.startsWith('**:')) {
      final name = segment.substring(3);
      _validateName(name, source, names);
      if (index != segments.length - 1) {
        throw PathSyntaxException(
          'A rest parameter must be the final segment.',
          source,
        );
      }
      parts.add(_RestPart(name));
      continue;
    }
    if (segment.startsWith(':')) {
      final name = segment.substring(1);
      _validateName(name, source, names);
      parts.add(_ParameterPart(name));
      continue;
    }
    if (segment.contains(':') ||
        segment.contains('*') ||
        segment.contains('(') ||
        segment.contains('{')) {
      throw PathSyntaxException(
        'Typed routes support only static, :name, and **:name segments.',
        source,
      );
    }
    parts.add(_StaticPart(segment));
  }

  return PathTemplate._(
    source,
    List<_Part>.of(parts, growable: false),
    Set<String>.of(names),
  );
}