Skip to content

Refactor fake #245

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 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
1 change: 1 addition & 0 deletions pkgs/io_file/lib/io_file.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// 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.

export 'src/exceptions.dart';
export 'src/file_system.dart';
export 'src/vm_file_system_property.dart'
if (dart.library.html) 'src/web_file_system_property.dart';
66 changes: 66 additions & 0 deletions pkgs/io_file/lib/src/exceptions.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// 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.

class SystemCallError {
final String systemCall;
final int errorCode;
final String message;

const SystemCallError(this.systemCall, this.errorCode, this.message);
}

class IOFileException 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 a file system path.
final String? path;

/// The underlying OS error.
///
/// Can be `null` if the exception is not raised due to an OS error.
final SystemCallError? systemCall;

const IOFileException(this.message, {this.path, this.systemCall});

String _toStringHelper(String className) {
final sb = StringBuffer('$className: $message');
if (path != null) {
sb.write(', path = "$path"');
}
if (systemCall != null) {
sb.write(
' (${systemCall!.systemCall}: ${systemCall!.message}, '
'errno=${systemCall!.errorCode} )',
);
}
return sb.toString();
}

@override
String toString() => _toStringHelper('IOFileException');
}

class PathAccessException extends IOFileException {
const PathAccessException(super.message, {super.path, super.systemCall});

@override
String toString() => _toStringHelper('PathAccessException');
}

class PathExistsException extends IOFileException {
const PathExistsException(super.message, {super.path, super.systemCall});

@override
String toString() => _toStringHelper('PathExistsException');
}

class PathNotFoundException extends IOFileException {
const PathNotFoundException(super.message, {super.path, super.systemCall});

@override
String toString() => _toStringHelper('PathNotFoundException');
}
204 changes: 204 additions & 0 deletions pkgs/io_file/lib/src/fake_posix_file_system.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
import 'dart:convert';

import 'dart:typed_data';

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

import 'exceptions.dart';
import 'file_system.dart';
import 'posix_file_system.dart';

sealed class _Entity {}

class _File extends _Entity {
_File();
}

class _Directory extends _Entity {
final children = <String, _Entity>{};

@override
String toString() {
return '<Directory children=$children>';
}
}

class _FakeMetadata implements Metadata {
@override
DateTime get access => throw UnimplementedError();

@override
DateTime? get creation => throw UnimplementedError();

@override
bool get isDirectory => this.type == FileSystemType.directory;

@override
bool get isFile => throw UnimplementedError();

@override
bool? get isHidden => throw UnimplementedError();

@override
bool get isLink => throw UnimplementedError();

@override
DateTime get modification => throw UnimplementedError();

@override
int get size => throw UnimplementedError();

@override
final FileSystemType type;

_FakeMetadata(this.type);
}

final class FakePosixFileSystem extends PosixFileSystem {
final context = p.Context(style: p.Style.posix);
final root = _Directory();
final tmp = _Directory();

FakePosixFileSystem() {
print('Reset!');
root.children['tmp'] = tmp;
}

_Entity? _findComponents(List<String> components) {
_Entity e = root;
for (var child in components.skip(1)) {
print('$child => $e');
if (e is _Directory) {
if (!e.children.containsKey(child)) return null;
e = e.children[child]!;
} else {
return null;
}
}
return e;
}

_Entity? _findEntity(String path) {
path = context.absolute(path);
return _findComponents(p.split(path));
}

_Entity? _upToLast(String path) {
path = context.absolute(path);
return _findComponents(p.split(p.dirname(path)));
}

@override
void createDirectory(String path) {
print('createDirectory($path)');
if (_findEntity(path) != null) {
throw PathExistsException(
'create directory failed',
path: path,
systemCall: const SystemCallError('XXX', 17, 'XXX'),
);
}
path = context.absolute(path);
final base = context.basename(path);

final parent = _upToLast(path);
print('parent $parent');
if (parent is _Directory) {
parent.children[base] = _Directory();
} else if (parent == null) {
throw PathNotFoundException(
'create directory failed',
path: path,
systemCall: const SystemCallError('XXX', 2, 'XXX'),
);
} else {
throw UnsupportedError(path);
}
}

@override
String createTemporaryDirectory({String? parent, String? prefix}) {
final parentDir = parent ?? '/tmp';

String path = p.join(parentDir, prefix);
createDirectory(path);
return path;
}

@override
Metadata metadata(String path) {
final e = _findEntity(path);
return _FakeMetadata(switch (e) {
_Directory() => FileSystemType.directory,
_File() => FileSystemType.file,
_ => FileSystemType.unknown,
});
}

@override
Uint8List readAsBytes(String path) {
// TODO: implement readAsBytes
throw UnimplementedError();
}

@override
void removeDirectory(String path) {
// TODO: implement removeDirectory
}

@override
void removeDirectoryTree(String path) {
final parent = _upToLast(path);
final base = context.basename(path);

if (parent is _Directory) {
if (parent.children.containsKey(base)) {
// Check that it is a directory.
parent.children.remove(base);
} else {
throw UnsupportedError('XXX');
}
} else {
throw UnsupportedError('XXX');
}
}

@override
void rename(String oldPath, String newPath) {
// TODO: implement rename
}

@override
bool same(String path1, String path2) {
// TODO: implement same
throw UnimplementedError();
}

@override
void writeAsBytes(
String path,
Uint8List data, [
WriteMode mode = WriteMode.failExisting,
]) {
// TODO: implement writeAsBytes
}

@override
void writeAsString(
String path,
String contents, [
WriteMode mode = WriteMode.failExisting,
Encoding encoding = utf8,
String? lineTerminator,
]) {
final parent = _upToLast(path);
final base = context.basename(path);

if (parent is _Directory) {
// Do something with the file contents.
parent.children[base] = _File();
} else {
throw UnsupportedError('XXX');
}
}
}
2 changes: 1 addition & 1 deletion pkgs/io_file/lib/src/file_system.dart
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,7 @@ class WriteMode {
///
/// 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.
/// this class should reach out to me.
@sealed
abstract class FileSystem {
/// Create a directory at the given path.
Expand Down
3 changes: 3 additions & 0 deletions pkgs/io_file/lib/src/posix_file_system.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import '../io_file.dart';

abstract class PosixFileSystem extends FileSystem {}
2 changes: 1 addition & 1 deletion pkgs/io_file/lib/src/vm_file_system_property.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,4 @@ import 'vm_windows_file_system.dart';

/// Return the default [FileSystem] for the current platform.
FileSystem get fileSystem =>
Platform.isWindows ? WindowsFileSystem() : PosixFileSystem();
Platform.isWindows ? WindowsFileSystem() : NativePosixFileSystem();
Loading
Loading