FanetJoinLink.parse constructor

FanetJoinLink.parse(
  1. String link
)

Parses a join link such as https://network.fa1.dev/join?network=net-1#pw=s3cret.

Throws a FormatException when link is not a valid fa_network join link: wrong scheme, wrong path, missing network query parameter, or an unexpected fragment shape.

Implementation

factory FanetJoinLink.parse(String link) {
  final Uri uri;
  try {
    uri = Uri.parse(link);
  } on FormatException catch (e) {
    throw FormatException('Invalid fa_network join link: $link', e);
  }
  if (!uri.isScheme('http') && !uri.isScheme('https')) {
    throw FormatException(
      'fa_network join link must use http(s), got ${uri.scheme}: $link',
    );
  }
  if (uri.host.isEmpty) {
    throw FormatException('fa_network join link has no host: $link');
  }
  if (uri.pathSegments.length != 1 || uri.pathSegments.single != 'join') {
    throw FormatException(
      'fa_network join link must have path /join, got ${uri.path}: $link',
    );
  }
  final networkId = uri.queryParameters['network'];
  if (networkId == null || networkId.isEmpty) {
    throw FormatException(
      'fa_network join link is missing the network query parameter: $link',
    );
  }
  String? password;
  if (uri.hasFragment && uri.fragment.isNotEmpty) {
    // Uri.fragment is NOT percent-decoded (unlike queryParameters).
    final raw = uri.fragment;
    if (!raw.startsWith(_fragmentPrefix)) {
      throw FormatException(
        'fa_network join link has an unexpected fragment '
        '"$raw" (expected "pw=<password>"): $link',
      );
    }
    String decoded;
    try {
      decoded = Uri.decodeComponent(raw.substring(_fragmentPrefix.length));
    } on ArgumentError {
      throw FormatException(
        'fa_network join link fragment is not valid percent-encoding: $link',
      );
    }
    if (decoded.isNotEmpty) password = decoded;
  }
  return FanetJoinLink(
    restBase: Uri(
      scheme: uri.scheme,
      host: uri.host,
      port: uri.hasPort ? uri.port : null,
    ),
    networkId: networkId,
    password: password,
  );
}