Skip to content

Commit e48fb93

Browse files
authored
[io_file] Add the ability to get file metadata on Windows. (#202)
1 parent 5f1db7a commit e48fb93

10 files changed

+1089
-87
lines changed

pkgs/io_file/example/io_file_example.dart

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,25 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
import 'package:io_file/posix_file_system.dart';
5+
import 'package:io_file/io_file.dart';
6+
import 'package:io_file/windows_file_system.dart';
7+
8+
import 'package:path/path.dart' as p;
9+
10+
bool isHidden(String path) {
11+
if (fileSystem case final WindowsFileSystem fs) {
12+
return fs.metadata(path).isHidden;
13+
} else {
14+
// On POSIX, convention is that files and directories starting with a period
15+
// are hidden (except for the special files representing the current working
16+
// directory and parent directory).
17+
//
18+
// In addition, macOS has the UF_HIDDEN flag.
19+
final name = p.basename(path);
20+
return name.startsWith('.') && name != '.' && name != '..';
21+
}
22+
}
623

724
void main() {
8-
// TODO(brianquinlan): Create a better example.
9-
PosixFileSystem().rename('foo.txt', 'bar.txt');
25+
isHidden('somefile');
1026
}

pkgs/io_file/lib/src/file_system.dart

Lines changed: 41 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,22 @@
55
import 'dart:convert';
66
import 'dart:typed_data';
77

8+
import 'package:meta/meta.dart' show sealed;
9+
810
// TODO(brianquinlan): When we switch to using exception types outside of
911
// `dart:io` then change the doc strings to use reference syntax rather than
1012
// code syntax e.g. `PathExistsException` => [PathExistsException].
1113

14+
/// Information about a directory, link, etc. stored in the [FileSystem].
15+
abstract interface class Metadata {
16+
// TODO(brianquinlan): Document all public fields.
17+
18+
bool get isFile;
19+
bool get isDirectory;
20+
bool get isLink;
21+
int get size;
22+
}
23+
1224
/// The modes in which a File can be written.
1325
class WriteMode {
1426
/// Open the file for writing such that data can only be appended to the end
@@ -34,26 +46,18 @@ class WriteMode {
3446
}
3547

3648
/// An abstract representation of a file system.
37-
base class FileSystem {
38-
/// Checks whether two paths refer to the same object in the file system.
39-
///
40-
/// Throws `PathNotFoundException` if either path doesn't exist.
41-
///
42-
/// Links are resolved before determining if the paths refer to the same
43-
/// object. Throws `PathNotFoundException` if either path requires resolving
44-
/// a broken link.
45-
bool same(String path1, String path2) {
46-
throw UnsupportedError('same');
47-
}
48-
49+
///
50+
/// TODO(brianquinlan): Far now, this class is not meant to be implemented,
51+
/// extended outside of this package. Clarify somewhere that people implementing
52+
/// this class should reach out to be.
53+
@sealed
54+
abstract class FileSystem {
4955
/// Create a directory at the given path.
5056
///
5157
/// If the directory already exists, then `PathExistsException` is thrown.
5258
///
5359
/// If the parent path does not exist, then `PathNotFoundException` is thrown.
54-
void createDirectory(String path) {
55-
throw UnsupportedError('createDirectory');
56-
}
60+
void createDirectory(String path);
5761

5862
/// Creates a temporary directory and returns its path.
5963
///
@@ -82,9 +86,13 @@ base class FileSystem {
8286
/// fileSystem.createTemporaryDirectory(prefix: 'myproject');
8387
/// }
8488
/// ```
85-
String createTemporaryDirectory({String? parent, String? prefix}) {
86-
throw UnsupportedError('createTemporaryDirectory');
87-
}
89+
String createTemporaryDirectory({String? parent, String? prefix});
90+
91+
/// Metadata for the file system object at [path].
92+
///
93+
/// If `path` represents a symbolic link then metadata for the link is
94+
/// returned.
95+
Metadata metadata(String path);
8896

8997
/// Deletes the directory at the given path.
9098
///
@@ -97,17 +105,13 @@ base class FileSystem {
97105
/// - On Windows, if `path` is a symbolic link to a directory then the
98106
/// symbolic link is deleted. Otherwise, a `FileSystemException` is thrown.
99107
/// - On POSIX, a `FileSystemException` is thrown.
100-
void removeDirectory(String path) {
101-
throw UnsupportedError('removeDirectory');
102-
}
108+
void removeDirectory(String path);
103109

104110
/// Deletes the directory at the given path and its contents.
105111
///
106112
/// If the directory (or its subdirectories) contains any symbolic links then
107113
/// those links are deleted but their targets are not.
108-
void removeDirectoryTree(String path) {
109-
throw UnsupportedError('removeDirectoryTree');
110-
}
114+
void removeDirectoryTree(String path);
111115

112116
/// Renames, and possibly moves a file system object from one path to another.
113117
///
@@ -125,14 +129,19 @@ base class FileSystem {
125129
// If `newPath` identifies an existing file or link, that entity is removed
126130
// first. If `newPath` identifies an existing directory, the operation
127131
// fails and raises [PathExistsException].
128-
void rename(String oldPath, String newPath) {
129-
throw UnsupportedError('rename');
130-
}
132+
void rename(String oldPath, String newPath);
131133

132134
/// Reads the entire file contents as a list of bytes.
133-
Uint8List readAsBytes(String path) {
134-
throw UnsupportedError('readAsBytes');
135-
}
135+
Uint8List readAsBytes(String path);
136+
137+
/// Checks whether two paths refer to the same object in the file system.
138+
///
139+
/// Throws `PathNotFoundException` if either path doesn't exist.
140+
///
141+
/// Links are resolved before determining if the paths refer to the same
142+
/// object. Throws `PathNotFoundException` if either path requires resolving
143+
/// a broken link.
144+
bool same(String path1, String path2);
136145

137146
/// The directory path used to store temporary files.
138147
///
@@ -156,9 +165,7 @@ base class FileSystem {
156165
String path,
157166
Uint8List data, [
158167
WriteMode mode = WriteMode.failExisting,
159-
]) {
160-
throw UnsupportedError('writeAsBytes');
161-
}
168+
]);
162169

163170
/// Write the string to a file.
164171
///
@@ -177,7 +184,5 @@ base class FileSystem {
177184
WriteMode mode = WriteMode.failExisting,
178185
Encoding encoding = utf8,
179186
String? lineTerminator,
180-
]) {
181-
throw UnsupportedError('writeAsString');
182-
}
187+
]);
183188
}

pkgs/io_file/lib/src/vm_posix_file_system.dart

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ external int write(int fd, Pointer<Uint8> buf, int count);
6464

6565
/// A [FileSystem] implementation for POSIX systems (e.g. Android, iOS, Linux,
6666
/// macOS).
67-
base class PosixFileSystem extends FileSystem {
67+
final class PosixFileSystem extends FileSystem {
6868
@override
6969
bool same(String path1, String path2) => ffi.using((arena) {
7070
final stat1 = arena<libc.Stat>();
@@ -111,6 +111,11 @@ base class PosixFileSystem extends FileSystem {
111111
return path.cast<ffi.Utf8>().toDartString();
112112
});
113113

114+
@override
115+
Metadata metadata(String path) {
116+
throw UnimplementedError();
117+
}
118+
114119
@override
115120
void removeDirectory(String path) => ffi.using((arena) {
116121
if (libc.unlinkat(
@@ -124,6 +129,11 @@ base class PosixFileSystem extends FileSystem {
124129
}
125130
});
126131

132+
@override
133+
void removeDirectoryTree(String path) {
134+
throw UnimplementedError();
135+
}
136+
127137
@override
128138
void rename(String oldPath, String newPath) => ffi.using((arena) {
129139
// See https://pubs.opengroup.org/onlinepubs/000095399/functions/rename.html

0 commit comments

Comments
 (0)