| // Copyright (c) 2017, 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 'dart:async'; |
| |
| import 'package:front_end/src/api_unstable/build_integration.dart'; |
| |
| /// Wraps a file system to create an overlay of files from multiple roots. |
| /// |
| /// Regular `file:` URIs are resolved directly in the underlying file system, |
| /// but URIs that use a special [markerScheme] are resolved by searching |
| /// under a set of given roots in order. |
| /// |
| /// For example, consider the following inputs: |
| /// |
| /// - markerScheme is `multi-root` |
| /// - the set of roots are `file:///a/` and `file:///b/` |
| /// - the underlying file system contains files: |
| /// a/c/a1.dart |
| /// a/c/a2.dart |
| /// b/c/b1.dart |
| /// b/c/a2.dart |
| /// c/c1.dart |
| /// |
| /// Then: |
| /// |
| /// - file:///c/c1.dart is resolved as file:///c/c1.dart |
| /// - multi-root:///c/a1.dart is resolved as file:///a/c/a1.dart |
| /// - multi-root:///c/b1.dart is resolved as file:///b/c/b1.dart |
| /// - multi-root:///c/a2.dart is resolved as file:///a/c/b2.dart |
| class MultiRootFileSystem implements FileSystem { |
| final String markerScheme; |
| final List<Uri> roots; |
| final FileSystem original; |
| |
| MultiRootFileSystem(this.markerScheme, List roots, this.original) |
| : roots = roots.map(_normalize).toList(); |
| |
| @override |
| FileSystemEntity entityForUri(Uri uri) => |
| new MultiRootFileSystemEntity(this, uri); |
| } |
| |
| /// Entity that searches the multiple roots and resolve a, possibly multi-root, |
| /// entity to a plain entity under `multiRootFileSystem.original`. |
| class MultiRootFileSystemEntity implements FileSystemEntity { |
| final MultiRootFileSystem multiRootFileSystem; |
| final Uri uri; |
| FileSystemEntity? _delegate; |
| Future<FileSystemEntity> get delegate async => |
| _delegate ??= await _resolveEntity(); |
| |
| Future<FileSystemEntity> _resolveEntity() async { |
| if (uri.scheme == multiRootFileSystem.markerScheme) { |
| if (!uri.isAbsolute) { |
| throw new FileSystemException( |
| uri, "This MultiRootFileSystem only handles absolutes URIs: $uri"); |
| } |
| var original = multiRootFileSystem.original; |
| assert(uri.path.startsWith('/')); |
| var path = uri.path.substring(1); |
| for (var root in multiRootFileSystem.roots) { |
| var candidate = original.entityForUri(root.resolve(path)); |
| if (await candidate.exists()) return candidate; |
| } |
| return MissingFileSystemEntity(uri); |
| } |
| return multiRootFileSystem.original.entityForUri(uri); |
| } |
| |
| MultiRootFileSystemEntity(this.multiRootFileSystem, this.uri); |
| |
| @override |
| Future<bool> exists() async => (await delegate).exists(); |
| |
| @override |
| Future<bool> existsAsyncIfPossible() async => |
| (await delegate).existsAsyncIfPossible(); |
| |
| @override |
| Future<List<int>> readAsBytes() async => (await delegate).readAsBytes(); |
| |
| @override |
| Future<List<int>> readAsBytesAsyncIfPossible() async => |
| (await delegate).readAsBytes(); |
| |
| @override |
| Future<String> readAsString() async => (await delegate).readAsString(); |
| } |
| |
| class MissingFileSystemEntity implements FileSystemEntity { |
| @override |
| final Uri uri; |
| |
| MissingFileSystemEntity(this.uri); |
| |
| @override |
| Future<bool> exists() => Future.value(false); |
| |
| @override |
| Future<bool> existsAsyncIfPossible() => exists(); |
| |
| @override |
| Future<List<int>> readAsBytes() => |
| Future.error(FileSystemException(uri, 'File not found')); |
| |
| @override |
| Future<List<int>> readAsBytesAsyncIfPossible() => readAsBytes(); |
| |
| @override |
| Future<String> readAsString() => |
| Future.error(FileSystemException(uri, 'File not found')); |
| } |
| |
| Uri _normalize(root) { |
| Uri uri = root; |
| return uri.path.endsWith('/') ? uri : uri.replace(path: '${uri.path}/'); |
| } |