| // Copyright (c) 2023, the Dart project authors. Please see the AUTHORS file |
| // for details. 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:yaml/yaml.dart'; |
| |
| import '../utils/uri.dart'; |
| import '../utils/yaml.dart'; |
| import 'link_mode.dart'; |
| import 'target.dart'; |
| |
| abstract class AssetPath { |
| factory AssetPath(String pathType, Uri? uri) { |
| switch (pathType) { |
| case AssetAbsolutePath._pathTypeValue: |
| return AssetAbsolutePath(uri!); |
| case AssetRelativePath._pathTypeValue: |
| return AssetRelativePath(uri!); |
| case AssetSystemPath._pathTypeValue: |
| return AssetSystemPath(uri!); |
| case AssetInExecutable._pathTypeValue: |
| return AssetInExecutable(); |
| case AssetInProcess._pathTypeValue: |
| return AssetInProcess(); |
| } |
| throw FormatException('Unknown pathType: $pathType.'); |
| } |
| |
| factory AssetPath.fromYaml(YamlMap yamlMap) { |
| final pathType = as<String>(yamlMap[_pathTypeKey]); |
| final uriString = as<String?>(yamlMap[_uriKey]); |
| final uri = uriString != null ? Uri(path: uriString) : null; |
| return AssetPath(pathType, uri); |
| } |
| |
| Map<String, Object> toYaml(); |
| List<String> toDartConst(); |
| |
| static const _pathTypeKey = 'path_type'; |
| static const _uriKey = 'uri'; |
| |
| Future<bool> exists(); |
| } |
| |
| /// Asset at absolute path [uri]. |
| class AssetAbsolutePath implements AssetPath { |
| final Uri uri; |
| |
| AssetAbsolutePath(this.uri); |
| |
| static const _pathTypeValue = 'absolute'; |
| |
| @override |
| Map<String, Object> toYaml() => { |
| AssetPath._pathTypeKey: _pathTypeValue, |
| AssetPath._uriKey: uri.toFilePath(), |
| }; |
| |
| @override |
| List<String> toDartConst() => [_pathTypeValue, uri.toFilePath()]; |
| |
| @override |
| int get hashCode => Object.hash(uri, 133711); |
| |
| @override |
| bool operator ==(Object other) { |
| if (other is! AssetAbsolutePath) { |
| return false; |
| } |
| return uri == other.uri; |
| } |
| |
| @override |
| Future<bool> exists() => uri.fileSystemEntity.exists(); |
| } |
| |
| /// Asset is avaliable on a relative path. |
| /// |
| /// If [LinkMode] of an [Asset] is [LinkMode.dynamic], |
| /// `Platform.script.resolve(uri)` will be used to load the asset at runtime. |
| class AssetRelativePath implements AssetPath { |
| final Uri uri; |
| |
| AssetRelativePath(this.uri); |
| |
| static const _pathTypeValue = 'relative'; |
| |
| @override |
| Map<String, Object> toYaml() => { |
| AssetPath._pathTypeKey: _pathTypeValue, |
| AssetPath._uriKey: uri.toFilePath(), |
| }; |
| |
| @override |
| List<String> toDartConst() => [_pathTypeValue, uri.toFilePath()]; |
| |
| @override |
| int get hashCode => Object.hash(uri, 133717); |
| |
| @override |
| bool operator ==(Object other) { |
| if (other is! AssetRelativePath) { |
| return false; |
| } |
| return uri == other.uri; |
| } |
| |
| @override |
| Future<bool> exists() => uri.fileSystemEntity.exists(); |
| } |
| |
| /// Asset is avaliable on the system `PATH`. |
| /// |
| /// [uri] only contains a file name. |
| class AssetSystemPath implements AssetPath { |
| final Uri uri; |
| |
| AssetSystemPath(this.uri); |
| |
| static const _pathTypeValue = 'system'; |
| |
| @override |
| Map<String, Object> toYaml() => { |
| AssetPath._pathTypeKey: _pathTypeValue, |
| AssetPath._uriKey: uri.toFilePath(), |
| }; |
| |
| @override |
| List<String> toDartConst() => [_pathTypeValue, uri.toFilePath()]; |
| |
| @override |
| int get hashCode => Object.hash(uri, 133723); |
| |
| @override |
| bool operator ==(Object other) { |
| if (other is! AssetSystemPath) { |
| return false; |
| } |
| return uri == other.uri; |
| } |
| |
| @override |
| Future<bool> exists() => Future.value(true); |
| } |
| |
| /// Asset is loaded in the process and symbols are available through |
| /// `DynamicLibrary.process()`. |
| class AssetInProcess implements AssetPath { |
| AssetInProcess._(); |
| |
| static final AssetInProcess _singleton = AssetInProcess._(); |
| |
| factory AssetInProcess() => _singleton; |
| |
| static const _pathTypeValue = 'process'; |
| |
| @override |
| Map<String, Object> toYaml() => { |
| AssetPath._pathTypeKey: _pathTypeValue, |
| }; |
| |
| @override |
| List<String> toDartConst() => [_pathTypeValue]; |
| |
| @override |
| Future<bool> exists() => Future.value(true); |
| } |
| |
| /// Asset is embedded in executable and symbols are available through |
| /// `DynamicLibrary.executable()`. |
| class AssetInExecutable implements AssetPath { |
| AssetInExecutable._(); |
| |
| static final AssetInExecutable _singleton = AssetInExecutable._(); |
| |
| factory AssetInExecutable() => _singleton; |
| |
| static const _pathTypeValue = 'executable'; |
| |
| @override |
| Map<String, Object> toYaml() => { |
| AssetPath._pathTypeKey: _pathTypeValue, |
| }; |
| |
| @override |
| List<String> toDartConst() => [_pathTypeValue]; |
| |
| @override |
| Future<bool> exists() => Future.value(true); |
| } |
| |
| class Asset { |
| final LinkMode linkMode; |
| final String id; |
| final Target target; |
| final AssetPath path; |
| |
| Asset({ |
| required this.id, |
| required this.linkMode, |
| required this.target, |
| required this.path, |
| }); |
| |
| factory Asset.fromYaml(YamlMap yamlMap) => Asset( |
| id: as<String>(yamlMap[_idKey]), |
| path: AssetPath.fromYaml(as<YamlMap>(yamlMap[_pathKey])), |
| target: Target.fromString(as<String>(yamlMap[_targetKey])), |
| linkMode: LinkMode.fromName(as<String>(yamlMap[_linkModeKey])), |
| ); |
| |
| static List<Asset> listFromYamlString(String yaml) { |
| final yamlObject = loadYaml(yaml); |
| if (yamlObject == null) { |
| return []; |
| } |
| return [ |
| for (final yamlElement in as<YamlList>(yamlObject)) |
| Asset.fromYaml(as<YamlMap>(yamlElement)), |
| ]; |
| } |
| |
| static List<Asset> listFromYamlList(YamlList yamlList) => [ |
| for (final yamlElement in yamlList) |
| Asset.fromYaml(as<YamlMap>(yamlElement)), |
| ]; |
| |
| Asset copyWith({ |
| LinkMode? linkMode, |
| String? id, |
| Target? target, |
| AssetPath? path, |
| }) => |
| Asset( |
| id: id ?? this.id, |
| linkMode: linkMode ?? this.linkMode, |
| target: target ?? this.target, |
| path: path ?? this.path, |
| ); |
| |
| @override |
| bool operator ==(Object other) { |
| if (other is! Asset) { |
| return false; |
| } |
| return other.id == id && |
| other.linkMode == linkMode && |
| other.target == target && |
| other.path == path; |
| } |
| |
| @override |
| int get hashCode => Object.hash(id, linkMode, target, path); |
| |
| Map<String, Object> toYaml() => { |
| _idKey: id, |
| _linkModeKey: linkMode.name, |
| _pathKey: path.toYaml(), |
| _targetKey: target.toString(), |
| }; |
| |
| Map<String, List<String>> toDartConst() => { |
| id: path.toDartConst(), |
| }; |
| |
| String toYamlString() => yamlEncode(toYaml()); |
| |
| static const _idKey = 'id'; |
| static const _linkModeKey = 'link_mode'; |
| static const _pathKey = 'path'; |
| static const _targetKey = 'target'; |
| |
| Future<bool> exists() => path.exists(); |
| |
| @override |
| String toString() => 'Asset(${toYaml()})'; |
| } |
| |
| extension AssetIterable on Iterable<Asset> { |
| List<Object> toYaml() => [for (final item in this) item.toYaml()]; |
| |
| String toYamlString() => yamlEncode(toYaml()); |
| |
| Iterable<Asset> whereLinkMode(LinkMode linkMode) => |
| where((e) => e.linkMode == linkMode); |
| |
| Map<Target, List<Asset>> get assetsPerTarget { |
| final result = <Target, List<Asset>>{}; |
| for (final asset in this) { |
| final assets = result[asset.target] ?? []; |
| assets.add(asset); |
| result[asset.target] = assets; |
| } |
| return result; |
| } |
| |
| Map<String, Map<String, List<String>>> toDartConst() => { |
| for (final entry in assetsPerTarget.entries) |
| entry.key.toString(): |
| _combineMaps(entry.value.map((e) => e.toDartConst()).toList()) |
| }; |
| |
| Map<Object, Object> toNativeAssetsFileEncoding() => { |
| 'format-version': [1, 0, 0], |
| 'native-assets': toDartConst(), |
| }; |
| |
| String toNativeAssetsFile() => yamlEncode(toNativeAssetsFileEncoding()); |
| |
| Future<bool> allExist() async { |
| final allResults = await Future.wait(map((e) => e.exists())); |
| final missing = allResults.contains(false); |
| return !missing; |
| } |
| } |
| |
| Map<X, Y> _combineMaps<X, Y>(Iterable<Map<X, Y>> maps) { |
| final result = <X, Y>{}; |
| for (final map in maps) { |
| result.addAll(map); |
| } |
| return result; |
| } |