Skip to content

[io_file] Add the ability to get file metadata on Windows. #202

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 53 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
5db08c8
Add `rename` support for Windows
brianquinlan Mar 13, 2025
a6f5c1c
Update rename_test.dart
brianquinlan Mar 13, 2025
f89d063
Some fixes
brianquinlan Mar 14, 2025
0f81c95
Update vm_windows_file_system.dart
brianquinlan Mar 14, 2025
ed98ec4
Update vm_windows_file_system.dart
brianquinlan Mar 14, 2025
2dfc83b
Test utils
brianquinlan Mar 14, 2025
8bf09fb
Metadata
brianquinlan Mar 16, 2025
1dcd074
Set metadata
brianquinlan Mar 17, 2025
2877b5e
Update vm_windows_file_system.dart
brianquinlan Mar 17, 2025
7e4717d
Tests
brianquinlan Mar 17, 2025
278de74
Merge branch 'main' into metadata
brianquinlan Mar 17, 2025
69490d5
Fixes
brianquinlan Mar 17, 2025
9704766
Windows metadata tests
brianquinlan Mar 17, 2025
36647c2
More windows properties
brianquinlan Mar 17, 2025
a363b49
Update vm_windows_file_system.dart
brianquinlan Mar 17, 2025
305655c
Test fixes
brianquinlan Mar 17, 2025
88dc69e
Update metadata_windows_test.dart
brianquinlan Mar 17, 2025
647a6a5
size
brianquinlan Mar 17, 2025
f7c6d76
creation time
brianquinlan Mar 17, 2025
dd67782
not such path
brianquinlan Mar 17, 2025
9185690
GetLastError
brianquinlan Mar 17, 2025
a687b84
Update metadata_test.dart
brianquinlan Mar 17, 2025
e3fc41a
Times
brianquinlan Mar 17, 2025
309c186
More fixes
brianquinlan Mar 17, 2025
2c51b7d
More tests
brianquinlan Mar 17, 2025
fa3e0a2
Update metadata_windows_test.dart
brianquinlan Mar 17, 2025
b287897
Docs
brianquinlan Mar 17, 2025
b88bfca
Update vm_windows_file_system.dart
brianquinlan Mar 18, 2025
d14f1a9
Update vm_windows_file_system.dart
brianquinlan Mar 18, 2025
4d8ccfd
Code review updates
brianquinlan Mar 18, 2025
1aeb152
Review feedback.
brianquinlan Mar 19, 2025
4fd4da1
Merge branch 'main' into metadata
brianquinlan Mar 27, 2025
b6ffeff
Add library
brianquinlan Mar 27, 2025
8c95248
Update file_system.dart
brianquinlan Mar 27, 2025
18e6f8b
Review comments
brianquinlan Mar 27, 2025
186c228
Update file_system.dart
brianquinlan Mar 27, 2025
7d11d36
Update file_system_test.dart
brianquinlan Mar 27, 2025
90d4070
Update vm_windows_file_system.dart
brianquinlan Mar 27, 2025
c878448
Merge remote-tracking branch 'upstream/main' into pr/brianquinlan/202
brianquinlan Apr 3, 2025
0188da6
Allow if original metadata
brianquinlan Apr 4, 2025
ea0946f
Update vm_windows_file_system.dart
brianquinlan Apr 16, 2025
e28568d
Fixes
brianquinlan Apr 17, 2025
49cbe04
Merge remote-tracking branch 'upstream/main' into metadata
brianquinlan May 8, 2025
d614fad
Update vm_posix_file_system.dart
brianquinlan May 8, 2025
a2cef78
Maybe this works?
brianquinlan May 8, 2025
3ecea3f
Update pubspec.yaml
brianquinlan May 8, 2025
5ff9dea
Update vm_windows_file_system.dart
brianquinlan May 8, 2025
8f85c57
Fix
brianquinlan May 8, 2025
86e013c
Harmonize
brianquinlan May 8, 2025
b3cea51
Update vm_windows_file_system.dart
brianquinlan May 8, 2025
e59c2de
Merge remote-tracking branch 'upstream/main' into metadata
brianquinlan May 23, 2025
99cd5af
Update pubspec.yaml
brianquinlan May 23, 2025
f5bbc52
Update vm_posix_file_system.dart
brianquinlan May 23, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 19 additions & 3 deletions pkgs/io_file/example/io_file_example.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,25 @@
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.

import 'package:io_file/posix_file_system.dart';
import 'package:io_file/io_file.dart';
import 'package:io_file/windows_file_system.dart';

import 'package:path/path.dart' as p;

bool isHidden(String path) {
if (fileSystem case final WindowsFileSystem fs) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(For future extension: The OSX file system has a hidden flag too, you can set it with chflags hidden /path/to/folder/. Not all tools recognize it - Finder does, ls doesn't. It's from BSD, so other Unixes might have it too. We should probably support it if possible/reasonable.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, linux has a hidden flag too - it's just not used in many file systems. I plan on moving some of these attributes around when I do the POSIX stat implementation.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Given that the semantics probably are quite different it makes a lot of sense to not share the hidden flag between platforms though they are named the same...

return fs.metadata(path).isHidden;
} else {
// On POSIX, convention is that files and directories starting with a period
// are hidden (except for the special files representing the current working
// directory and parent directory).
//
// In addition, macOS has the UF_HIDDEN flag.
final name = p.basename(path);
return name.startsWith('.') && name != '.' && name != '..';
}
}

