| // 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 '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 void _BlobDataSyncWriter<T>(File file, T data); |
| |
| /// Callback responsible for asynchronously writing result [data] to the |
| /// specified [file]. |
| /// |
| /// See also: |
| /// - [_BlobFutureReference] |
| typedef Future<Null> _BlobDataAsyncWriter<T>(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) => |
| new 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<List<int>> _openRead([int start, int end]) { |
| return new _BlobStreamReference<List<int>>( |
| file: _newRecordingFile(), |
| stream: delegate.openRead(start, end), |
| writer: (File file, List<int> bytes) { |
| file.writeAsBytesSync(bytes, mode: FileMode.APPEND, flush: true); |
| }, |
| ); |
| } |
| |
| IOSink _openWrite({FileMode mode: FileMode.WRITE, Encoding encoding: UTF8}) { |
| return new RecordingIOSink( |
| fileSystem, |
| delegate.openWrite(mode: mode, encoding: encoding), |
| ); |
| } |
| |
| FutureReference<List<int>> _readAsBytes() { |
| return new _BlobFutureReference<List<int>>( |
| file: _newRecordingFile(), |
| future: delegate.readAsBytes(), |
| writer: (File file, List<int> bytes) async { |
| await file.writeAsBytes(bytes, flush: true); |
| }, |
| ); |
| } |
| |
| ResultReference<List<int>> _readAsBytesSync() { |
| return new _BlobReference<List<int>>( |
| file: _newRecordingFile(), |
| value: delegate.readAsBytesSync(), |
| writer: (File file, List<int> bytes) { |
| file.writeAsBytesSync(bytes, flush: true); |
| }, |
| ); |
| } |
| |
| FutureReference<String> _readAsString({Encoding encoding: UTF8}) { |
| return new _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 new _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 new _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 new _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> { |
| final File _file; |
| final T _value; |
| final _BlobDataSyncWriter<T> _writer; |
| |
| _BlobReference({ |
| @required File file, |
| @required T value, |
| @required _BlobDataSyncWriter<T> writer, |
| }) |
| : _file = file, |
| _value = value, |
| _writer = 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> { |
| final File _file; |
| final _BlobDataAsyncWriter<T> _writer; |
| |
| _BlobFutureReference({ |
| @required File file, |
| @required Future<T> future, |
| @required _BlobDataAsyncWriter<T> writer, |
| }) |
| : _file = file, |
| _writer = writer, |
| super(future); |
| |
| @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> { |
| final File _file; |
| final _BlobDataSyncWriter<T> _writer; |
| |
| _BlobStreamReference({ |
| @required File file, |
| @required Stream<T> stream, |
| @required _BlobDataSyncWriter<T> writer, |
| }) |
| : _file = file, |
| _writer = writer, |
| super(stream); |
| |
| @override |
| void onData(T event) { |
| _writer(_file, event); |
| } |
| |
| @override |
| String get serializedValue => '!${_file.basename}'; |
| } |