tryParse static method

Address? tryParse(
  1. String source
)

Null rather than throwing, for the many places that read user input.

Implementation

static Address? tryParse(String source) {
  const prefix = '$scheme://';
  if (!source.startsWith(prefix)) return null;

  var rest = source.substring(prefix.length);

  // The fragment is not part of the grammar. Reject rather than silently drop
  // it — a plugin segment containing `#` has to arrive percent-encoded, and
  // quietly truncating there would lose half of an entry id.
  if (rest.contains('#')) return null;

  var axes = const <String, String>{};
  var queryStart = rest.indexOf('?');
  if (queryStart >= 0) {
    var query = rest.substring(queryStart + 1);
    rest = rest.substring(0, queryStart);
    var parsed = <String, String>{};
    for (var pair in query.split('&')) {
      if (pair.isEmpty) continue;
      var eq = pair.indexOf('=');
      if (eq < 0) return null;
      var key = _decode(pair.substring(0, eq));
      var value = _decode(pair.substring(eq + 1));
      if (key == null || value == null) return null;
      parsed[key] = value;
    }
    axes = parsed;
  }

  // Hand-parsed rather than `Uri.parse`, to keep the percent-encoding
  // deliberately sparse — `_encode` escapes only what would be read as
  // structure, so `team.dart%23TeamList` stays legible where `Uri` would
  // escape more. The two agree on everything they both decide, and
  // `address_test.dart` asserts it.
  var parts = rest.split('/');
  // The authority. Empty is the normal case and means "this session's
  // project"; it is not a segment and never was one.
  var project = parts.first;
  var path = parts.skip(1).toList();
  // `fw:///worktrees/wt/` and `fw:///worktrees/wt` name the same worktree.
  if (path.isNotEmpty && path.last.isEmpty) path.removeLast();
  if (path.any((s) => s.isEmpty)) return null;

  var decoded = <String>[];
  for (var part in [project, ...path]) {
    var value = _decode(part);
    if (value == null) return null;
    decoded.add(value);
  }
  var decodedProject = decoded.first;
  var decodedPath = decoded.skip(1).toList();

  // The constructor asserts what the grammar cannot — that a worktree lives
  // in the worktrees space, and so on — and those refusals are this
  // function's `null` rather than its caller's exception.
  try {
    return Address(
      project: decodedProject.isEmpty ? null : decodedProject,
      space: decodedPath.isEmpty ? null : decodedPath.first,
      worktree: decodedPath.length < 2 ? null : decodedPath[1],
      plugin: decodedPath.length < 3 ? null : decodedPath[2],
      segments: decodedPath.skip(3).toList(),
      axes: axes,
    );
  } on ArgumentError {
    return null;
  }
}