95b1b8d756
(Part of https://github.com/dart-lang/sdk/issues/63288) This change migrates the front_end package to use the new constructor declaration syntax, described in https://github.com/dart-lang/language/blob/main/accepted/future-releases/primary-constructors/feature-specification.md#abbreviations-of-in-body-constructor-declarations. This change was performed in an automated fashion, by (a) enabling the lints `unnecessary_type_name_in_constructor` and `unnecessary_const_in_enum_constructor`, (b) fixing the resulting lint failures using `dart fix`, and then (c) reformatting the affected files. To ease code review, I've reverted unrelated formatting changes. Change-Id: I6b48c0f1c762c3fa132fbd79382496ca6a6a6964 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/508368 Reviewed-by: Konstantin Shcheglov <scheglov@google.com> SLSA-Policy-Verified: SLSA Policy Verification Service <devtools-gerritcodereview-exitgate@google.com> Commit-Queue: Paul Berry <paulberry@google.com>
206 lines
5.7 KiB
Dart
206 lines
5.7 KiB
Dart
// Copyright (c) 2022, 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.
|
|
|
|
/// A value of type [T] read from a yaml/json-like structure.
|
|
abstract class Value<T> {
|
|
const new();
|
|
|
|
/// Reads the [T] value from [value].
|
|
///
|
|
/// If the [value] isn't a valid encoding of [T] an [ArgumentError] is thrown
|
|
/// using [context] to provide additional information in the error message.
|
|
T read(dynamic value, {String context = ''});
|
|
}
|
|
|
|
/// A `bool` value encoded as a bool.
|
|
class BoolValue extends Value<bool> {
|
|
const new();
|
|
|
|
@override
|
|
bool read(dynamic value, {String context = ''}) {
|
|
if (value is bool) {
|
|
return value;
|
|
} else {
|
|
throw new ArgumentError("${context}Value must be a boolean.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A `String` value encoded as a string.
|
|
///
|
|
/// If [options] is provided, only values in [options] are allowed.
|
|
class StringValue extends Value<String> {
|
|
final Set<String>? options;
|
|
|
|
const new({this.options});
|
|
|
|
@override
|
|
String read(dynamic value, {String context = ''}) {
|
|
if (value is String) {
|
|
if (options != null && !options!.contains(value)) {
|
|
throw new ArgumentError(
|
|
"${context}Unexpected value '${value}'. "
|
|
"Expected one of: ${options!.join(',')}.",
|
|
);
|
|
}
|
|
return value;
|
|
} else {
|
|
throw new ArgumentError("${context}Value must be a string.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An `int` value encoded as an integer.
|
|
class IntValue extends Value<int> {
|
|
const new();
|
|
|
|
@override
|
|
int read(dynamic value, {String context = ''}) {
|
|
if (value is int) {
|
|
return value;
|
|
} else {
|
|
throw new ArgumentError("${context}Value must be a integer.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A `List<E>` value encoded as a list or a single element, if
|
|
/// [supportSingleton] is `true`.
|
|
///
|
|
/// The individual elements of type [E] are read using the [elementReader].
|
|
class ListValue<E> extends Value<List<E>> {
|
|
final Value<E> elementReader;
|
|
final bool supportSingleton;
|
|
|
|
const new(this.elementReader, {this.supportSingleton = false});
|
|
|
|
@override
|
|
List<E> read(dynamic value, {String context = ''}) {
|
|
if (value is List) {
|
|
List<E> list = [];
|
|
for (dynamic element in value) {
|
|
list.add(elementReader.read(element));
|
|
}
|
|
return list;
|
|
} else if (supportSingleton) {
|
|
return [elementReader.read(value, context: context)];
|
|
} else {
|
|
throw new ArgumentError("${context}Value must be a list.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A `Map<String, V>` value encoded as a map.
|
|
///
|
|
/// The individual map entry values of type [V] are read using the
|
|
/// [valueReader].
|
|
class MapValue<V> extends Value<Map<String, V>> {
|
|
final Value<V> valueReader;
|
|
|
|
const new(this.valueReader);
|
|
|
|
@override
|
|
Map<String, V> read(dynamic value, {String context = ''}) {
|
|
if (value is Map) {
|
|
Map<String, V> map = {};
|
|
value.forEach((key, value) {
|
|
map[key as String] = valueReader.read(value, context: context);
|
|
});
|
|
return map;
|
|
} else {
|
|
throw new ArgumentError("${context}Value must be a map.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// An enum value of type [E] encoding as a string using the enum value name.
|
|
class EnumValue<E extends Enum> extends Value<E> {
|
|
final List<E> enumValues;
|
|
|
|
const new(this.enumValues);
|
|
|
|
@override
|
|
E read(dynamic value, {String context = ''}) {
|
|
if (value is String) {
|
|
for (E enumValue in enumValues) {
|
|
if (value == enumValue.name) {
|
|
return enumValue;
|
|
}
|
|
}
|
|
throw new ArgumentError(
|
|
"${context}Unexpected value: '${value}'. "
|
|
"Expected one of: ${enumValues.map((e) => e.name).join(',')}.",
|
|
);
|
|
} else {
|
|
throw new ArgumentError("${context}Value must be a string.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A value of type [O] encoded as a value of type [I], using [valueReader] to
|
|
/// decode the value.
|
|
class CustomValue<I, O> extends Value<O> {
|
|
final O Function(I) valueReader;
|
|
|
|
const new(this.valueReader);
|
|
|
|
@override
|
|
O read(dynamic value, {String context = ''}) {
|
|
if (value is I) {
|
|
return valueReader(value);
|
|
} else {
|
|
throw new ArgumentError("${context}Value must be of type $I.");
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A potentially optional named property of type [T].
|
|
///
|
|
/// This class can be used to document and support the decoding of properties
|
|
/// defined in a yaml/json-like map.
|
|
class Property<T> {
|
|
/// The name of the property as found in property map.
|
|
final String name;
|
|
|
|
/// The reader used to decode values of this property.
|
|
final Value<T> valueReader;
|
|
|
|
/// The default value for this property if omitted.
|
|
final T? defaultValue;
|
|
|
|
/// Whether this property is required.
|
|
final bool required;
|
|
|
|
/// Creates a required property with given [name] that uses [valueReader] to
|
|
/// decode its value.
|
|
const new required(this.name, this.valueReader)
|
|
: required = true,
|
|
defaultValue = null,
|
|
assert(null is! T);
|
|
|
|
/// Creates an optional property with given [name] that uses [valueReader] to
|
|
/// decode its value when present and [defaultValue] if omitted.
|
|
const new optional(this.name, this.valueReader, {this.defaultValue})
|
|
: required = false,
|
|
assert(defaultValue is T);
|
|
|
|
/// Reads this property from [map].
|
|
///
|
|
/// If [keys] is provided, [name] is removed from [keys] to signal that this
|
|
/// property has been recognized. This can be used to detect and report
|
|
/// unknown/unsupported properties.
|
|
T read(Map map, [Set<String>? keys]) {
|
|
dynamic value = map[name];
|
|
keys?.remove(name);
|
|
if (value == null) {
|
|
if (required) {
|
|
throw new ArgumentError("Missing property '$name'.");
|
|
}
|
|
return defaultValue as T;
|
|
} else {
|
|
return valueReader.read(value, context: "Property '$name': ");
|
|
}
|
|
}
|
|
}
|