Skip to content

Commit

Permalink
Don't throw for missing directories (#60)
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
natebosch authored May 4, 2022
1 parent da1f459 commit e10eb24
Show file tree
Hide file tree
Showing 4 changed files with 58 additions and 53 deletions.
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -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.
Expand Down
69 changes: 36 additions & 33 deletions lib/src/list_tree.dart
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ class _ListTreeNode {
return fileSystem
.directory(dir)
.list(recursive: true, followLinks: followLinks)
.ignoreMissing()
.where((entity) => _matches(p.relative(entity.path, from: dir)));
}

Expand All @@ -340,6 +341,7 @@ class _ListTreeNode {
var entities = await fileSystem
.directory(dir)
.list(followLinks: followLinks)
.ignoreMissing()
.toList();
await _validateIntermediateChildrenAsync(dir, entities, fileSystem);

Expand All @@ -353,17 +355,8 @@ class _ListTreeNode {
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);
});
}
Expand Down Expand Up @@ -409,10 +402,15 @@ class _ListTreeNode {
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
Expand All @@ -428,7 +426,13 @@ class _ListTreeNode {
});
}

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) {
Expand All @@ -440,23 +444,10 @@ class _ListTreeNode {
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;
Expand Down Expand Up @@ -509,3 +500,15 @@ SequenceNode _join(Iterable<AstNode> components) {
}
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;
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -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
Expand Down
35 changes: 16 additions & 19 deletions test/list_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,15 +26,17 @@ void main() {
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(),
[]);
});
});

Expand All @@ -43,32 +45,27 @@ void main() {
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(), []);
});
});

Expand Down

0 comments on commit e10eb24

Please sign in to comment.