blob: 616688f0b942d315d4a61678dd0a32cd231c5e51 [file] [log] [blame] [edit]
// 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;
}