Don't throw for missing directories (#60)
Closes #59
Treated as non-breaking because it only turns errors into non-errors.
Ignore all missing directory errors from the filesystem, instead of only
ignoring those which come from a directory operation after the first
wildcard.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7dd6382..dbef334 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+## 2.1.0-dev
+
+* Return empty results instead of throwing when trying to list a path that does
+ not exist.
+
## 2.0.2
* Drop package:pedantic dependency, use package:lints instead.
diff --git a/lib/src/list_tree.dart b/lib/src/list_tree.dart
index ece6f09..2a6c020 100644
--- a/lib/src/list_tree.dart
+++ b/lib/src/list_tree.dart
@@ -319,6 +319,7 @@
return fileSystem
.directory(dir)
.list(recursive: true, followLinks: followLinks)
+ .ignoreMissing()
.where((entity) => _matches(p.relative(entity.path, from: dir)));
}
@@ -340,6 +341,7 @@
var entities = await fileSystem
.directory(dir)
.list(followLinks: followLinks)
+ .ignoreMissing()
.toList();
await _validateIntermediateChildrenAsync(dir, entities, fileSystem);
@@ -353,17 +355,8 @@
children!.forEach((sequence, child) {
if (entity is! Directory) return;
if (!sequence.matches(basename)) return;
- var stream = child
- .list(p.join(dir, basename), fileSystem, followLinks: followLinks)
- .handleError((_) {}, test: (error) {
- // Ignore errors from directories not existing. We do this here so
- // that we only ignore warnings below wild cards. For example, the
- // glob "foo/bar/*/baz" should fail if "foo/bar" doesn't exist but
- // succeed if "foo/bar/qux/baz" doesn't exist.
- return error is FileSystemException &&
- (error.osError!.errorCode == _enoent ||
- error.osError!.errorCode == _enoentWin);
- });
+ var stream = child.list(p.join(dir, basename), fileSystem,
+ followLinks: followLinks);
resultGroup.add(stream);
});
}
@@ -409,10 +402,15 @@
Iterable<FileSystemEntity> listSync(String dir, FileSystem fileSystem,
{bool followLinks = true}) {
if (isRecursive) {
- return fileSystem
- .directory(dir)
- .listSync(recursive: true, followLinks: followLinks)
- .where((entity) => _matches(p.relative(entity.path, from: dir)));
+ try {
+ return fileSystem
+ .directory(dir)
+ .listSync(recursive: true, followLinks: followLinks)
+ .where((entity) => _matches(p.relative(entity.path, from: dir)));
+ } on FileSystemException catch (error) {
+ if (error.isMissing) return const [];
+ rethrow;
+ }
}
// Don't spawn extra [Directory.listSync] calls when we already know exactly
@@ -428,7 +426,13 @@
});
}
- var entities = fileSystem.directory(dir).listSync(followLinks: followLinks);
+ List<FileSystemEntity> entities;
+ try {
+ entities = fileSystem.directory(dir).listSync(followLinks: followLinks);
+ } on FileSystemException catch (error) {
+ if (error.isMissing) return const [];
+ rethrow;
+ }
_validateIntermediateChildrenSync(dir, entities, fileSystem);
return entities.expand((entity) {
@@ -440,23 +444,10 @@
entities.addAll(children!.keys
.where((sequence) => sequence.matches(basename))
.expand((sequence) {
- try {
- return children![sequence]!
- .listSync(p.join(dir, basename), fileSystem,
- followLinks: followLinks)
- .toList();
- } on FileSystemException catch (error) {
- // Ignore errors from directories not existing. We do this here so
- // that we only ignore warnings below wild cards. For example, the
- // glob "foo/bar/*/baz" should fail if "foo/bar" doesn't exist but
- // succeed if "foo/bar/qux/baz" doesn't exist.
- if (error.osError!.errorCode == _enoent ||
- error.osError!.errorCode == _enoentWin) {
- return const [];
- } else {
- rethrow;
- }
- }
+ return children![sequence]!
+ .listSync(p.join(dir, basename), fileSystem,
+ followLinks: followLinks)
+ .toList();
}));
return entities;
@@ -509,3 +500,15 @@
}
return SequenceNode(nodes, caseSensitive: first.caseSensitive);
}
+
+extension on Stream<FileSystemEntity> {
+ Stream<FileSystemEntity> ignoreMissing() => handleError((_) {},
+ test: (error) => error is FileSystemException && error.isMissing);
+}
+
+extension on FileSystemException {
+ bool get isMissing {
+ final errorCode = osError?.errorCode;
+ return errorCode == _enoent || errorCode == _enoentWin;
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 728c0d3..37a37fd 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: glob
-version: 2.0.2
+version: 2.1.0-dev
description: Bash-style filename globbing.
repository: https://github.com/dart-lang/glob
diff --git a/test/list_test.dart b/test/list_test.dart
index 512d901..5714f25 100644
--- a/test/list_test.dart
+++ b/test/list_test.dart
@@ -26,15 +26,17 @@
expect(Glob('*', context: p.url).list, throwsStateError);
});
- test('reports exceptions for non-existent case-sensitive directories', () {
- expect(Glob('non/existent/**', caseSensitive: true).list().toList(),
- throwsA(isA<FileSystemException>()));
+ test('returns empty list for non-existent case-sensitive directories',
+ () async {
+ expect(await Glob('non/existent/**', caseSensitive: true).list().toList(),
+ []);
});
- test('reports exceptions for non-existent case-insensitive directories',
- () {
- expect(Glob('non/existent/**', caseSensitive: false).list().toList(),
- throwsA(isA<FileSystemException>()));
+ test('returns empty list for non-existent case-insensitive directories',
+ () async {
+ expect(
+ await Glob('non/existent/**', caseSensitive: false).list().toList(),
+ []);
});
});
@@ -43,32 +45,27 @@
expect(Glob('*', context: p.url).listSync, throwsStateError);
});
- test('reports exceptions for non-existent case-sensitive directories', () {
- expect(Glob('non/existent/**', caseSensitive: true).listSync,
- throwsA(isA<FileSystemException>()));
+ test('returns empty list for non-existent case-sensitive directories', () {
+ expect(Glob('non/existent/**', caseSensitive: true).listSync(), []);
});
- test('reports exceptions for non-existent case-insensitive directories',
+ test('returns empty list for non-existent case-insensitive directories',
() {
- expect(Glob('non/existent/**', caseSensitive: false).listSync,
- throwsA(isA<FileSystemException>()));
+ expect(Glob('non/existent/**', caseSensitive: false).listSync(), []);
});
});
group('when case-sensitive', () {
test('lists literals case-sensitively', () {
- expect(Glob('foo/BAZ/qux', caseSensitive: true).listSync,
- throwsA(isA<FileSystemException>()));
+ expect(Glob('foo/BAZ/qux', caseSensitive: true).listSync(), []);
});
test('lists ranges case-sensitively', () {
- expect(Glob('foo/[BX][A-Z]z/qux', caseSensitive: true).listSync,
- throwsA(isA<FileSystemException>()));
+ expect(Glob('foo/[BX][A-Z]z/qux', caseSensitive: true).listSync(), []);
});
test('options preserve case-sensitivity', () {
- expect(Glob('foo/{BAZ,ZAP}/qux', caseSensitive: true).listSync,
- throwsA(isA<FileSystemException>()));
+ expect(Glob('foo/{BAZ,ZAP}/qux', caseSensitive: true).listSync(), []);
});
});