Skip to content

Commit 18a00a3

Browse files
tvolkertcbracken
authored andcommitted
v.next interface for package:file/file.dart (flutter#7)
* v.next interface for package:file/file.dart This changes the interface to chiefly expose the FileSystem abstraction and deal in native dart:io types where at all possible: - Expose all dart:io file-system static methods as instance methods on `FileSystem`, to allow injectable implementations (e.g. in-memory, local, mock, etc.) - Create FileSystemEntity, File, Directory, Link as abstract classes that implement their native counterparts and add any extra methods we choose in this library (initially, only `get fileSystem => Filesystem`) By going this route, it implies that each FileSystem implementation will need to provide types that implement both the sync and async APIs (since they implement the interfaces in native dart:io). However, I still plan to provide SynchronousFileSystem & AsynchronousFileSystem, which will simply throw UnsupportedError for the APIs they don't support. This change will allow existing libraries/apps to seamlessly start using FileSystem as a drop-in replacement for existing direct dart:io usage.
1 parent 63adab7 commit 18a00a3

File tree

9 files changed

+127
-139
lines changed

9 files changed

+127
-139
lines changed

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#### 0.2.0
2+
3+
* New interface
4+
5+
#### 0.1.0
6+
7+
* Initial version

lib/file.dart

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
library file;
2-
3-
export 'package:file/src/backends/memory.dart';
4-
export 'package:file/src/memory_utils.dart' show MemoryFileStorage;
5-
export 'package:file/src/interface.dart';
1+
export 'src/interface/directory.dart';
2+
export 'src/interface/file_system_entity.dart';
3+
export 'src/interface/file_system.dart';
4+
export 'src/interface/file.dart';
5+
export 'src/interface/link.dart';

lib/src/interface.dart

Lines changed: 0 additions & 9 deletions
This file was deleted.

lib/src/interface/directory.dart

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
part of file.src.interface.file;
1+
import 'dart:io' as io;
22

3-
/// A reference to a directory on the file system.
4-
abstract class Directory implements FileSystemEntity {
5-
@override
6-
Future<bool> exists() async {
7-
return await fileSystem.type(path) == FileSystemEntityType.DIRECTORY;
8-
}
3+
import 'file_system_entity.dart';
94

10-
Stream<FileSystemEntity> list({bool recursive: false});
5+
/// A reference to a directory on the file system.
6+
abstract class Directory implements FileSystemEntity, io.Directory {
117
}

lib/src/interface/file.dart

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,7 @@
1-
part of file.src.interface.file;
1+
import 'dart:io' as io;
22

3-
/// A reference to a file on the file system.
4-
abstract class File implements FileSystemEntity {
5-
@override
6-
Future<bool> exists() async {
7-
return await fileSystem.type(path) == FileSystemEntityType.FILE;
8-
}
9-
10-
Future<List<int>> readAsBytes();
11-
12-
Future<String> readAsString();
3+
import 'file_system_entity.dart';
134

14-
/// Writes [bytes] to the file.
15-
Future<File> writeAsBytes(List<int> bytes);
16-
17-
/// Writes [contents] to the file.
18-
Future<File> writeAsString(String contents);
5+
/// A reference to a file on the file system.
6+
abstract class File implements FileSystemEntity, io.File {
197
}

lib/src/interface/file_system.dart

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
part of file.src.interface.file;
1+
import 'dart:async';
2+
import 'dart:io' as io;
3+
4+
import 'directory.dart';
5+
import 'file.dart';
26

37
/// A generic representation of a file system.
48
abstract class FileSystem {
@@ -8,15 +12,101 @@ abstract class FileSystem {
812
/// Returns a reference to a [File] at [path].
913
File file(String path);
1014

11-
/// Finds the type of file system object that a [path] points to.
15+
/// Creates a directory object pointing to the current working directory.
16+
Directory get currentDirectory;
17+
18+
/// Sets the current working directory to the specified path. The new value
19+
/// set can be either a [Directory] or a [String].
20+
///
21+
/// Relative paths will be resolved by the underlying file system
22+
/// implementation (meaning it is up to the underlying implementation to
23+
/// decide whether to support relative paths).
24+
set currentDirectory(path);
25+
26+
/// Asynchronously calls the operating system's stat() function on [path].
27+
/// Returns a Future which completes with a [FileStat] object containing
28+
/// the data returned by stat().
29+
/// If the call fails, completes the future with a [FileStat] object with
30+
/// .type set to FileSystemEntityType.NOT_FOUND and the other fields invalid.
31+
Future<io.FileStat> stat(String path);
32+
33+
/// Calls the operating system's stat() function on [path].
34+
/// Returns a [FileStat] object containing the data returned by stat().
35+
/// If the call fails, returns a [FileStat] object with .type set to
36+
/// FileSystemEntityType.NOT_FOUND and the other fields invalid.
37+
io.FileStat statSync(String path);
38+
39+
/// Checks whether two paths refer to the same object in the
40+
/// file system. Returns a [Future<bool>] that completes with the result.
41+
///
42+
/// Comparing a link to its target returns false, as does comparing two links
43+
/// that point to the same target. To check the target of a link, use
44+
/// Link.target explicitly to fetch it. Directory links appearing
45+
/// inside a path are followed, though, to find the file system object.
46+
///
47+
/// Completes the returned Future with an error if one of the paths points
48+
/// to an object that does not exist.
49+
Future<bool> identical(String path1, String path2);
50+
51+
/// Synchronously checks whether two paths refer to the same object in the
52+
/// file system.
53+
///
54+
/// Comparing a link to its target returns false, as does comparing two links
55+
/// that point to the same target. To check the target of a link, use
56+
/// Link.target explicitly to fetch it. Directory links appearing
57+
/// inside a path are followed, though, to find the file system object.
58+
///
59+
/// Throws an error if one of the paths points to an object that does not
60+
/// exist.
61+
bool identicalSync(String path1, String path2);
62+
63+
/// Tests if [watch] is supported on the current system.
64+
bool get isWatchSupported;
65+
66+
/// Finds the type of file system object that a [path] points to. Returns
67+
/// a Future<FileSystemEntityType> that completes with the result.
68+
///
69+
/// [FileSystemEntityType.LINK] will only be returned if [followLinks] is
70+
/// `false`, and [path] points to a link
1271
///
13-
/// Returns a Future<FileSystemEntityType> that completes with the result.
72+
/// If the [path] does not point to a file system object or an error occurs
73+
/// then [FileSystemEntityType.NOT_FOUND] is returned.
74+
Future<io.FileSystemEntityType> type(String path, {bool followLinks: true});
75+
76+
/// Syncronously finds the type of file system object that a [path] points
77+
/// to. Returns a [FileSystemEntityType].
1478
///
1579
/// [FileSystemEntityType.LINK] will only be returned if [followLinks] is
16-
/// `false`, otherwise symbolic links are resolved and the result type is
17-
/// returned instead.
80+
/// `false`, and [path] points to a link
1881
///
1982
/// If the [path] does not point to a file system object or an error occurs
2083
/// then [FileSystemEntityType.NOT_FOUND] is returned.
21-
Future<FileSystemEntityType> type(String path, {bool followLinks: true});
84+
io.FileSystemEntityType typeSync(String path, {bool followLinks: true});
85+
86+
/// Checks if [`type(path)`](type) returns [io.FileSystemEntityType.FILE].
87+
Future<bool> isFile(String path) async =>
88+
await type(path) == io.FileSystemEntityType.FILE;
89+
90+
/// Synchronously checks if [`type(path)`](type) returns
91+
/// [io.FileSystemEntityType.FILE].
92+
bool isFileSync(String path) =>
93+
typeSync(path) == io.FileSystemEntityType.FILE;
94+
95+
/// Checks if [`type(path)`](type) returns [io.FileSystemEntityType.DIRECTORY].
96+
Future<bool> isDirectory(String path) async =>
97+
await type(path) == io.FileSystemEntityType.DIRECTORY;
98+
99+
/// Synchronously checks if [`type(path)`](type) returns
100+
/// [io.FileSystemEntityType.DIRECTORY].
101+
bool isDirectorySync(String path) =>
102+
typeSync(path) == io.FileSystemEntityType.DIRECTORY;
103+
104+
/// Checks if [`type(path)`](type) returns [io.FileSystemEntityType.LINK].
105+
Future<bool> isLink(String path) async
106+
=> await type(path) == io.FileSystemEntityType.LINK;
107+
108+
/// Synchronously checks if [`type(path)`](type) returns
109+
/// [io.FileSystemEntityType.LINK].
110+
bool isLinkSync(String path) =>
111+
typeSync(path) == io.FileSystemEntityType.LINK;
22112
}
Lines changed: 5 additions & 88 deletions
Original file line numberDiff line numberDiff line change
@@ -1,92 +1,9 @@
1-
part of file.src.interface.file;
1+
import 'dart:io' as io;
22

3-
/// The common super class for [File], [Directory], and [Link] objects.
4-
///
5-
/// [FileSystemEntity] objects are returned from file listing operations. To
6-
/// determine if a [FileSystemEntity] is a [File], [Directory], or [Link]
7-
/// perform a type check:
8-
/// if (entity is File) (entity as File).remove();
9-
///
10-
/// Unlike the native `dart:io` package, all operations are asynchronous. This
11-
/// is because some backing implementations communicate over the network.
12-
abstract class FileSystemEntity {
13-
Future<FileSystemEntity> copy(String newPath);
14-
15-
/// Creates the entity this reference represents.
16-
///
17-
/// Returns a [Future<FileSystemEntity>] that completes with a reference to
18-
/// the file system object that was created.
19-
///
20-
/// If [recursive] is `false`, the default, the object is created only if all
21-
/// directories in the [path] actually exist. If [recursive] is `true`, all
22-
/// non-existing path components are created.
23-
///
24-
/// Existing objects are left untouched by create.
25-
///
26-
/// May complete with a [Future<FileSystemEntityException>] if the operation
27-
/// fails.
28-
Future<FileSystemEntity> create({bool recursive: false});
29-
30-
/// Deletes this [FileSystemEntity].
31-
///
32-
/// If the [FileSystemEntity] is a [Directory], and if [recursive] is `false`,
33-
/// the directory must be empty. Otherwise, if [recursive] is `true`, the
34-
/// directory and all sub-directories and files in the directories are
35-
/// deleted.
36-
///
37-
/// If [recursive] is true, the [FileSystemEntity] is deleted even if the type
38-
/// of the [FileSystemEntity] doesn't match the content of the file system.
39-
/// This behavior allows delete to be used to unconditionally delete any file
40-
/// system object.
41-
///
42-
/// Returns a [Future<FileSystemEntity>] that completes with this
43-
/// [FileSystemEntity] when the deletion is done. If the FileSystemEntity
44-
/// cannot be deleted, the future completes with an exception.
45-
Future<FileSystemEntity> delete({bool recursive: false});
46-
47-
/// Checks whether the file system entity with this [path] exists.
48-
///
49-
/// Returns a [Future<bool>] that completes with the result.
50-
///
51-
/// **NOTE**: Since the method is implemented on every super class, it will
52-
/// complete with false if a *different* type of object exists. To check if
53-
/// *any* object exists at a given path, use [FileSystem.type] method.
54-
Future<bool> exists();
3+
import 'file_system.dart';
554

56-
/// The backing implementation of this file system object.
5+
/// The common super class for [File], [Directory], and [Link] objects.
6+
abstract class FileSystemEntity implements io.FileSystemEntity {
7+
/// Returns the file system responsible for this entity.
578
FileSystem get fileSystem;
58-
59-
/// Returns a reference to the parent directory of this file system object.
60-
///
61-
/// If this object is a root directory, returns `null`.
62-
Directory get parent;
63-
64-
/// The absolute location this entity refers to.
65-
String get path;
66-
67-
Future<FileSystemEntity> rename(String newPath);
68-
}
69-
70-
/// Exception thrown when a file operation fails.
71-
class FileSystemEntityException implements Exception {
72-
final String message;
73-
74-
/// The file system path on which the error occurred.
75-
///
76-
/// Can be `null` if the exception does not relate directly to an object.
77-
final String path;
78-
79-
FileSystemEntityException(this.message, this.path);
80-
81-
String toString() => '${FileSystemEntityException}: $message: $path';
82-
}
83-
84-
/// The type of an entity on the file system.
85-
enum FileSystemEntityType { DIRECTORY, FILE, LINK, NOT_FOUND }
86-
87-
/// Returns the parent directory of [path].
88-
String getParentPath(String path) {
89-
return path == '/' || path == ''
90-
? null
91-
: path.substring(0, path.lastIndexOf('/'));
929
}

lib/src/interface/link.dart

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,7 @@
1-
part of file.src.interface.file;
1+
import 'dart:io' as io;
2+
3+
import 'file_system_entity.dart';
24

35
/// A reference to a symbolic link on the file system.
4-
abstract class Link implements FileSystemEntity {
5-
@override
6-
Future<bool> exists() async {
7-
return await fileSystem.type(path) == FileSystemEntityType.LINK;
8-
}
6+
abstract class Link implements FileSystemEntity, io.Link {
97
}

pubspec.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ name: file
22
authors:
33
- Matan Lurey <[email protected]>
44
- Yegor Jbanov <[email protected]>
5+
- Todd Volkert <[email protected]>
56
description: A pluggable, mockable file system abstraction for Dart.
67
homepage: https://github.com/matanlurey/file
7-
version: 0.1.2
8+
version: 0.2.0
89
dependencies:
910
dev_dependencies:
1011
dart_style:

0 commit comments

Comments
 (0)