void main() {
// TODO(brianquinlan): Create a better example.
PosixFileSystem().rename('foo.txt', 'bar.txt');
isHidden('somefile');
}
77 changes: 41 additions & 36 deletions pkgs/io_file/lib/src/file_system.dart
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,22 @@
import 'dart:convert';
import 'dart:typed_data';

import 'package:meta/meta.dart' show sealed;

// TODO(brianquinlan): When we switch to using exception types outside of
// `dart:io` then change the doc strings to use reference syntax rather than
// code syntax e.g. `PathExistsException` => [PathExistsException].

/// Information about a directory, link, etc. stored in the [FileSystem].
abstract interface class Metadata {
// TODO(brianquinlan): Document all public fields.

bool get isFile;
bool get isDirectory;
bool get isLink;
int get size;
}

/// The modes in which a File can be written.
class WriteMode {
/// Open the file for writing such that data can only be appended to the end
Expand All @@ -34,26 +46,18 @@ class WriteMode {
}

/// An abstract representation of a file system.
base class FileSystem {
/// Checks whether two paths refer to the same object in the file system.
///
/// Throws `PathNotFoundException` if either path doesn't exist.
///
/// Links are resolved before determining if the paths refer to the same
/// object. Throws `PathNotFoundException` if either path requires resolving
/// a broken link.
bool same(String path1, String path2) {
throw UnsupportedError('same');
}

///
/// TODO(brianquinlan): Far now, this class is not meant to be implemented,
/// extended outside of this package. Clarify somewhere that people implementing
/// this class should reach out to be.
@sealed
abstract class FileSystem {
/// Create a directory at the given path.
///
/// If the directory already exists, then `PathExistsException` is thrown.
///
/// If the parent path does not exist, then `PathNotFoundException` is thrown.
void createDirectory(String path) {
throw UnsupportedError('createDirectory');
}
void createDirectory(String path);

/// Creates a temporary directory and returns its path.
///
Expand Down Expand Up @@ -82,9 +86,13 @@ base class FileSystem {
/// fileSystem.createTemporaryDirectory(prefix: 'myproject');
/// }
/// ```
String createTemporaryDirectory({String? parent, String? prefix}) {
throw UnsupportedError('createTemporaryDirectory');
}
String createTemporaryDirectory({String? parent, String? prefix});

/// Metadata for the file system object at [path].
///
/// If `path` represents a symbolic link then metadata for the link is
/// returned.
Metadata metadata(String path);

/// Deletes the directory at the given path.
///
Expand All @@ -97,17 +105,13 @@ base class FileSystem {
/// - On Windows, if `path` is a symbolic link to a directory then the
/// symbolic link is deleted. Otherwise, a `FileSystemException` is thrown.
/// - On POSIX, a `FileSystemException` is thrown.
void removeDirectory(String path) {
throw UnsupportedError('removeDirectory');
}
void removeDirectory(String path);

/// Deletes the directory at the given path and its contents.
///
/// If the directory (or its subdirectories) contains any symbolic links then
/// those links are deleted but their targets are not.
void removeDirectoryTree(String path) {
throw UnsupportedError('removeDirectoryTree');
}
void removeDirectoryTree(String path);

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

/// Reads the entire file contents as a list of bytes.
Uint8List readAsBytes(String path) {
throw UnsupportedError('readAsBytes');
}
Uint8List readAsBytes(String path);

/// Checks whether two paths refer to the same object in the file system.
///
/// Throws `PathNotFoundException` if either path doesn't exist.
///
/// Links are resolved before determining if the paths refer to the same
/// object. Throws `PathNotFoundException` if either path requires resolving
/// a broken link.
bool same(String path1, String path2);

/// The directory path used to store temporary files.
///
Expand All @@ -156,9 +165,7 @@ base class FileSystem {
String path,
Uint8List data, [
WriteMode mode = WriteMode.failExisting,
]) {
throw UnsupportedError('writeAsBytes');
}
]);

/// Write the string to a file.
///
Expand All @@ -177,7 +184,5 @@ base class FileSystem {
WriteMode mode = WriteMode.failExisting,
Encoding encoding = utf8,
String? lineTerminator,
]) {
throw UnsupportedError('writeAsString');
}
]);
}
12 changes: 11 additions & 1 deletion pkgs/io_file/lib/src/vm_posix_file_system.dart
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ external int write(int fd, Pointer<Uint8> buf, int count);

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

@override
Metadata metadata(String path) {
throw UnimplementedError();
}

@override
void removeDirectory(String path) => ffi.using((arena) {
if (libc.unlinkat(
Expand All @@ -124,6 +129,11 @@ base class PosixFileSystem extends FileSystem {
}
});

@override
void removeDirectoryTree(String path) {
throw UnimplementedError();
}

@override
void rename(String oldPath, String newPath) => ffi.using((arena) {
// See https://pubs.opengroup.org/onlinepubs/000095399/functions/rename.html
Expand Down
Loading
Loading