blob: 7c0b83c588210fb4bfbd20e8a6794cf5d42816f7 [file]
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'package:flutter_tools_core/flutter_tools_core.dart';
import 'package:flutter_tools_extension/flutter_tools_extension.dart';
import '../base/logger.dart';
import '../doctor_validator.dart';
import 'extension_discovery.dart';
/// A host [DoctorValidator] adapter that delegates diagnostic checks to a single tool extension.
class ExtensionDoctorValidator extends DoctorValidator {
/// Creates an [ExtensionDoctorValidator] for the given diagnostic [extension].
ExtensionDoctorValidator({required this.extension, required Logger logger})
: _logger = logger,
super(extension.title);
/// The active extension service executing diagnostic checks.
final DiagnosticsExtension extension;
final Logger _logger;
@override
String get title => extension.title;
@override
Future<ValidationResult> validateImpl() async {
_logger.printTrace(
'ExtensionDoctorValidator validating diagnostics for extension "${extension.title}".',
);
final List<ValidationResult> results = await extension.runDiagnostics();
_logger.printTrace(
'ExtensionDoctorValidator received ${results.length} validation result(s) from "${extension.title}".',
);
final allMessages = <ValidationMessage>[];
ValidationType aggregateType = ValidationType.success;
String? statusInfo;
for (final result in results) {
statusInfo ??= result.statusInfo;
allMessages.addAll(result.messages);
switch (result.type) {
case ValidationType.crash:
aggregateType = ValidationType.crash;
case ValidationType.missing:
if (aggregateType != ValidationType.crash) {
aggregateType = ValidationType.missing;
}
case ValidationType.partial:
if (aggregateType != ValidationType.crash && aggregateType != ValidationType.missing) {
aggregateType = ValidationType.partial;
}
case ValidationType.notAvailable:
if (aggregateType != ValidationType.crash &&
aggregateType != ValidationType.missing &&
aggregateType != ValidationType.partial) {
aggregateType = ValidationType.notAvailable;
}
case ValidationType.success:
break;
}
}
return ValidationResult(aggregateType, allMessages, statusInfo: statusInfo);
}
}
/// A host-side [DiagnosticsExtension] client adapter that delegates RPC queries to an [ExtensionConnection].
class DiagnosticsExtensionClient extends DiagnosticsExtension {
/// Creates a [DiagnosticsExtensionClient] wrapping the host [connection].
DiagnosticsExtensionClient(
this.connection, {
required Logger logger,
String defaultTitle = 'Tool Extension Diagnostics',
}) : _defaultTitle = defaultTitle,
_logger = logger;
/// The active extension isolate connection.
final ExtensionConnection connection;
final String _defaultTitle;
final Logger _logger;
String? _titleCache;
@override
String get title => _titleCache ?? _defaultTitle;
/// Fetches the validator title from the remote extension isolate.
Future<String> fetchTitle() async {
if (_titleCache != null) {
return _titleCache!;
}
_logger.printTrace(
'DiagnosticsExtensionClient fetching title via RPC ("${DiagnosticsExtension.getTitleMethod}")...',
);
try {
final Object? response = await connection.sendRequest(DiagnosticsExtension.getTitleMethod);
if (response is String) {
_titleCache = response;
} else {
_logger.printTrace('DiagnosticsExtensionClient received invalid title response: $response');
}
} on Object catch (err, stack) {
_logger.printTrace('DiagnosticsExtensionClient failed to fetch title: $err\n$stack');
}
return _titleCache ?? _defaultTitle;
}
@override
Future<List<ValidationResult>> runDiagnostics() async {
_logger.printTrace(
'DiagnosticsExtensionClient running diagnostics via RPC ("${DiagnosticsExtension.runDiagnosticsMethod}")...',
);
try {
final Object? response = await connection.sendRequest(
DiagnosticsExtension.runDiagnosticsMethod,
);
if (response is List<Object?>) {
final results = <ValidationResult>[];
for (final Object? element in response) {
if (element is Map) {
results.add(ValidationResult.fromJson(element.cast<String, Object?>()));
}
}
_logger.printTrace(
'DiagnosticsExtensionClient received ${results.length} result(s) via RPC.',
);
return results;
}
_logger.printTrace(
'DiagnosticsExtensionClient received invalid diagnostics response: $response',
);
} on Object catch (err, stack) {
_logger.printTrace('DiagnosticsExtensionClient failed to run diagnostics: $err\n$stack');
}
return const <ValidationResult>[];
}
}