| // Copyright 2019 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| import 'package:fxtest/fxtest.dart'; |
| import 'package:meta/meta.dart'; |
| |
| /// Deconstructed Fuchsia Package Url used to precisely target URL components. |
| class PackageUrl { |
| /// Root chunk of the Package URL. |
| /// |
| /// ``` |
| /// fuchsia.com |
| /// ``` |
| /// |
| /// from |
| /// |
| /// ``` |
| /// fuchsia-pkg://fuchsia.com/pkg-name/variant?hash=1234#meta/component-name.cmx |
| /// ``` |
| final String host; |
| |
| /// First chunk from the URI. |
| /// |
| /// ``` |
| /// pkg-name |
| /// ``` |
| /// |
| /// from |
| /// |
| /// ``` |
| /// fuchsia-pkg://fuchsia.com/pkg-name/variant?hash=1234#meta/component-name.cmx |
| /// ``` |
| final String packageName; |
| |
| /// Optional. Second chunk in the URI. |
| /// |
| /// ``` |
| /// variant |
| /// ``` |
| /// |
| /// from |
| /// |
| /// ``` |
| /// fuchsia-pkg://fuchsia.com/pkg-name/variant?hash=1234#meta/component-name.cmx |
| /// ``` |
| final String packageVariant; |
| |
| /// Optional. Value for "hash" querystring value. |
| /// |
| /// ``` |
| /// 1234 |
| /// ``` |
| /// |
| /// from |
| /// |
| /// ``` |
| /// fuchsia-pkg://fuchsia.com/pkg-name/variant?hash=1234#meta/component-name.cmx |
| /// ``` |
| final String hash; |
| |
| /// Component name with the extension. |
| /// |
| /// ``` |
| /// component-name.cmx |
| /// ``` |
| /// |
| /// from |
| /// |
| /// ``` |
| /// fuchsia-pkg://fuchsia.com/pkg-name/variant?hash=1234#meta/component-name.cmx |
| /// ``` |
| final String fullComponentName; |
| |
| /// Component name without the extension. |
| /// |
| /// ``` |
| /// component-name |
| /// ``` |
| /// |
| /// from |
| /// |
| /// ``` |
| /// fuchsia-pkg://fuchsia.com/pkg-name/variant?hash=1234#meta/component-name.cmx |
| /// ``` |
| final String componentName; |
| |
| String _stringifed; |
| |
| PackageUrl({ |
| @required this.host, |
| @required this.packageName, |
| @required this.packageVariant, |
| @required this.hash, |
| @required this.fullComponentName, |
| @required this.componentName, |
| }); |
| |
| PackageUrl.copyWithHash({ |
| @required PackageUrl other, |
| @required this.hash, |
| }) : host = other.host, |
| packageName = other.packageName, |
| packageVariant = other.packageVariant, |
| fullComponentName = other.fullComponentName, |
| componentName = other.componentName; |
| |
| /// Breaks out a canonical Fuchsia URL into its constituent parts. |
| /// |
| /// Parses something like |
| /// `fuchsia-pkg://host/package_name/variant?hash=1234#PATH.cmx` into: |
| /// |
| /// ```dart |
| /// PackageUrl( |
| /// 'host': 'host', |
| /// 'packageName': 'package_name', |
| /// 'packageVariant': 'variant', |
| /// 'hash': '1234', |
| /// 'fullComponentName': 'PATH.cmx', |
| /// 'componentName': 'PATH', |
| /// ); |
| /// ``` |
| factory PackageUrl.fromString(String packageUrl) { |
| Uri parsedUri = Uri.parse(packageUrl); |
| |
| if (parsedUri.scheme != 'fuchsia-pkg') { |
| throw MalformedFuchsiaUrlException(packageUrl); |
| } |
| |
| return PackageUrl( |
| host: parsedUri.host, |
| packageName: |
| parsedUri.pathSegments.isNotEmpty ? parsedUri.pathSegments[0] : null, |
| packageVariant: |
| parsedUri.pathSegments.length > 1 ? parsedUri.pathSegments[1] : null, |
| hash: parsedUri.queryParameters['hash'], |
| fullComponentName: PackageUrl._removeMetaPrefix(parsedUri.fragment), |
| componentName: PackageUrl._removeMetaPrefix( |
| PackageUrl._removeExtension(parsedUri.fragment), |
| ), |
| ); |
| } |
| |
| /// Returns something like |
| /// `fuchsia-pkg://host/package_name/variant?hash=1234#PATH.cmx` |
| @override |
| String toString() { |
| if (_stringifed == null) { |
| var path = [packageName]; |
| if (packageVariant != null) path.add(packageVariant); |
| _stringifed = Uri( |
| scheme: 'fuchsia-pkg', |
| host: host, |
| pathSegments: path, |
| fragment: fullComponentName == null ? null : 'meta/$fullComponentName', |
| queryParameters: hash == null ? null : {'hash': hash}, |
| ).toString(); |
| } |
| return _stringifed; |
| } |
| |
| static String _removeMetaPrefix(String fullComponentName) { |
| const token = 'meta/'; |
| return fullComponentName.startsWith(token) |
| ? fullComponentName.substring(token.length) |
| : fullComponentName; |
| } |
| |
| static String _removeExtension(String fullComponentName) { |
| // Guard against uninteresting edge cases |
| if (fullComponentName == null || !fullComponentName.contains('.')) { |
| return fullComponentName ?? ''; |
| } |
| return fullComponentName.substring(0, fullComponentName.lastIndexOf('.')); |
| } |
| } |