blob: 45c1177102dfdb768a7cf000880fb0a49070c698 [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:convert';
import 'dart:io' show ProcessStartMode;
import 'can_run_manifest_entry.dart';
import 'manifest_entry.dart';
import 'run_manifest_entry.dart';
/// Tests if two lists contain pairwise equal elements.
bool _areListsEqual/*<T>*/(
List<dynamic/*=T*/ > list1, List<dynamic/*=T*/ > list2) {
int i = 0;
return list1 != null &&
list2 != null &&
list1.length == list2.length &&
list1.every((dynamic element) => element == list2[i++]);
}
/// Tells whether [testValue] is non-null and equal to [entryValue]. If
/// [isEqual] is specified, it will be used to determine said equality.
bool _isHit(
dynamic entryValue,
dynamic testValue, [
bool isEqual(dynamic value1, dynamic value2),
]) {
if (testValue == null) {
return true;
} else if (isEqual != null) {
return isEqual(entryValue, testValue);
}
return entryValue == testValue;
}
/// The process invocation manifest, holding metadata about every recorded
/// process invocation.
class Manifest {
final List<ManifestEntry> _entries = <ManifestEntry>[];
/// Creates a new manifest.
Manifest();
/// Creates a new manifest populated with the specified [json] data.
///
/// If [json] does not represent a valid JSON string (matching the format of
/// [toJson]), a [FormatException] will be thrown.
factory Manifest.fromJson(String json) {
List<Map<String, dynamic>> decoded = new JsonDecoder().convert(json);
Manifest manifest = new Manifest();
decoded.forEach((Map<String, dynamic> entry) {
switch (entry['type']) {
case 'run':
manifest._entries.add(new RunManifestEntry.fromJson(entry['body']));
break;
case 'can_run':
manifest._entries
.add(new CanRunManifestEntry.fromJson(entry['body']));
break;
default:
throw new UnsupportedError(
'Manifest type ${entry['type']} is unkown.');
}
});
return manifest;
}
/// Adds the specified [entry] to this manifest.
void add(ManifestEntry entry) => _entries.add(entry);
/// The number of entries currently in the manifest.
int get length => _entries.length;
/// Gets the entry whose [RunManifestEntry.pid] matches the specified [pid].
ManifestEntry getRunEntry(int pid) {
return _entries.firstWhere(
(ManifestEntry entry) => entry is RunManifestEntry && entry.pid == pid);
}
/// Finds the first manifest entry that has not been invoked and whose
/// metadata matches the specified criteria. If no arguments are specified,
/// this will simply return the first entry that has not yet been invoked.
ManifestEntry findPendingRunEntry({
List<String> command,
ProcessStartMode mode,
Encoding stdoutEncoding,
Encoding stderrEncoding,
}) {
return _entries.firstWhere(
(ManifestEntry entry) {
return entry is RunManifestEntry &&
!entry.invoked &&
_isHit(entry.command, command, _areListsEqual) &&
_isHit(entry.mode, mode) &&
_isHit(entry.stdoutEncoding, stdoutEncoding) &&
_isHit(entry.stderrEncoding, stderrEncoding);
},
orElse: () => null,
);
}
/// Finds the first manifest entry that has not been invoked and whose
/// metadata matches the specified criteria. If no arguments are specified,
/// this will simply return the first entry that has not yet been invoked.
ManifestEntry findPendingCanRunEntry({
String executable,
}) {
return _entries.firstWhere(
(ManifestEntry entry) {
return entry is CanRunManifestEntry &&
!entry.invoked &&
_isHit(entry.executable, executable);
},
orElse: () => null,
);
}
/// Returns a JSON-encoded representation of this manifest.
String toJson() {
List<Map<String, dynamic>> list = <Map<String, dynamic>>[];
_entries.forEach((ManifestEntry entry) => list.add(new JsonBuilder()
.add('type', entry.type)
.add('body', entry.toJson())
.entry));
return const JsonEncoder.withIndent(' ').convert(list);
}
}