v.next interface for package:file/file.dart (#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.
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..dc33797
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,7 @@
+#### 0.2.0
+
+* New interface
+
+#### 0.1.0
+
+* Initial version
diff --git a/lib/file.dart b/lib/file.dart
index 8be8c77..d1a73c0 100644
--- a/lib/file.dart
+++ b/lib/file.dart
@@ -1,5 +1,5 @@
-library file;
-
-export 'package:file/src/backends/memory.dart';
-export 'package:file/src/memory_utils.dart' show MemoryFileStorage;
-export 'package:file/src/interface.dart';
+export 'src/interface/directory.dart';
+export 'src/interface/file_system_entity.dart';
+export 'src/interface/file_system.dart';
+export 'src/interface/file.dart';
+export 'src/interface/link.dart';
diff --git a/lib/src/interface.dart b/lib/src/interface.dart
deleted file mode 100644
index 1a6c987..0000000
--- a/lib/src/interface.dart
+++ /dev/null
@@ -1,9 +0,0 @@
-library file.src.interface.file;
-
-import 'dart:async';
-
-part 'interface/directory.dart';
-part 'interface/file.dart';
-part 'interface/file_system.dart';
-part 'interface/file_system_entity.dart';
-part 'interface/link.dart';
diff --git a/lib/src/interface/directory.dart b/lib/src/interface/directory.dart
index 09a3b65..c039f2c 100644
--- a/lib/src/interface/directory.dart
+++ b/lib/src/interface/directory.dart
@@ -1,11 +1,7 @@
-part of file.src.interface.file;
+import 'dart:io' as io;
+
+import 'file_system_entity.dart';
/// A reference to a directory on the file system.
-abstract class Directory implements FileSystemEntity {
- @override
- Future<bool> exists() async {
- return await fileSystem.type(path) == FileSystemEntityType.DIRECTORY;
- }
-
- Stream<FileSystemEntity> list({bool recursive: false});
+abstract class Directory implements FileSystemEntity, io.Directory {
}
diff --git a/lib/src/interface/file.dart b/lib/src/interface/file.dart
index ed11a09..c755848 100644
--- a/lib/src/interface/file.dart
+++ b/lib/src/interface/file.dart
@@ -1,19 +1,7 @@
-part of file.src.interface.file;
+import 'dart:io' as io;
+
+import 'file_system_entity.dart';
/// A reference to a file on the file system.
-abstract class File implements FileSystemEntity {
- @override
- Future<bool> exists() async {
- return await fileSystem.type(path) == FileSystemEntityType.FILE;
- }
-
- Future<List<int>> readAsBytes();
-
- Future<String> readAsString();
-
- /// Writes [bytes] to the file.
- Future<File> writeAsBytes(List<int> bytes);
-
- /// Writes [contents] to the file.
- Future<File> writeAsString(String contents);
+abstract class File implements FileSystemEntity, io.File {
}
diff --git a/lib/src/interface/file_system.dart b/lib/src/interface/file_system.dart
index fca70b6..ed6216b 100644
--- a/lib/src/interface/file_system.dart
+++ b/lib/src/interface/file_system.dart
@@ -1,4 +1,8 @@
-part of file.src.interface.file;
+import 'dart:async';
+import 'dart:io' as io;
+
+import 'directory.dart';
+import 'file.dart';
/// A generic representation of a file system.
abstract class FileSystem {
@@ -8,15 +12,101 @@
/// Returns a reference to a [File] at [path].
File file(String path);
- /// Finds the type of file system object that a [path] points to.
+ /// Creates a directory object pointing to the current working directory.
+ Directory get currentDirectory;
+
+ /// Sets the current working directory to the specified path. The new value
+ /// set can be either a [Directory] or a [String].
///
- /// Returns a Future<FileSystemEntityType> that completes with the result.
+ /// Relative paths will be resolved by the underlying file system
+ /// implementation (meaning it is up to the underlying implementation to
+ /// decide whether to support relative paths).
+ set currentDirectory(path);
+
+ /// Asynchronously calls the operating system's stat() function on [path].
+ /// Returns a Future which completes with a [FileStat] object containing
+ /// the data returned by stat().
+ /// If the call fails, completes the future with a [FileStat] object with
+ /// .type set to FileSystemEntityType.NOT_FOUND and the other fields invalid.
+ Future<io.FileStat> stat(String path);
+
+ /// Calls the operating system's stat() function on [path].
+ /// Returns a [FileStat] object containing the data returned by stat().
+ /// If the call fails, returns a [FileStat] object with .type set to
+ /// FileSystemEntityType.NOT_FOUND and the other fields invalid.
+ io.FileStat statSync(String path);
+
+ /// Checks whether two paths refer to the same object in the
+ /// file system. Returns a [Future<bool>] that completes with the result.
+ ///
+ /// Comparing a link to its target returns false, as does comparing two links
+ /// that point to the same target. To check the target of a link, use
+ /// Link.target explicitly to fetch it. Directory links appearing
+ /// inside a path are followed, though, to find the file system object.
+ ///
+ /// Completes the returned Future with an error if one of the paths points
+ /// to an object that does not exist.
+ Future<bool> identical(String path1, String path2);
+
+ /// Synchronously checks whether two paths refer to the same object in the
+ /// file system.
+ ///
+ /// Comparing a link to its target returns false, as does comparing two links
+ /// that point to the same target. To check the target of a link, use
+ /// Link.target explicitly to fetch it. Directory links appearing
+ /// inside a path are followed, though, to find the file system object.
+ ///
+ /// Throws an error if one of the paths points to an object that does not
+ /// exist.
+ bool identicalSync(String path1, String path2);
+
+ /// Tests if [watch] is supported on the current system.
+ bool get isWatchSupported;
+
+ /// Finds the type of file system object that a [path] points to. Returns
+ /// a Future<FileSystemEntityType> that completes with the result.
///
/// [FileSystemEntityType.LINK] will only be returned if [followLinks] is
- /// `false`, otherwise symbolic links are resolved and the result type is
- /// returned instead.
+ /// `false`, and [path] points to a link
///
/// If the [path] does not point to a file system object or an error occurs
/// then [FileSystemEntityType.NOT_FOUND] is returned.
- Future<FileSystemEntityType> type(String path, {bool followLinks: true});
+ Future<io.FileSystemEntityType> type(String path, {bool followLinks: true});
+
+ /// Syncronously finds the type of file system object that a [path] points
+ /// to. Returns a [FileSystemEntityType].
+ ///
+ /// [FileSystemEntityType.LINK] will only be returned if [followLinks] is
+ /// `false`, and [path] points to a link
+ ///
+ /// If the [path] does not point to a file system object or an error occurs
+ /// then [FileSystemEntityType.NOT_FOUND] is returned.
+ io.FileSystemEntityType typeSync(String path, {bool followLinks: true});
+
+ /// Checks if [`type(path)`](type) returns [io.FileSystemEntityType.FILE].
+ Future<bool> isFile(String path) async =>
+ await type(path) == io.FileSystemEntityType.FILE;
+
+ /// Synchronously checks if [`type(path)`](type) returns
+ /// [io.FileSystemEntityType.FILE].
+ bool isFileSync(String path) =>
+ typeSync(path) == io.FileSystemEntityType.FILE;
+
+ /// Checks if [`type(path)`](type) returns [io.FileSystemEntityType.DIRECTORY].
+ Future<bool> isDirectory(String path) async =>
+ await type(path) == io.FileSystemEntityType.DIRECTORY;
+
+ /// Synchronously checks if [`type(path)`](type) returns
+ /// [io.FileSystemEntityType.DIRECTORY].
+ bool isDirectorySync(String path) =>
+ typeSync(path) == io.FileSystemEntityType.DIRECTORY;
+
+ /// Checks if [`type(path)`](type) returns [io.FileSystemEntityType.LINK].
+ Future<bool> isLink(String path) async
+ => await type(path) == io.FileSystemEntityType.LINK;
+
+ /// Synchronously checks if [`type(path)`](type) returns
+ /// [io.FileSystemEntityType.LINK].
+ bool isLinkSync(String path) =>
+ typeSync(path) == io.FileSystemEntityType.LINK;
}
diff --git a/lib/src/interface/file_system_entity.dart b/lib/src/interface/file_system_entity.dart
index 3b3415f..5b6a84e 100644
--- a/lib/src/interface/file_system_entity.dart
+++ b/lib/src/interface/file_system_entity.dart
@@ -1,92 +1,9 @@
-part of file.src.interface.file;
+import 'dart:io' as io;
+
+import 'file_system.dart';
/// The common super class for [File], [Directory], and [Link] objects.
-///
-/// [FileSystemEntity] objects are returned from file listing operations. To
-/// determine if a [FileSystemEntity] is a [File], [Directory], or [Link]
-/// perform a type check:
-/// if (entity is File) (entity as File).remove();
-///
-/// Unlike the native `dart:io` package, all operations are asynchronous. This
-/// is because some backing implementations communicate over the network.
-abstract class FileSystemEntity {
- Future<FileSystemEntity> copy(String newPath);
-
- /// Creates the entity this reference represents.
- ///
- /// Returns a [Future<FileSystemEntity>] that completes with a reference to
- /// the file system object that was created.
- ///
- /// If [recursive] is `false`, the default, the object is created only if all
- /// directories in the [path] actually exist. If [recursive] is `true`, all
- /// non-existing path components are created.
- ///
- /// Existing objects are left untouched by create.
- ///
- /// May complete with a [Future<FileSystemEntityException>] if the operation
- /// fails.
- Future<FileSystemEntity> create({bool recursive: false});
-
- /// Deletes this [FileSystemEntity].
- ///
- /// If the [FileSystemEntity] is a [Directory], and if [recursive] is `false`,
- /// the directory must be empty. Otherwise, if [recursive] is `true`, the
- /// directory and all sub-directories and files in the directories are
- /// deleted.
- ///
- /// If [recursive] is true, the [FileSystemEntity] is deleted even if the type
- /// of the [FileSystemEntity] doesn't match the content of the file system.
- /// This behavior allows delete to be used to unconditionally delete any file
- /// system object.
- ///
- /// Returns a [Future<FileSystemEntity>] that completes with this
- /// [FileSystemEntity] when the deletion is done. If the FileSystemEntity
- /// cannot be deleted, the future completes with an exception.
- Future<FileSystemEntity> delete({bool recursive: false});
-
- /// Checks whether the file system entity with this [path] exists.
- ///
- /// Returns a [Future<bool>] that completes with the result.
- ///
- /// **NOTE**: Since the method is implemented on every super class, it will
- /// complete with false if a *different* type of object exists. To check if
- /// *any* object exists at a given path, use [FileSystem.type] method.
- Future<bool> exists();
-
- /// The backing implementation of this file system object.
+abstract class FileSystemEntity implements io.FileSystemEntity {
+ /// Returns the file system responsible for this entity.
FileSystem get fileSystem;
-
- /// Returns a reference to the parent directory of this file system object.
- ///
- /// If this object is a root directory, returns `null`.
- Directory get parent;
-
- /// The absolute location this entity refers to.
- String get path;
-
- Future<FileSystemEntity> rename(String newPath);
-}
-
-/// Exception thrown when a file operation fails.
-class FileSystemEntityException implements Exception {
- final String message;
-
- /// The file system path on which the error occurred.
- ///
- /// Can be `null` if the exception does not relate directly to an object.
- final String path;
-
- FileSystemEntityException(this.message, this.path);
-
- String toString() => '${FileSystemEntityException}: $message: $path';
-}
-
-/// The type of an entity on the file system.
-enum FileSystemEntityType { DIRECTORY, FILE, LINK, NOT_FOUND }
-
-/// Returns the parent directory of [path].
-String getParentPath(String path) {
- return path == '/' || path == ''
- ? null
- : path.substring(0, path.lastIndexOf('/'));
}
diff --git a/lib/src/interface/link.dart b/lib/src/interface/link.dart
index 2e064d2..88dcf3e 100644
--- a/lib/src/interface/link.dart
+++ b/lib/src/interface/link.dart
@@ -1,9 +1,7 @@
-part of file.src.interface.file;
+import 'dart:io' as io;
+
+import 'file_system_entity.dart';
/// A reference to a symbolic link on the file system.
-abstract class Link implements FileSystemEntity {
- @override
- Future<bool> exists() async {
- return await fileSystem.type(path) == FileSystemEntityType.LINK;
- }
+abstract class Link implements FileSystemEntity, io.Link {
}
diff --git a/pubspec.yaml b/pubspec.yaml
index 78cbbf2..91f302f 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -2,9 +2,10 @@
authors:
- Matan Lurey <matanl@google.com>
- Yegor Jbanov <yjbanov@google.com>
+- Todd Volkert <tvolkert@google.com>
description: A pluggable, mockable file system abstraction for Dart.
homepage: https://github.com/matanlurey/file
-version: 0.1.2
+version: 0.2.0
dependencies:
dev_dependencies:
dart_style: