| // 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); |
| } |
| } |