|  | // Copyright 2020 The Fuchsia Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | // @dart = 2.8 | 
|  |  | 
|  | import 'dart:core'; | 
|  | import 'dart:core' as core; | 
|  | import 'dart:mirrors'; | 
|  |  | 
|  | import 'queries/index.dart'; | 
|  |  | 
|  | bool _parseBool(String val) { | 
|  | if (val == 'true') return true; | 
|  | if (val == 'false') return false; | 
|  | throw Exception('$val is not a valid value for boolean'); | 
|  | } | 
|  |  | 
|  | class ReflectQuery { | 
|  | static String nameType(Type type) { | 
|  | final classMirror = reflectClass(type); | 
|  | return Query.stripQuerySuffix(classMirror.simpleName.toString()); | 
|  | } | 
|  |  | 
|  | static Query instantiate(QueryFactory f, Map<String, String> arguments) { | 
|  | final classMirror = reflectClass(f.type); | 
|  | final constructors = List<MethodMirror>.from(classMirror.declarations.values | 
|  | .whereType<MethodMirror>() | 
|  | .where((decl) => decl.isConstructor)); | 
|  | if (arguments.isNotEmpty && constructors.isEmpty) { | 
|  | throw Exception( | 
|  | '$f has no constructors, but specified arguments $arguments'); | 
|  | } | 
|  | for (final constructor in constructors) { | 
|  | if (constructor is MethodMirror) { | 
|  | final List<ParameterMirror> parameters = constructor.parameters; | 
|  | if (parameters.any((param) => !param.isNamed)) { | 
|  | throw Exception('$constructor must only use named parameters'); | 
|  | } | 
|  | final parameterNames = | 
|  | parameters.where((param) => param.isNamed).map((e) => e.simpleName); | 
|  | if (arguments.keys.any((arg) => !parameterNames.contains(Symbol(arg)))) | 
|  | continue; | 
|  | final resolvedArgs = | 
|  | Map<Symbol, dynamic>.fromEntries(arguments.entries.map((entry) { | 
|  | final key = entry.key, value = entry.value; | 
|  | final param = | 
|  | parameters.firstWhere((param) => param.simpleName == Symbol(key)); | 
|  | final name = param.simpleName; | 
|  | switch (param.type.simpleName) { | 
|  | case #int: | 
|  | return MapEntry<Symbol, dynamic>(name, int.parse(value)); | 
|  | case #bool: | 
|  | return MapEntry<Symbol, dynamic>(name, _parseBool(value)); | 
|  | case #String: | 
|  | return MapEntry<Symbol, dynamic>(name, value); | 
|  | default: | 
|  | throw Exception('Unexpected type ${param.type.simpleName}'); | 
|  | } | 
|  | })); | 
|  | return classMirror | 
|  | .newInstance(core.Symbol.empty, [], resolvedArgs) | 
|  | .reflectee; | 
|  | } | 
|  | } | 
|  | final constructorDesc = constructors | 
|  | .map((c) => describeConstructor(c, f)) | 
|  | .map((s) => ' - tried $s') | 
|  | .join('\n'); | 
|  | throw Exception( | 
|  | 'Did not find suitable constructor for ${f.type} with $arguments.\n\n' | 
|  | '$constructorDesc\n'); | 
|  | } | 
|  |  | 
|  | /// Prints the method signature for `constructor`. | 
|  | static String describeConstructor(MethodMirror constructor, QueryFactory f) { | 
|  | final params = describeArgDeclarations(constructor); | 
|  | if (params.isEmpty) { | 
|  | return '${f.name}()'; | 
|  | } else { | 
|  | return '${f.name}({${params.join(", ")}})'; | 
|  | } | 
|  | } | 
|  |  | 
|  | static Iterable<String> describeArgDeclarations(MethodMirror constructor) => | 
|  | constructor.parameters.map((param) { | 
|  | final typeName = MirrorSystem.getName(param.type.simpleName); | 
|  | final paramName = MirrorSystem.getName(param.simpleName); | 
|  | var str = '$typeName $paramName'; | 
|  | if (param.hasDefaultValue) { | 
|  | str += ' = ${param.defaultValue.reflectee}'; | 
|  | } | 
|  | return str; | 
|  | }); | 
|  |  | 
|  | static Iterable<String> describeQueryConstructors(QueryFactory f) { | 
|  | final classMirror = reflectClass(f.type); | 
|  | final constructors = List<MethodMirror>.from(classMirror.declarations.values | 
|  | .whereType<MethodMirror>() | 
|  | .where((decl) => decl.isConstructor)); | 
|  | return constructors.map((c) => describeConstructor(c, f)); | 
|  | } | 
|  |  | 
|  | static bool hasCustomArguments(QueryFactory f) { | 
|  | final classMirror = reflectClass(f.type); | 
|  | final constructors = List<MethodMirror>.from(classMirror.declarations.values | 
|  | .whereType<MethodMirror>() | 
|  | .where((decl) => decl.isConstructor)); | 
|  | return constructors.any((c) => c.parameters.isNotEmpty); | 
|  | } | 
|  | } |