Merge `encoding.dart` and `resurrectors.dart` (#30)
This merges the two concepts into one set of `Converter` and
`Codec` types that all live in `codecs.dart`.
Part of #11
diff --git a/lib/src/backends/record_replay/codecs.dart b/lib/src/backends/record_replay/codecs.dart
new file mode 100644
index 0000000..443c337
--- /dev/null
+++ b/lib/src/backends/record_replay/codecs.dart
@@ -0,0 +1,377 @@
+// 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:file/file.dart';
+import 'package:path/path.dart' as path;
+
+import 'common.dart';
+import 'events.dart';
+import 'replay_directory.dart';
+import 'replay_file.dart';
+import 'replay_file_stat.dart';
+import 'replay_file_system.dart';
+import 'replay_link.dart';
+import 'result_reference.dart';
+
+/// Converter that leaves object untouched.
+const Converter<dynamic, dynamic> kPassthrough = const _PassthroughConverter();
+
+/// Converter that will turn an object into a [Future] of that object.
+const Converter<dynamic, dynamic> kFutureReviver =
+ const _FutureDecoder<dynamic>();
+
+/// Converter that will deserialize a [DateTime].
+const Converter<dynamic, dynamic> kDateTimeReviver = _DateTimeCodec.kDecoder;
+
+/// Converter that will deserialize a [FileStat].
+const Converter<dynamic, dynamic> kFileStatReviver = _FileStatCodec.kDecoder;
+
+/// Converter that will deserialize a [FileSystemEntityType].
+const Converter<dynamic, dynamic> kEntityTypeReviver =
+ _EntityTypeCodec.kDecoder;
+
+/// Converter that will deserialize a [path.Context].
+const Converter<dynamic, dynamic> kPathContextReviver =
+ _PathContextCodec.kDecoder;
+
+/// Converter that will deserialize a [ReplayDirectory].
+Converter<dynamic, dynamic> directoryReviver(ReplayFileSystemImpl fileSystem) =>
+ new _DirectoryDecoder(fileSystem);
+
+/// Converter that will deserialize a [ReplayFile].
+Converter<dynamic, dynamic> fileReviver(ReplayFileSystemImpl fileSystem) =>
+ new _FileDecoder(fileSystem);
+
+/// Converter that will deserialize a [ReplayLink].
+Converter<dynamic, dynamic> linkReviver(ReplayFileSystemImpl fileSystem) =>
+ new _LinkDecoder(fileSystem);
+
+/// Encodes an arbitrary [object] into a JSON-ready representation (a number,
+/// boolean, string, null, list, or map).
+///
+/// Returns a value suitable for conversion into JSON using [JsonEncoder]
+/// without the need for a `toEncodable` argument.
+dynamic encode(dynamic object) => const _GenericEncoder().convert(object);
+
+typedef T _ConverterDelegate<S, T>(S input);
+
+class _ForwardingConverter<S, T> extends Converter<S, T> {
+ final _ConverterDelegate<S, T> _delegate;
+
+ const _ForwardingConverter(this._delegate);
+
+ @override
+ T convert(S input) => _delegate(input);
+}
+
+class _GenericEncoder extends Converter<dynamic, dynamic> {
+ const _GenericEncoder();
+
+ /// Known encoders. Types not covered here will be encoded using
+ /// [_encodeDefault].
+ ///
+ /// When encoding an object, we will walk this map in insertion order looking
+ /// for a matching encoder. Thus, when there are two encoders that match an
+ /// object, the first one will win.
+ static const Map<TypeMatcher<dynamic>, Converter<Object, Object>> _encoders =
+ const <TypeMatcher<dynamic>, Converter<Object, Object>>{
+ const TypeMatcher<num>(): const _PassthroughConverter(),
+ const TypeMatcher<bool>(): const _PassthroughConverter(),
+ const TypeMatcher<String>(): const _PassthroughConverter(),
+ const TypeMatcher<Null>(): const _PassthroughConverter(),
+ const TypeMatcher<Iterable<dynamic>>(): const _IterableEncoder(),
+ const TypeMatcher<Map<dynamic, dynamic>>(): const _MapEncoder(),
+ const TypeMatcher<Symbol>(): const _SymbolEncoder(),
+ const TypeMatcher<DateTime>(): _DateTimeCodec.kEncoder,
+ const TypeMatcher<Uri>(): const _ToStringEncoder(),
+ const TypeMatcher<path.Context>(): _PathContextCodec.kEncoder,
+ const TypeMatcher<ResultReference<dynamic>>(): const _ResultEncoder(),
+ const TypeMatcher<LiveInvocationEvent<dynamic>>(): const _EventEncoder(),
+ const TypeMatcher<ReplayAware>(): const _ReplayAwareEncoder(),
+ const TypeMatcher<Encoding>(): const _EncodingEncoder(),
+ const TypeMatcher<FileMode>(): const _FileModeEncoder(),
+ const TypeMatcher<FileStat>(): _FileStatCodec.kEncoder,
+ const TypeMatcher<FileSystemEntityType>(): _EntityTypeCodec.kEncoder,
+ const TypeMatcher<FileSystemEvent>(): const _FileSystemEventEncoder(),
+ };
+
+ /// Default encoder (used for types not covered in [_encoders]).
+ static String _encodeDefault(dynamic object) => object.runtimeType.toString();
+
+ @override
+ dynamic convert(dynamic input) {
+ Converter<dynamic, dynamic> encoder =
+ const _ForwardingConverter<dynamic, String>(_encodeDefault);
+ for (TypeMatcher<dynamic> matcher in _encoders.keys) {
+ if (matcher.matches(input)) {
+ encoder = _encoders[matcher];
+ break;
+ }
+ }
+ return encoder.convert(input);
+ }
+}
+
+class _PassthroughConverter extends Converter<dynamic, dynamic> {
+ const _PassthroughConverter();
+
+ @override
+ dynamic convert(dynamic input) => input;
+}
+
+class _IterableEncoder extends Converter<Iterable<dynamic>, List<dynamic>> {
+ const _IterableEncoder();
+
+ @override
+ List<dynamic> convert(Iterable<dynamic> input) {
+ _GenericEncoder generic = const _GenericEncoder();
+ List<dynamic> encoded = <dynamic>[];
+ for (Object element in input) {
+ encoded.add(generic.convert(element));
+ }
+ return encoded;
+ }
+}
+
+class _MapEncoder
+ extends Converter<Map<dynamic, dynamic>, Map<String, dynamic>> {
+ const _MapEncoder();
+
+ @override
+ Map<String, dynamic> convert(Map<dynamic, dynamic> input) {
+ _GenericEncoder generic = const _GenericEncoder();
+ Map<String, dynamic> encoded = <String, dynamic>{};
+ for (dynamic key in input.keys) {
+ String encodedKey = generic.convert(key);
+ encoded[encodedKey] = generic.convert(input[key]);
+ }
+ return encoded;
+ }
+}
+
+class _SymbolEncoder extends Converter<Symbol, String> {
+ const _SymbolEncoder();
+
+ @override
+ String convert(Symbol input) => getSymbolName(input);
+}
+
+class _DateTimeCodec extends Codec<DateTime, int> {
+ const _DateTimeCodec();
+
+ static int _encode(DateTime input) => input.millisecondsSinceEpoch;
+
+ static DateTime _decode(int input) =>
+ new DateTime.fromMillisecondsSinceEpoch(input);
+
+ static const Converter<DateTime, int> kEncoder =
+ const _ForwardingConverter<DateTime, int>(_encode);
+
+ static const Converter<int, DateTime> kDecoder =
+ const _ForwardingConverter<int, DateTime>(_decode);
+
+ @override
+ Converter<DateTime, int> get encoder => kEncoder;
+
+ @override
+ Converter<int, DateTime> get decoder => kDecoder;
+}
+
+class _ToStringEncoder extends Converter<Object, String> {
+ const _ToStringEncoder();
+
+ @override
+ String convert(Object input) => input.toString();
+}
+
+class _PathContextCodec extends Codec<path.Context, Map<String, String>> {
+ const _PathContextCodec();
+
+ static Map<String, String> _encode(path.Context input) {
+ return <String, String>{
+ 'style': input.style.name,
+ 'cwd': input.current,
+ };
+ }
+
+ static path.Context _decode(Map<String, String> input) {
+ return new path.Context(
+ style: <String, path.Style>{
+ 'posix': path.Style.posix,
+ 'windows': path.Style.windows,
+ 'url': path.Style.url,
+ }[input['style']],
+ current: input['cwd'],
+ );
+ }
+
+ static const Converter<path.Context, Map<String, String>> kEncoder =
+ const _ForwardingConverter<path.Context, Map<String, String>>(_encode);
+
+ static const Converter<Map<String, String>, path.Context> kDecoder =
+ const _ForwardingConverter<Map<String, String>, path.Context>(_decode);
+
+ @override
+ Converter<path.Context, Map<String, String>> get encoder => kEncoder;
+
+ @override
+ Converter<Map<String, String>, path.Context> get decoder => kDecoder;
+}
+
+class _ResultEncoder extends Converter<ResultReference<dynamic>, Object> {
+ const _ResultEncoder();
+
+ @override
+ Object convert(ResultReference<dynamic> input) => input.serializedValue;
+}
+
+class _EventEncoder
+ extends Converter<LiveInvocationEvent<dynamic>, Map<String, Object>> {
+ const _EventEncoder();
+
+ @override
+ Map<String, Object> convert(LiveInvocationEvent<dynamic> input) {
+ return input.serialize();
+ }
+}
+
+class _ReplayAwareEncoder extends Converter<ReplayAware, String> {
+ const _ReplayAwareEncoder();
+
+ @override
+ String convert(ReplayAware input) => input.identifier;
+}
+
+class _EncodingEncoder extends Converter<Encoding, String> {
+ const _EncodingEncoder();
+
+ @override
+ String convert(Encoding input) => input.name;
+}
+
+class _FileModeEncoder extends Converter<FileMode, String> {
+ const _FileModeEncoder();
+
+ @override
+ String convert(FileMode input) {
+ switch (input) {
+ case FileMode.READ:
+ return 'READ';
+ case FileMode.WRITE:
+ return 'WRITE';
+ case FileMode.APPEND:
+ return 'APPEND';
+ case FileMode.WRITE_ONLY:
+ return 'WRITE_ONLY';
+ case FileMode.WRITE_ONLY_APPEND:
+ return 'WRITE_ONLY_APPEND';
+ }
+ throw new ArgumentError('Invalid value: $input');
+ }
+}
+
+class _FileStatCodec extends Codec<FileStat, Map<String, Object>> {
+ const _FileStatCodec();
+
+ static Map<String, Object> _encode(FileStat input) {
+ return <String, dynamic>{
+ 'changed': const _DateTimeCodec().encode(input.changed),
+ 'modified': const _DateTimeCodec().encode(input.modified),
+ 'accessed': const _DateTimeCodec().encode(input.accessed),
+ 'type': const _EntityTypeCodec().encode(input.type),
+ 'mode': input.mode,
+ 'size': input.size,
+ 'modeString': input.modeString(),
+ };
+ }
+
+ static FileStat _decode(Map<String, Object> input) =>
+ new ReplayFileStat(input);
+
+ static const Converter<FileStat, Map<String, Object>> kEncoder =
+ const _ForwardingConverter<FileStat, Map<String, Object>>(_encode);
+
+ static const Converter<Map<String, Object>, FileStat> kDecoder =
+ const _ForwardingConverter<Map<String, Object>, FileStat>(_decode);
+
+ @override
+ Converter<FileStat, Map<String, Object>> get encoder => kEncoder;
+
+ @override
+ Converter<Map<String, Object>, FileStat> get decoder => kDecoder;
+}
+
+class _EntityTypeCodec extends Codec<FileSystemEntityType, String> {
+ const _EntityTypeCodec();
+
+ static String _encode(FileSystemEntityType input) => input.toString();
+
+ static FileSystemEntityType _decode(String input) {
+ return const <String, FileSystemEntityType>{
+ 'FILE': FileSystemEntityType.FILE,
+ 'DIRECTORY': FileSystemEntityType.DIRECTORY,
+ 'LINK': FileSystemEntityType.LINK,
+ 'NOT_FOUND': FileSystemEntityType.NOT_FOUND,
+ }[input];
+ }
+
+ static const Converter<FileSystemEntityType, String> kEncoder =
+ const _ForwardingConverter<FileSystemEntityType, String>(_encode);
+
+ static const Converter<String, FileSystemEntityType> kDecoder =
+ const _ForwardingConverter<String, FileSystemEntityType>(_decode);
+
+ @override
+ Converter<FileSystemEntityType, String> get encoder => kEncoder;
+
+ @override
+ Converter<String, FileSystemEntityType> get decoder => kDecoder;
+}
+
+class _FileSystemEventEncoder
+ extends Converter<FileSystemEvent, Map<String, Object>> {
+ const _FileSystemEventEncoder();
+
+ @override
+ Map<String, Object> convert(FileSystemEvent input) {
+ return <String, Object>{
+ 'type': input.type,
+ 'path': input.path,
+ };
+ }
+}
+
+class _FutureDecoder<T> extends Converter<T, Future<T>> {
+ const _FutureDecoder();
+
+ @override
+ Future<T> convert(T input) async => input;
+}
+
+class _DirectoryDecoder extends Converter<String, Directory> {
+ final ReplayFileSystemImpl fileSystem;
+ const _DirectoryDecoder(this.fileSystem);
+
+ @override
+ Directory convert(String input) => new ReplayDirectory(fileSystem, input);
+}
+
+class _FileDecoder extends Converter<String, File> {
+ final ReplayFileSystemImpl fileSystem;
+ const _FileDecoder(this.fileSystem);
+
+ @override
+ File convert(String input) => new ReplayFile(fileSystem, input);
+}
+
+class _LinkDecoder extends Converter<String, Link> {
+ final ReplayFileSystemImpl fileSystem;
+ const _LinkDecoder(this.fileSystem);
+
+ @override
+ Link convert(String input) => new ReplayLink(fileSystem, input);
+}
diff --git a/lib/src/backends/record_replay/common.dart b/lib/src/backends/record_replay/common.dart
index 2368047..9313f90 100644
--- a/lib/src/backends/record_replay/common.dart
+++ b/lib/src/backends/record_replay/common.dart
@@ -92,11 +92,11 @@
/// opaque identifier.
///
/// Unlike other objects, objects that are replay-aware don't need to serialize
-/// meaningful metadata about their state for the sake of resurrection. Rather,
-/// they derive all the information they need to operate from the recording.
-/// As such, they are serialized using only an opaque unique identifier. When
-/// they are resurrected during replay, their identifier allows them to find
-/// invocations in the recording for which they are the target.
+/// meaningful metadata about their state for the sake of revival. Rather, they
+/// derive all the information they need to operate from the recording. As such,
+/// they are serialized using only an opaque unique identifier. When they are
+/// revived during replay, their identifier allows them to find invocations in
+/// the recording for which they are the target.
abstract class ReplayAware {
/// The identifier of this object, guaranteed to be unique within a single
/// recording.
diff --git a/lib/src/backends/record_replay/encoding.dart b/lib/src/backends/record_replay/encoding.dart
deleted file mode 100644
index f43c474..0000000
--- a/lib/src/backends/record_replay/encoding.dart
+++ /dev/null
@@ -1,143 +0,0 @@
-// 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:convert';
-
-import 'package:file/file.dart';
-import 'package:path/path.dart' as p;
-
-import 'common.dart';
-import 'events.dart';
-import 'result_reference.dart';
-
-/// Encodes an object into a JSON-ready representation.
-///
-/// Must return one of {number, boolean, string, null, list, or map}.
-typedef dynamic _Encoder(dynamic object);
-
-/// Known encoders. Types not covered here will be encoded using
-/// [_encodeDefault].
-///
-/// When encoding an object, we will walk this map in iteration order looking
-/// for a matching encoder. Thus, when there are two encoders that match an
-// object, the first one will win.
-const Map<TypeMatcher<dynamic>, _Encoder> _kEncoders =
- const <TypeMatcher<dynamic>, _Encoder>{
- const TypeMatcher<num>(): _encodeRaw,
- const TypeMatcher<bool>(): _encodeRaw,
- const TypeMatcher<String>(): _encodeRaw,
- const TypeMatcher<Null>(): _encodeRaw,
- const TypeMatcher<Iterable<dynamic>>(): _encodeIterable,
- const TypeMatcher<Map<dynamic, dynamic>>(): _encodeMap,
- const TypeMatcher<Symbol>(): getSymbolName,
- const TypeMatcher<DateTime>(): _encodeDateTime,
- const TypeMatcher<Uri>(): _encodeUri,
- const TypeMatcher<p.Context>(): _encodePathContext,
- const TypeMatcher<ResultReference<dynamic>>(): _encodeResultReference,
- const TypeMatcher<LiveInvocationEvent<dynamic>>(): _encodeEvent,
- const TypeMatcher<ReplayAware>(): _encodeReplayAwareEntity,
- const TypeMatcher<Encoding>(): _encodeEncoding,
- const TypeMatcher<FileMode>(): _encodeFileMode,
- const TypeMatcher<FileStat>(): _encodeFileStat,
- const TypeMatcher<FileSystemEntityType>(): _encodeFileSystemEntityType,
- const TypeMatcher<FileSystemEvent>(): _encodeFileSystemEvent,
-};
-
-/// Encodes an arbitrary [object] into a JSON-ready representation (a number,
-/// boolean, string, null, list, or map).
-///
-/// Returns a value suitable for conversion into JSON using [JsonEncoder]
-/// without the need for a `toEncodable` argument.
-dynamic encode(dynamic object) {
- _Encoder encoder = _encodeDefault;
- for (TypeMatcher<dynamic> matcher in _kEncoders.keys) {
- if (matcher.matches(object)) {
- encoder = _kEncoders[matcher];
- break;
- }
- }
- return encoder(object);
-}
-
-/// Default encoder (used for types not covered in [_kEncoders]).
-String _encodeDefault(dynamic object) => object.runtimeType.toString();
-
-/// Pass-through encoder (used on `num`, `bool`, `String`, and `Null`).
-dynamic _encodeRaw(dynamic object) => object;
-
-/// Encodes the specified [iterable] into a JSON-ready list of encoded items.
-List<dynamic> _encodeIterable(Iterable<dynamic> iterable) {
- List<dynamic> encoded = <dynamic>[];
- for (dynamic element in iterable) {
- encoded.add(encode(element));
- }
- return encoded;
-}
-
-/// Encodes the specified [map] into a JSON-ready map of encoded key/value
-/// pairs.
-Map<String, dynamic> _encodeMap(Map<dynamic, dynamic> map) {
- Map<String, dynamic> encoded = <String, dynamic>{};
- for (dynamic key in map.keys) {
- String encodedKey = encode(key);
- encoded[encodedKey] = encode(map[key]);
- }
- return encoded;
-}
-
-int _encodeDateTime(DateTime dateTime) => dateTime.millisecondsSinceEpoch;
-
-String _encodeUri(Uri uri) => uri.toString();
-
-Map<String, String> _encodePathContext(p.Context context) {
- return <String, String>{
- 'style': context.style.name,
- 'cwd': context.current,
- };
-}
-
-dynamic _encodeResultReference(ResultReference<dynamic> reference) =>
- reference.serializedValue;
-
-Map<String, dynamic> _encodeEvent(LiveInvocationEvent<dynamic> event) =>
- event.serialize();
-
-String _encodeReplayAwareEntity(ReplayAware entity) => entity.identifier;
-
-String _encodeEncoding(Encoding encoding) => encoding.name;
-
-String _encodeFileMode(FileMode fileMode) {
- switch (fileMode) {
- case FileMode.READ:
- return 'READ';
- case FileMode.WRITE:
- return 'WRITE';
- case FileMode.APPEND:
- return 'APPEND';
- case FileMode.WRITE_ONLY:
- return 'WRITE_ONLY';
- case FileMode.WRITE_ONLY_APPEND:
- return 'WRITE_ONLY_APPEND';
- }
- throw new ArgumentError('Invalid value: $fileMode');
-}
-
-Map<String, dynamic> _encodeFileStat(FileStat stat) => <String, dynamic>{
- 'changed': _encodeDateTime(stat.changed),
- 'modified': _encodeDateTime(stat.modified),
- 'accessed': _encodeDateTime(stat.accessed),
- 'type': _encodeFileSystemEntityType(stat.type),
- 'mode': stat.mode,
- 'size': stat.size,
- 'modeString': stat.modeString(),
- };
-
-String _encodeFileSystemEntityType(FileSystemEntityType type) =>
- type.toString();
-
-Map<String, dynamic> _encodeFileSystemEvent(FileSystemEvent event) =>
- <String, dynamic>{
- 'type': event.type,
- 'path': event.path,
- };
diff --git a/lib/src/backends/record_replay/errors.dart b/lib/src/backends/record_replay/errors.dart
index c7892d3..8e199ba 100644
--- a/lib/src/backends/record_replay/errors.dart
+++ b/lib/src/backends/record_replay/errors.dart
@@ -2,8 +2,8 @@
// 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 'codecs.dart';
import 'common.dart';
-import 'encoding.dart';
/// Error thrown during replay when there is no matching invocation in the
/// recording.
diff --git a/lib/src/backends/record_replay/events.dart b/lib/src/backends/record_replay/events.dart
index 75ffd15..65bab49 100644
--- a/lib/src/backends/record_replay/events.dart
+++ b/lib/src/backends/record_replay/events.dart
@@ -4,8 +4,8 @@
import 'dart:async';
+import 'codecs.dart';
import 'common.dart';
-import 'encoding.dart';
import 'recording.dart';
import 'result_reference.dart';
diff --git a/lib/src/backends/record_replay/mutable_recording.dart b/lib/src/backends/record_replay/mutable_recording.dart
index 60ce88e..2693291 100644
--- a/lib/src/backends/record_replay/mutable_recording.dart
+++ b/lib/src/backends/record_replay/mutable_recording.dart
@@ -8,8 +8,8 @@
import 'package:file/file.dart';
import 'package:intl/intl.dart';
+import 'codecs.dart';
import 'common.dart';
-import 'encoding.dart';
import 'events.dart';
import 'recording.dart';
diff --git a/lib/src/backends/record_replay/replay_directory.dart b/lib/src/backends/record_replay/replay_directory.dart
index 928db67..abac3e1 100644
--- a/lib/src/backends/record_replay/replay_directory.dart
+++ b/lib/src/backends/record_replay/replay_directory.dart
@@ -2,11 +2,12 @@
// 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:convert';
+
import 'package:file/file.dart';
import 'replay_file_system.dart';
import 'replay_file_system_entity.dart';
-import 'resurrectors.dart';
/// [Directory] implementation that replays all invocation activity from a
/// prior recording.
@@ -15,7 +16,7 @@
ReplayDirectory(ReplayFileSystemImpl fileSystem, String identifier)
: super(fileSystem, identifier) {
// TODO(tvolkert): fill in resurrectors
- methods.addAll(<Symbol, Resurrector>{
+ methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
#create: null,
#createSync: null,
#createTemp: null,
diff --git a/lib/src/backends/record_replay/replay_file.dart b/lib/src/backends/record_replay/replay_file.dart
index 5a3eb63..931f5a1 100644
--- a/lib/src/backends/record_replay/replay_file.dart
+++ b/lib/src/backends/record_replay/replay_file.dart
@@ -2,11 +2,12 @@
// 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:convert';
+
import 'package:file/file.dart';
import 'replay_file_system.dart';
import 'replay_file_system_entity.dart';
-import 'resurrectors.dart';
/// [File] implementation that replays all invocation activity from a prior
/// recording.
@@ -15,7 +16,7 @@
ReplayFile(ReplayFileSystemImpl fileSystem, String identifier)
: super(fileSystem, identifier) {
// TODO(tvolkert): fill in resurrectors
- methods.addAll(<Symbol, Resurrector>{
+ methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
#create: null,
#createSync: null,
#copy: null,
diff --git a/lib/src/backends/record_replay/replay_file_stat.dart b/lib/src/backends/record_replay/replay_file_stat.dart
index c0ab022..2e91c75 100644
--- a/lib/src/backends/record_replay/replay_file_stat.dart
+++ b/lib/src/backends/record_replay/replay_file_stat.dart
@@ -4,7 +4,7 @@
import 'package:file/file.dart';
-import 'resurrectors.dart';
+import 'codecs.dart';
/// [FileStat] implementation that derives its properties from a recorded
/// invocation event.
@@ -16,16 +16,16 @@
ReplayFileStat(Map<String, dynamic> data) : _data = data;
@override
- DateTime get changed => resurrectDateTime(_data['changed']);
+ DateTime get changed => kDateTimeReviver.convert(_data['changed']);
@override
- DateTime get modified => resurrectDateTime(_data['modified']);
+ DateTime get modified => kDateTimeReviver.convert(_data['modified']);
@override
- DateTime get accessed => resurrectDateTime(_data['accessed']);
+ DateTime get accessed => kDateTimeReviver.convert(_data['accessed']);
@override
- FileSystemEntityType get type => resurrectFileSystemEntityType(_data['type']);
+ FileSystemEntityType get type => kEntityTypeReviver.convert(_data['type']);
@override
int get mode => _data['mode'];
diff --git a/lib/src/backends/record_replay/replay_file_system.dart b/lib/src/backends/record_replay/replay_file_system.dart
index 5a36354..df77057 100644
--- a/lib/src/backends/record_replay/replay_file_system.dart
+++ b/lib/src/backends/record_replay/replay_file_system.dart
@@ -7,11 +7,11 @@
import 'package:file/file.dart';
import 'package:meta/meta.dart';
+import 'codecs.dart';
import 'common.dart';
import 'errors.dart';
import 'recording_file_system.dart';
import 'replay_proxy_mixin.dart';
-import 'resurrectors.dart';
/// A file system that replays invocations from a prior recording for use
/// in tests.
@@ -70,24 +70,24 @@
implements ReplayFileSystem, ReplayAware {
/// Creates a new `ReplayFileSystemImpl`.
ReplayFileSystemImpl(this.manifest) {
- methods.addAll(<Symbol, Resurrector>{
- #directory: resurrectDirectory(this),
- #file: resurrectFile(this),
- #link: resurrectLink(this),
- #stat: resurrectFuture(resurrectFileStat),
- #statSync: resurrectFileStat,
- #identical: resurrectFuture(resurrectPassthrough),
- #identicalSync: resurrectPassthrough,
- #type: resurrectFuture(resurrectFileSystemEntityType),
- #typeSync: resurrectFileSystemEntityType,
+ methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
+ #directory: directoryReviver(this),
+ #file: fileReviver(this),
+ #link: linkReviver(this),
+ #stat: kFileStatReviver.fuse(kFutureReviver),
+ #statSync: kFileStatReviver,
+ #identical: kPassthrough.fuse(kFutureReviver),
+ #identicalSync: kPassthrough,
+ #type: kEntityTypeReviver.fuse(kFutureReviver),
+ #typeSync: kEntityTypeReviver,
});
- properties.addAll(<Symbol, Resurrector>{
- #path: resurrectPathContext,
- #systemTempDirectory: resurrectDirectory(this),
- #currentDirectory: resurrectDirectory(this),
- const Symbol('currentDirectory='): resurrectPassthrough,
- #isWatchSupported: resurrectPassthrough,
+ properties.addAll(<Symbol, Converter<dynamic, dynamic>>{
+ #path: kPathContextReviver,
+ #systemTempDirectory: directoryReviver(this),
+ #currentDirectory: directoryReviver(this),
+ const Symbol('currentDirectory='): kPassthrough,
+ #isWatchSupported: kPassthrough,
});
}
diff --git a/lib/src/backends/record_replay/replay_file_system_entity.dart b/lib/src/backends/record_replay/replay_file_system_entity.dart
index d74ede3..3dfd39d 100644
--- a/lib/src/backends/record_replay/replay_file_system_entity.dart
+++ b/lib/src/backends/record_replay/replay_file_system_entity.dart
@@ -2,11 +2,13 @@
// 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:convert';
+
import 'package:file/file.dart';
+import 'codecs.dart';
import 'replay_file_system.dart';
import 'replay_proxy_mixin.dart';
-import 'resurrectors.dart';
/// [FileSystemEntity] implementation that replays all invocation activity
/// from a prior recording.
@@ -16,7 +18,7 @@
/// Creates a new `ReplayFileSystemEntity`.
ReplayFileSystemEntity(this.fileSystem, this.identifier) {
// TODO(tvolkert): fill in resurrectors
- methods.addAll(<Symbol, Resurrector>{
+ methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
#exists: null,
#existsSync: null,
#rename: null,
@@ -31,8 +33,8 @@
});
// TODO(tvolkert): fill in resurrectors
- properties.addAll(<Symbol, Resurrector>{
- #path: resurrectPassthrough,
+ properties.addAll(<Symbol, Converter<dynamic, dynamic>>{
+ #path: kPassthrough,
#uri: null,
#isAbsolute: null,
#absolute: null,
diff --git a/lib/src/backends/record_replay/replay_link.dart b/lib/src/backends/record_replay/replay_link.dart
index aed0b35..d23e826 100644
--- a/lib/src/backends/record_replay/replay_link.dart
+++ b/lib/src/backends/record_replay/replay_link.dart
@@ -2,11 +2,12 @@
// 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:convert';
+
import 'package:file/file.dart';
import 'replay_file_system.dart';
import 'replay_file_system_entity.dart';
-import 'resurrectors.dart';
/// [Link] implementation that replays all invocation activity from a prior
/// recording.
@@ -15,7 +16,7 @@
ReplayLink(ReplayFileSystemImpl fileSystem, String identifier)
: super(fileSystem, identifier) {
// TODO(tvolkert): fill in resurrectors
- methods.addAll(<Symbol, Resurrector>{
+ methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
#create: null,
#createSync: null,
#update: null,
diff --git a/lib/src/backends/record_replay/replay_proxy_mixin.dart b/lib/src/backends/record_replay/replay_proxy_mixin.dart
index 60a9502..401781b 100644
--- a/lib/src/backends/record_replay/replay_proxy_mixin.dart
+++ b/lib/src/backends/record_replay/replay_proxy_mixin.dart
@@ -2,13 +2,14 @@
// 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:convert';
+
import 'package:meta/meta.dart';
+import 'codecs.dart';
import 'common.dart';
-import 'encoding.dart';
import 'errors.dart';
import 'proxy.dart';
-import 'resurrectors.dart';
typedef bool _InvocationMatcher(Map<String, dynamic> entry);
@@ -47,37 +48,39 @@
/// final String identifier;
///
/// ReplayFoo(this.manifest, this.identifier) {
-/// methods.addAll(<Symbol, Resurrector>{
-/// #sampleMethod: resurrectComplexObject,
+/// methods.addAll(<Symbol, Converter<dynamic, dynamic>>{
+/// #sampleMethod: complexObjectReviver,
/// });
///
-/// properties.addAll(<Symbol, Resurrector>{
-/// #sampleParent: resurrectFoo,
-/// const Symbol('sampleParent='): resurrectPassthrough,
+/// properties.addAll(<Symbol, Converter<dynamic, dynamic>>{
+/// #sampleParent: fooReviver,
+/// const Symbol('sampleParent='): passthroughReviver,
/// });
/// }
/// }
abstract class ReplayProxyMixin implements ProxyObject, ReplayAware {
- /// Maps method names to [Resurrector] functions.
+ /// Maps method names to [Converter]s that will revive result values.
///
/// Invocations of methods listed in this map will be replayed by looking for
- /// matching invocations in the [manifest] and resurrecting the invocation
- /// return value using the [Resurrector] found in this map.
+ /// matching invocations in the [manifest] and reviving the invocation return
+ /// value using the [Converter] found in this map.
@protected
- final Map<Symbol, Resurrector> methods = <Symbol, Resurrector>{};
+ final Map<Symbol, Converter<dynamic, dynamic>> methods =
+ <Symbol, Converter<dynamic, dynamic>>{};
- /// Maps property getter and setter names to [Resurrector] functions.
+ /// Maps property getter and setter names to [Converter]s that will revive
+ /// result values.
///
/// Access and mutation of properties listed in this map will be replayed
- /// by looking for matching property accesses in the [manifest] and
- /// resurrecting the invocation return value using the [Resurrector] found
- /// in this map.
+ /// by looking for matching property accesses in the [manifest] and reviving
+ /// the invocation return value using the [Converter] found in this map.
///
/// The keys for property getters are the simple property names, whereas the
/// keys for property setters are the property names followed by an equals
/// sign (e.g. `propertyName=`).
@protected
- final Map<Symbol, Resurrector> properties = <Symbol, Resurrector>{};
+ final Map<Symbol, Converter<dynamic, dynamic>> properties =
+ <Symbol, Converter<dynamic, dynamic>>{};
/// The manifest of recorded invocation events.
///
@@ -92,11 +95,11 @@
@override
dynamic noSuchMethod(Invocation invocation) {
Symbol name = invocation.memberName;
- Resurrector resurrector =
+ Converter<dynamic, dynamic> reviver =
invocation.isAccessor ? properties[name] : methods[name];
- if (resurrector == null) {
- // No resurrector generally means that there truly is no such method on
+ if (reviver == null) {
+ // No reviver generally means that there truly is no such method on
// this object. The exception is when the invocation represents a getter
// on a method, in which case we return a method proxy that, when
// invoked, will replay the desired invocation.
@@ -111,7 +114,7 @@
}
entry[kManifestOrdinalKey] = _nextOrdinal++;
- return resurrector(entry[kManifestResultKey]);
+ return reviver.convert(entry[kManifestResultKey]);
}
/// Finds the next available invocation event in the [manifest] that matches
diff --git a/lib/src/backends/record_replay/result_reference.dart b/lib/src/backends/record_replay/result_reference.dart
index 211d006..fa6e9c4 100644
--- a/lib/src/backends/record_replay/result_reference.dart
+++ b/lib/src/backends/record_replay/result_reference.dart
@@ -6,7 +6,7 @@
import 'package:meta/meta.dart';
-import 'encoding.dart';
+import 'codecs.dart';
import 'events.dart';
import 'recording_proxy_mixin.dart';
diff --git a/lib/src/backends/record_replay/resurrectors.dart b/lib/src/backends/record_replay/resurrectors.dart
deleted file mode 100644
index a221cc7..0000000
--- a/lib/src/backends/record_replay/resurrectors.dart
+++ /dev/null
@@ -1,85 +0,0 @@
-// 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 'package:file/file.dart';
-import 'package:path/path.dart' as path;
-
-import 'replay_directory.dart';
-import 'replay_file.dart';
-import 'replay_file_stat.dart';
-import 'replay_file_system.dart';
-import 'replay_link.dart';
-
-/// Resurrects an invocation result (return value) from the specified
-/// serialized [data].
-typedef Object Resurrector(dynamic data);
-
-/// Returns a [Resurrector] that will wrap the return value of the specified
-/// [delegate] in a [Future].
-Resurrector resurrectFuture(Resurrector delegate) {
- return (dynamic serializedResult) async => delegate(serializedResult);
-}
-
-/// Returns a [Resurrector] that will resurrect a [ReplayDirectory] that is
-/// tied to the specified [fileSystem].
-Resurrector resurrectDirectory(ReplayFileSystemImpl fileSystem) {
- return (String identifier) {
- return new ReplayDirectory(fileSystem, identifier);
- };
-}
-
-/// Returns a [Resurrector] that will resurrect a [ReplayFile] that is tied to
-/// the specified [fileSystem].
-Resurrector resurrectFile(ReplayFileSystemImpl fileSystem) {
- return (String identifier) {
- return new ReplayFile(fileSystem, identifier);
- };
-}
-
-/// Returns a [Resurrector] that will resurrect a [ReplayLink] that is tied to
-/// the specified [fileSystem].
-Resurrector resurrectLink(ReplayFileSystemImpl fileSystem) {
- return (String identifier) {
- return new ReplayLink(fileSystem, identifier);
- };
-}
-
-/// Resurrects a [FileStat] from the specified serialized [data].
-FileStat resurrectFileStat(Map<String, dynamic> data) {
- return new ReplayFileStat(data);
-}
-
-/// Resurrects a [DateTime] from the specified [milliseconds] since the epoch.
-DateTime resurrectDateTime(int milliseconds) {
- return new DateTime.fromMillisecondsSinceEpoch(milliseconds);
-}
-
-/// Resurrects a [FileSystemEntityType] from the specified string
-/// representation.
-FileSystemEntityType resurrectFileSystemEntityType(String type) {
- return const <String, FileSystemEntityType>{
- 'FILE': FileSystemEntityType.FILE,
- 'DIRECTORY': FileSystemEntityType.DIRECTORY,
- 'LINK': FileSystemEntityType.LINK,
- 'NOT_FOUND': FileSystemEntityType.NOT_FOUND,
- }[type];
-}
-
-/// Resurrects a value whose serialized representation is the same the real
-/// value.
-dynamic resurrectPassthrough(dynamic value) => value;
-
-/// Resurrects a [path.Context] from the specified serialized [data]
-path.Context resurrectPathContext(Map<String, String> data) {
- return new path.Context(
- style: <String, path.Style>{
- 'posix': path.Style.posix,
- 'windows': path.Style.windows,
- 'url': path.Style.url,
- }[data['style']],
- current: data['cwd'],
- );
-}
diff --git a/test/recording_test.dart b/test/recording_test.dart
index 545b146..7efcf2f 100644
--- a/test/recording_test.dart
+++ b/test/recording_test.dart
@@ -9,8 +9,8 @@
import 'package:file/memory.dart';
import 'package:file/record_replay.dart';
import 'package:file/testing.dart';
+import 'package:file/src/backends/record_replay/codecs.dart';
import 'package:file/src/backends/record_replay/common.dart';
-import 'package:file/src/backends/record_replay/encoding.dart';
import 'package:file/src/backends/record_replay/events.dart';
import 'package:file/src/backends/record_replay/mutable_recording.dart';
import 'package:file/src/backends/record_replay/recording_proxy_mixin.dart';