blob: 6cfb178cf8323bae393b88f6658a6d01e08d4d88 [file] [log] [blame]
// Copyright (c) 2017, 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.
import 'dart:async';
import 'dart:convert';
import 'dart:typed_data';
import 'package:meta/meta.dart';
import 'package:file/file.dart';
import 'package:file/src/io.dart' as io;
import 'recording_file_system.dart';
import 'recording_file_system_entity.dart';
import 'recording_io_sink.dart';
import 'recording_random_access_file.dart';
import 'result_reference.dart';
/// Callback responsible for synchronously writing result [data] to the
/// specified [file].
///
/// See also:
/// - [_BlobReference]
/// - [_BlobStreamReference]
typedef _BlobDataSyncWriter<T> = void Function(File file, T data);
/// Callback responsible for asynchronously writing result [data] to the
/// specified [file].
///
/// See also:
/// - [_BlobFutureReference]
typedef _BlobDataAsyncWriter<T> = Future<void> Function(File file, T data);
/// [File] implementation that records all invocation activity to its file
/// system's recording.
class RecordingFile extends RecordingFileSystemEntity<File> implements File {
/// Creates a new `RecordingFile`.
RecordingFile(RecordingFileSystem fileSystem, io.File delegate)
: super(fileSystem, delegate) {
methods.addAll(<Symbol, Function>{
#create: _create,
#createSync: delegate.createSync,
#copy: _copy,
#copySync: _copySync,
#length: delegate.length,
#lengthSync: delegate.lengthSync,
#lastAccessed: delegate.lastAccessed,
#lastAccessedSync: delegate.lastAccessedSync,
#setLastAccessed: delegate.setLastAccessed,
#setLastAccessedSync: delegate.setLastAccessedSync,
#lastModified: delegate.lastModified,
#lastModifiedSync: delegate.lastModifiedSync,
#setLastModified: delegate.setLastModified,
#setLastModifiedSync: delegate.setLastModifiedSync,
#open: _open,
#openSync: _openSync,
#openRead: _openRead,
#openWrite: _openWrite,
#readAsBytes: _readAsBytes,
#readAsBytesSync: _readAsBytesSync,
#readAsString: _readAsString,
#readAsStringSync: _readAsStringSync,
#readAsLines: _readAsLines,
#readAsLinesSync: _readAsLinesSync,
#writeAsBytes: _writeAsBytes,
#writeAsBytesSync: delegate.writeAsBytesSync,
#writeAsString: _writeAsString,
#writeAsStringSync: delegate.writeAsStringSync,
});
}
@override
File wrap(File delegate) => super.wrap(delegate) ?? wrapFile(delegate);
File _newRecordingFile() => recording.newFile(delegate.basename);
RandomAccessFile _wrapRandomAccessFile(RandomAccessFile delegate) =>
RecordingRandomAccessFile(fileSystem, delegate);
Future<File> _create({bool recursive = false}) =>
delegate.create(recursive: recursive).then(wrap);
Future<File> _copy(String newPath) => delegate.copy(newPath).then(wrap);
File _copySync(String newPath) => wrap(delegate.copySync(newPath));
Future<RandomAccessFile> _open({FileMode mode = FileMode.read}) =>
delegate.open(mode: mode).then(_wrapRandomAccessFile);
RandomAccessFile _openSync({FileMode mode = FileMode.read}) =>
_wrapRandomAccessFile(delegate.openSync(mode: mode));
StreamReference<Uint8List> _openRead([int start, int end]) {
return _BlobStreamReference<Uint8List>(
file: _newRecordingFile(),
stream: delegate.openRead(start, end),
writer: (File file, Uint8List bytes) {
file.writeAsBytesSync(bytes, mode: FileMode.append, flush: true);
},
);
}
IOSink _openWrite(
{FileMode mode = FileMode.write, Encoding encoding = utf8}) {
return RecordingIOSink(
fileSystem,
delegate.openWrite(mode: mode, encoding: encoding),
);
}
FutureReference<Uint8List> _readAsBytes() {
return _BlobFutureReference<Uint8List>(
file: _newRecordingFile(),
future: delegate.readAsBytes(),
writer: (File file, Uint8List bytes) async {
await file.writeAsBytes(bytes, flush: true);
},
);
}
ResultReference<Uint8List> _readAsBytesSync() {
return _BlobReference<Uint8List>(
file: _newRecordingFile(),
value: delegate.readAsBytesSync(),
writer: (File file, Uint8List bytes) {
file.writeAsBytesSync(bytes, flush: true);
},
);
}
FutureReference<String> _readAsString({Encoding encoding = utf8}) {
return _BlobFutureReference<String>(
file: _newRecordingFile(),
future: delegate.readAsString(encoding: encoding),
writer: (File file, String content) async {
await file.writeAsString(content, flush: true);
},
);
}
ResultReference<String> _readAsStringSync({Encoding encoding = utf8}) {
return _BlobReference<String>(
file: _newRecordingFile(),
value: delegate.readAsStringSync(encoding: encoding),
writer: (File file, String content) {
file.writeAsStringSync(content, flush: true);
},
);
}
FutureReference<List<String>> _readAsLines({Encoding encoding = utf8}) {
return _BlobFutureReference<List<String>>(
file: _newRecordingFile(),
future: delegate.readAsLines(encoding: encoding),
writer: (File file, List<String> lines) async {
await file.writeAsString(lines.join('\n'), flush: true);
},
);
}
ResultReference<List<String>> _readAsLinesSync({Encoding encoding = utf8}) {
return _BlobReference<List<String>>(
file: _newRecordingFile(),
value: delegate.readAsLinesSync(encoding: encoding),
writer: (File file, List<String> lines) {
file.writeAsStringSync(lines.join('\n'), flush: true);
},
);
}
Future<File> _writeAsBytes(
List<int> bytes, {
FileMode mode = FileMode.write,
bool flush = false,
}) =>
delegate.writeAsBytes(bytes, mode: mode, flush: flush).then(wrap);
Future<File> _writeAsString(
String contents, {
FileMode mode = FileMode.write,
Encoding encoding = utf8,
bool flush = false,
}) =>
delegate
.writeAsString(contents, mode: mode, encoding: encoding, flush: flush)
.then(wrap);
}
/// A [ResultReference] that serializes its value data to a separate file.
class _BlobReference<T> extends ResultReference<T> {
_BlobReference({
@required File file,
@required T value,
@required _BlobDataSyncWriter<T> writer,
}) : _file = file,
_value = value,
_writer = writer;
final File _file;
final T _value;
final _BlobDataSyncWriter<T> _writer;
@override
T get value {
_writer(_file, _value);
return _value;
}
@override
T get recordedValue => _value;
@override
String get serializedValue => '!${_file.basename}';
}
/// A [FutureReference] that serializes its value data to a separate file.
class _BlobFutureReference<T> extends FutureReference<T> {
_BlobFutureReference({
@required File file,
@required Future<T> future,
@required _BlobDataAsyncWriter<T> writer,
}) : _file = file,
_writer = writer,
super(future);
final File _file;
final _BlobDataAsyncWriter<T> _writer;
@override
Future<T> get value {
return super.value.then((T value) async {
await _writer(_file, value);
return value;
});
}
@override
String get serializedValue => '!${_file.basename}';
}
/// A [StreamReference] that serializes its value data to a separate file.
class _BlobStreamReference<T> extends StreamReference<T> {
_BlobStreamReference({
@required File file,
@required Stream<T> stream,
@required _BlobDataSyncWriter<T> writer,
}) : _file = file,
_writer = writer,
super(stream);
final File _file;
final _BlobDataSyncWriter<T> _writer;
@override
void onData(T event) {
_writer(_file, event);
}
@override
String get serializedValue => '!${_file.basename}';
}