// Copyright (c) 2025, 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. // This file is generated, do not edit. // File generated by pkgs/hooks/tool/generate_syntax.dart. // Must be rerun when pkgs/hooks/doc/schema/ is modified. // ignore_for_file: unused_element, public_member_api_docs import 'dart:io'; class AssetSyntax extends JsonObjectSyntax { factory AssetSyntax.fromJson( Map json, { List path = const [], }) { final result = AssetSyntax._fromJson(json, path: path); if (result.isHooksMetadataAsset) { return result.asHooksMetadataAsset; } return result; } AssetSyntax._fromJson(super.json, {super.path = const []}) : super.fromJson(); AssetSyntax({ required JsonObjectSyntax? encoding, required String type, super.path = const [], }) : super() { _encoding = encoding; _type = type; json.sortOnKey(); } JsonObjectSyntax? get encoding { final jsonValue = _reader.optionalMap('encoding'); if (jsonValue == null) return null; return JsonObjectSyntax.fromJson(jsonValue, path: [...path, 'encoding']); } set _encoding(JsonObjectSyntax? value) { json.setOrRemove('encoding', value?.json); } List _validateEncoding() { final mapErrors = _reader.validate?>('encoding'); if (mapErrors.isNotEmpty) { return mapErrors; } return encoding?.validate() ?? []; } String get type => _reader.get('type'); set _type(String value) { json.setOrRemove('type', value); } List _validateType() => _reader.validate('type'); @override List validate() => [ ...super.validate(), ..._validateEncoding(), ..._validateType(), ..._validateExtraRulesAsset(), ]; List _validateExtraRulesAsset() { final result = []; if (_reader.tryTraverse(['type']) == 'hooks/metadata') { final objectErrors = _reader.validate?>('encoding'); result.addAll(objectErrors); if (objectErrors.isEmpty) { final jsonValue = _reader.get?>('encoding'); if (jsonValue != null) { final reader = _JsonReader(jsonValue, [...path, 'encoding']); result.addAll(reader.validate('key')); } } } return result; } @override String toString() => 'AssetSyntax($json)'; } class BuildConfigSyntax extends ConfigSyntax { BuildConfigSyntax.fromJson(super.json, {super.path}) : super.fromJson(); BuildConfigSyntax({ required super.buildAssetTypes, required super.extensions, required bool linkingEnabled, super.path = const [], }) : super() { _linkingEnabled = linkingEnabled; json.sortOnKey(); } /// Setup all fields for [BuildConfigSyntax] that are not in /// [ConfigSyntax]. void setup({required bool linkingEnabled}) { _linkingEnabled = linkingEnabled; json.sortOnKey(); } bool get linkingEnabled => _reader.get('linking_enabled'); set _linkingEnabled(bool value) { json.setOrRemove('linking_enabled', value); } List _validateLinkingEnabled() => _reader.validate('linking_enabled'); @override List validate() => [ ...super.validate(), ..._validateLinkingEnabled(), ]; @override String toString() => 'BuildConfigSyntax($json)'; } class BuildInputSyntax extends HookInputSyntax { BuildInputSyntax.fromJson(super.json, {super.path}) : super.fromJson(); BuildInputSyntax({ required Map>? assets, required BuildConfigSyntax config, required super.outDirShared, required super.outFile, required super.packageName, required super.packageRoot, required super.userDefines, super.path = const [], }) : super(config: config) { _assets = assets; json.sortOnKey(); } /// Setup all fields for [BuildInputSyntax] that are not in /// [HookInputSyntax]. void setup({required Map>? assets}) { _assets = assets; json.sortOnKey(); } Map>? get assets { final jsonValue = _reader.optionalMap('assets'); if (jsonValue == null) { return null; } final result = >{}; for (final MapEntry(:key, :value) in jsonValue.entries) { result[key] = [ for (final (index, item) in (value as List).indexed) AssetSyntax.fromJson( item as Map, path: [...path, key, index], ), ]; } return result; } set _assets(Map>? value) { _checkArgumentMapKeys(value); if (value == null) { json.remove('assets'); } else { json['assets'] = { for (final MapEntry(:key, :value) in value.entries) key: [for (final item in value) item.json], }; } } List _validateAssets() { final mapErrors = _reader.validateOptionalMap('assets'); if (mapErrors.isNotEmpty) { return mapErrors; } final jsonValue = _reader.optionalMap('assets'); if (jsonValue == null) { return []; } final result = []; for (final list in assets!.values) { for (final element in list) { result.addAll(element.validate()); } } return result; } @override BuildConfigSyntax get config { final jsonValue = _reader.map$('config'); return BuildConfigSyntax.fromJson(jsonValue, path: [...path, 'config']); } @override List validate() => [ ...super.validate(), ..._validateAssets(), ..._validateConfig(), ]; @override String toString() => 'BuildInputSyntax($json)'; } class BuildOutputSyntax extends HookOutputSyntax { BuildOutputSyntax.fromJson(super.json, {super.path}) : super.fromJson(); BuildOutputSyntax({ required super.assets, required List? assetsForBuild, required super.assetsForLinking, required super.dependencies, required super.failureDetails, required super.status, required super.timestamp, super.path = const [], }) : super() { this.assetsForBuild = assetsForBuild; json.sortOnKey(); } /// Setup all fields for [BuildOutputSyntax] that are not in /// [HookOutputSyntax]. void setup({required List? assetsForBuild}) { this.assetsForBuild = assetsForBuild; json.sortOnKey(); } List? get assetsForBuild { final jsonValue = _reader.optionalList('assets_for_build'); if (jsonValue == null) return null; return [ for (final (index, element) in jsonValue.indexed) AssetSyntax.fromJson( element as Map, path: [...path, 'assets_for_build', index], ), ]; } set assetsForBuild(List? value) { if (value == null) { json.remove('assets_for_build'); } else { json['assets_for_build'] = [for (final item in value) item.json]; } json.sortOnKey(); } List _validateAssetsForBuild() { final listErrors = _reader.validateOptionalList>( 'assets_for_build', ); if (listErrors.isNotEmpty) { return listErrors; } final elements = assetsForBuild; if (elements == null) { return []; } return [for (final element in elements) ...element.validate()]; } @override List validate() => [ ...super.validate(), ..._validateAssetsForBuild(), ..._validateExtraRulesBuildOutput(), ]; List _validateExtraRulesBuildOutput() { final result = []; if (_reader.tryTraverse(['status']) == 'failure') { result.addAll(_reader.validate('failure_details')); } return result; } @override String toString() => 'BuildOutputSyntax($json)'; } class ConfigSyntax extends JsonObjectSyntax { ConfigSyntax.fromJson(super.json, {super.path = const []}) : super.fromJson(); ConfigSyntax({ required List buildAssetTypes, required JsonObjectSyntax? extensions, super.path = const [], }) : super() { this.buildAssetTypes = buildAssetTypes; this.extensions = extensions; json.sortOnKey(); } List get buildAssetTypes => _reader.stringList('build_asset_types'); set buildAssetTypes(List value) { json['build_asset_types'] = value; json.sortOnKey(); } List _validateBuildAssetTypes() => _reader.validateStringList('build_asset_types'); JsonObjectSyntax? get extensions { final jsonValue = _reader.optionalMap('extensions'); if (jsonValue == null) return null; return JsonObjectSyntax.fromJson(jsonValue, path: [...path, 'extensions']); } set extensions(JsonObjectSyntax? value) { json.setOrRemove('extensions', value?.json); json.sortOnKey(); } List _validateExtensions() { final mapErrors = _reader.validate?>('extensions'); if (mapErrors.isNotEmpty) { return mapErrors; } return extensions?.validate() ?? []; } @override List validate() => [ ...super.validate(), ..._validateBuildAssetTypes(), ..._validateExtensions(), ]; @override String toString() => 'ConfigSyntax($json)'; } class FailureSyntax extends JsonObjectSyntax { FailureSyntax.fromJson(super.json, {super.path = const []}) : super.fromJson(); FailureSyntax({required FailureTypeSyntax type, super.path = const []}) : super() { _type = type; json.sortOnKey(); } FailureTypeSyntax get type { final jsonValue = _reader.get('type'); return FailureTypeSyntax.fromJson(jsonValue); } set _type(FailureTypeSyntax value) { json['type'] = value.name; } List _validateType() => _reader.validate('type'); @override List validate() => [...super.validate(), ..._validateType()]; @override String toString() => 'FailureSyntax($json)'; } class FailureTypeSyntax { final String name; const FailureTypeSyntax._(this.name); static const build = FailureTypeSyntax._('build'); static const infra = FailureTypeSyntax._('infra'); static const uncategorized = FailureTypeSyntax._('uncategorized'); static const List values = [build, infra, uncategorized]; static final Map _byName = { for (final value in values) value.name: value, }; FailureTypeSyntax.unknown(this.name) : assert(!_byName.keys.contains(name)); factory FailureTypeSyntax.fromJson(String name) { final knownValue = _byName[name]; if (knownValue != null) { return knownValue; } return FailureTypeSyntax.unknown(name); } bool get isKnown => _byName[name] != null; @override String toString() => name; } class HookInputSyntax extends JsonObjectSyntax { HookInputSyntax.fromJson(super.json, {super.path = const []}) : super.fromJson(); HookInputSyntax({ required ConfigSyntax config, required Uri outDirShared, required Uri outFile, required String packageName, required Uri packageRoot, required UserDefinesSyntax? userDefines, super.path = const [], }) : super() { this.config = config; this.outDirShared = outDirShared; this.outFile = outFile; this.packageName = packageName; this.packageRoot = packageRoot; this.userDefines = userDefines; json.sortOnKey(); } ConfigSyntax get config { final jsonValue = _reader.map$('config'); return ConfigSyntax.fromJson(jsonValue, path: [...path, 'config']); } set config(ConfigSyntax value) { json['config'] = value.json; json.sortOnKey(); } List _validateConfig() { final mapErrors = _reader.validate>('config'); if (mapErrors.isNotEmpty) { return mapErrors; } return config.validate(); } Uri get outDirShared => _reader.path$('out_dir_shared'); set outDirShared(Uri value) { json['out_dir_shared'] = value.toFilePath(); json.sortOnKey(); } List _validateOutDirShared() => _reader.validatePath('out_dir_shared'); Uri get outFile => _reader.path$('out_file'); set outFile(Uri value) { json['out_file'] = value.toFilePath(); json.sortOnKey(); } List _validateOutFile() => _reader.validatePath('out_file'); String get packageName => _reader.get('package_name'); set packageName(String value) { json.setOrRemove('package_name', value); json.sortOnKey(); } List _validatePackageName() => _reader.validate('package_name'); Uri get packageRoot => _reader.path$('package_root'); set packageRoot(Uri value) { json['package_root'] = value.toFilePath(); json.sortOnKey(); } List _validatePackageRoot() => _reader.validatePath('package_root'); UserDefinesSyntax? get userDefines { final jsonValue = _reader.optionalMap('user_defines'); if (jsonValue == null) return null; return UserDefinesSyntax.fromJson( jsonValue, path: [...path, 'user_defines'], ); } set userDefines(UserDefinesSyntax? value) { json.setOrRemove('user_defines', value?.json); json.sortOnKey(); } List _validateUserDefines() { final mapErrors = _reader.validate?>('user_defines'); if (mapErrors.isNotEmpty) { return mapErrors; } return userDefines?.validate() ?? []; } @override List validate() => [ ...super.validate(), ..._validateConfig(), ..._validateOutDirShared(), ..._validateOutFile(), ..._validatePackageName(), ..._validatePackageRoot(), ..._validateUserDefines(), ]; @override String toString() => 'HookInputSyntax($json)'; } class HookOutputSyntax extends JsonObjectSyntax { HookOutputSyntax.fromJson(super.json, {super.path = const []}) : super.fromJson(); HookOutputSyntax({ required List? assets, required Map>? assetsForLinking, required List? dependencies, required FailureSyntax? failureDetails, required OutputStatusSyntax? status, required String timestamp, super.path = const [], }) : super() { this.assets = assets; this.assetsForLinking = assetsForLinking; this.dependencies = dependencies; this.failureDetails = failureDetails; this.status = status; this.timestamp = timestamp; json.sortOnKey(); } List? get assets { final jsonValue = _reader.optionalList('assets'); if (jsonValue == null) return null; return [ for (final (index, element) in jsonValue.indexed) AssetSyntax.fromJson( element as Map, path: [...path, 'assets', index], ), ]; } set assets(List? value) { if (value == null) { json.remove('assets'); } else { json['assets'] = [for (final item in value) item.json]; } json.sortOnKey(); } List _validateAssets() { final listErrors = _reader.validateOptionalList>( 'assets', ); if (listErrors.isNotEmpty) { return listErrors; } final elements = assets; if (elements == null) { return []; } return [for (final element in elements) ...element.validate()]; } Map>? get assetsForLinking { final jsonValue = _reader.optionalMap('assets_for_linking'); if (jsonValue == null) { return null; } final result = >{}; for (final MapEntry(:key, :value) in jsonValue.entries) { result[key] = [ for (final (index, item) in (value as List).indexed) AssetSyntax.fromJson( item as Map, path: [...path, key, index], ), ]; } return result; } set assetsForLinking(Map>? value) { _checkArgumentMapKeys(value); if (value == null) { json.remove('assets_for_linking'); } else { json['assets_for_linking'] = { for (final MapEntry(:key, :value) in value.entries) key: [for (final item in value) item.json], }; } json.sortOnKey(); } List _validateAssetsForLinking() { final mapErrors = _reader.validateOptionalMap('assets_for_linking'); if (mapErrors.isNotEmpty) { return mapErrors; } final jsonValue = _reader.optionalMap('assets_for_linking'); if (jsonValue == null) { return []; } final result = []; for (final list in assetsForLinking!.values) { for (final element in list) { result.addAll(element.validate()); } } return result; } List? get dependencies => _reader.optionalPathList('dependencies'); set dependencies(List? value) { json.setOrRemove('dependencies', value?.toJson()); json.sortOnKey(); } List _validateDependencies() => _reader.validateOptionalPathList('dependencies'); FailureSyntax? get failureDetails { final jsonValue = _reader.optionalMap('failure_details'); if (jsonValue == null) return null; return FailureSyntax.fromJson( jsonValue, path: [...path, 'failure_details'], ); } set failureDetails(FailureSyntax? value) { json.setOrRemove('failure_details', value?.json); json.sortOnKey(); } List _validateFailureDetails() { final mapErrors = _reader.validate?>( 'failure_details', ); if (mapErrors.isNotEmpty) { return mapErrors; } return failureDetails?.validate() ?? []; } OutputStatusSyntax? get status { final jsonValue = _reader.get('status'); if (jsonValue == null) return null; return OutputStatusSyntax.fromJson(jsonValue); } set status(OutputStatusSyntax? value) { json.setOrRemove('status', value?.name); json.sortOnKey(); } List _validateStatus() => _reader.validate('status'); String get timestamp => _reader.get('timestamp'); set timestamp(String value) { json.setOrRemove('timestamp', value); json.sortOnKey(); } List _validateTimestamp() => _reader.validate('timestamp'); @override List validate() => [ ...super.validate(), ..._validateAssets(), ..._validateAssetsForLinking(), ..._validateDependencies(), ..._validateFailureDetails(), ..._validateStatus(), ..._validateTimestamp(), ..._validateExtraRulesHookOutput(), ]; List _validateExtraRulesHookOutput() { final result = []; if (_reader.tryTraverse(['status']) == 'failure') { result.addAll(_reader.validate('failure_details')); } return result; } @override String toString() => 'HookOutputSyntax($json)'; } class HooksMetadataAssetSyntax extends AssetSyntax { static const typeValue = 'hooks/metadata'; HooksMetadataAssetSyntax.fromJson(super.json, {super.path}) : super._fromJson(); HooksMetadataAssetSyntax({ required MetadataAssetEncodingSyntax encoding, super.path = const [], }) : super(type: 'hooks/metadata', encoding: encoding); /// Setup all fields for [HooksMetadataAssetSyntax] that are not in /// [AssetSyntax]. void setup() {} @override MetadataAssetEncodingSyntax get encoding { final jsonValue = _reader.map$('encoding'); return MetadataAssetEncodingSyntax.fromJson( jsonValue, path: [...path, 'encoding'], ); } @override List validate() => [...super.validate(), ..._validateEncoding()]; @override String toString() => 'HooksMetadataAssetSyntax($json)'; } extension HooksMetadataAssetSyntaxExtension on AssetSyntax { bool get isHooksMetadataAsset => type == 'hooks/metadata'; HooksMetadataAssetSyntax get asHooksMetadataAsset => HooksMetadataAssetSyntax.fromJson(json, path: path); } class LinkInputSyntax extends HookInputSyntax { LinkInputSyntax.fromJson(super.json, {super.path}) : super.fromJson(); LinkInputSyntax({ required List? assets, required List? assetsFromLinking, required super.config, required super.outDirShared, required super.outFile, required super.packageName, required super.packageRoot, required Uri? resourceIdentifiers, required super.userDefines, super.path = const [], }) : super() { _assets = assets; _assetsFromLinking = assetsFromLinking; _resourceIdentifiers = resourceIdentifiers; json.sortOnKey(); } /// Setup all fields for [LinkInputSyntax] that are not in /// [HookInputSyntax]. void setup({ required List? assets, required List? assetsFromLinking, required Uri? resourceIdentifiers, }) { _assets = assets; _assetsFromLinking = assetsFromLinking; _resourceIdentifiers = resourceIdentifiers; json.sortOnKey(); } List? get assets { final jsonValue = _reader.optionalList('assets'); if (jsonValue == null) return null; return [ for (final (index, element) in jsonValue.indexed) AssetSyntax.fromJson( element as Map, path: [...path, 'assets', index], ), ]; } set _assets(List? value) { if (value == null) { json.remove('assets'); } else { json['assets'] = [for (final item in value) item.json]; } } List _validateAssets() { final listErrors = _reader.validateOptionalList>( 'assets', ); if (listErrors.isNotEmpty) { return listErrors; } final elements = assets; if (elements == null) { return []; } return [for (final element in elements) ...element.validate()]; } List? get assetsFromLinking { final jsonValue = _reader.optionalList('assets_from_linking'); if (jsonValue == null) return null; return [ for (final (index, element) in jsonValue.indexed) AssetSyntax.fromJson( element as Map, path: [...path, 'assets_from_linking', index], ), ]; } set _assetsFromLinking(List? value) { if (value == null) { json.remove('assets_from_linking'); } else { json['assets_from_linking'] = [for (final item in value) item.json]; } } List _validateAssetsFromLinking() { final listErrors = _reader.validateOptionalList>( 'assets_from_linking', ); if (listErrors.isNotEmpty) { return listErrors; } final elements = assetsFromLinking; if (elements == null) { return []; } return [for (final element in elements) ...element.validate()]; } Uri? get resourceIdentifiers => _reader.optionalPath('resource_identifiers'); set _resourceIdentifiers(Uri? value) { json.setOrRemove('resource_identifiers', value?.toFilePath()); } List _validateResourceIdentifiers() => _reader.validateOptionalPath('resource_identifiers'); @override List validate() => [ ...super.validate(), ..._validateAssets(), ..._validateAssetsFromLinking(), ..._validateResourceIdentifiers(), ]; @override String toString() => 'LinkInputSyntax($json)'; } class LinkOutputSyntax extends HookOutputSyntax { LinkOutputSyntax.fromJson(super.json, {super.path}) : super.fromJson(); LinkOutputSyntax({ required super.assets, required super.assetsForLinking, required super.dependencies, required super.failureDetails, required super.status, required super.timestamp, super.path = const [], }) : super(); @override List validate() => [ ...super.validate(), ..._validateExtraRulesLinkOutput(), ]; List _validateExtraRulesLinkOutput() { final result = []; if (_reader.tryTraverse(['status']) == 'failure') { result.addAll(_reader.validate('failure_details')); } return result; } @override String toString() => 'LinkOutputSyntax($json)'; } class MetadataAssetEncodingSyntax extends JsonObjectSyntax { MetadataAssetEncodingSyntax.fromJson(super.json, {super.path = const []}) : super.fromJson(); MetadataAssetEncodingSyntax({ required String key, required Object? value, super.path = const [], }) : super() { _key = key; _value = value; json.sortOnKey(); } String get key => _reader.get('key'); set _key(String value) { json.setOrRemove('key', value); } List _validateKey() => _reader.validate('key'); Object? get value => _reader.get('value'); set _value(Object? value) { json.setOrRemove('value', value); } List _validateValue() => _reader.validate('value'); @override List validate() => [ ...super.validate(), ..._validateKey(), ..._validateValue(), ]; @override String toString() => 'MetadataAssetEncodingSyntax($json)'; } class OutputStatusSyntax { final String name; const OutputStatusSyntax._(this.name); static const failure = OutputStatusSyntax._('failure'); static const success = OutputStatusSyntax._('success'); static const List values = [failure, success]; static final Map _byName = { for (final value in values) value.name: value, }; OutputStatusSyntax.unknown(this.name) : assert(!_byName.keys.contains(name)); factory OutputStatusSyntax.fromJson(String name) { final knownValue = _byName[name]; if (knownValue != null) { return knownValue; } return OutputStatusSyntax.unknown(name); } bool get isKnown => _byName[name] != null; @override String toString() => name; } class UserDefinesSyntax extends JsonObjectSyntax { UserDefinesSyntax.fromJson(super.json, {super.path = const []}) : super.fromJson(); UserDefinesSyntax({ required UserDefinesSourceSyntax? workspacePubspec, super.path = const [], }) : super() { _workspacePubspec = workspacePubspec; json.sortOnKey(); } UserDefinesSourceSyntax? get workspacePubspec { final jsonValue = _reader.optionalMap('workspace_pubspec'); if (jsonValue == null) return null; return UserDefinesSourceSyntax.fromJson( jsonValue, path: [...path, 'workspace_pubspec'], ); } set _workspacePubspec(UserDefinesSourceSyntax? value) { json.setOrRemove('workspace_pubspec', value?.json); } List _validateWorkspacePubspec() { final mapErrors = _reader.validate?>( 'workspace_pubspec', ); if (mapErrors.isNotEmpty) { return mapErrors; } return workspacePubspec?.validate() ?? []; } @override List validate() => [ ...super.validate(), ..._validateWorkspacePubspec(), ]; @override String toString() => 'UserDefinesSyntax($json)'; } class UserDefinesSourceSyntax extends JsonObjectSyntax { UserDefinesSourceSyntax.fromJson(super.json, {super.path = const []}) : super.fromJson(); UserDefinesSourceSyntax({ required Uri basePath, required JsonObjectSyntax defines, super.path = const [], }) : super() { _basePath = basePath; _defines = defines; json.sortOnKey(); } Uri get basePath => _reader.path$('base_path'); set _basePath(Uri value) { json['base_path'] = value.toFilePath(); } List _validateBasePath() => _reader.validatePath('base_path'); JsonObjectSyntax get defines { final jsonValue = _reader.map$('defines'); return JsonObjectSyntax.fromJson(jsonValue, path: [...path, 'defines']); } set _defines(JsonObjectSyntax value) { json['defines'] = value.json; } List _validateDefines() { final mapErrors = _reader.validate>('defines'); if (mapErrors.isNotEmpty) { return mapErrors; } return defines.validate(); } @override List validate() => [ ...super.validate(), ..._validateBasePath(), ..._validateDefines(), ]; @override String toString() => 'UserDefinesSourceSyntax($json)'; } class JsonObjectSyntax { final Map json; final List path; _JsonReader get _reader => _JsonReader(json, path); JsonObjectSyntax({this.path = const []}) : json = {}; JsonObjectSyntax.fromJson(this.json, {this.path = const []}); List validate() => []; } class _JsonReader { /// The JSON Object this reader is reading. final Map json; /// The path traversed by readers of the surrounding JSON. /// /// Contains [String] property keys and [int] indices. /// /// This is used to give more precise error messages. final List path; _JsonReader(this.json, this.path); T get(String key) { final value = json[key]; if (value is T) return value; throwFormatException(value, T, [key]); } List validate(String key) { final value = json[key]; if (value is T) return []; return [ errorString(value, T, [key]), ]; } List list(String key) => _castList(get>(key), key); List validateList(String key) { final listErrors = validate>(key); if (listErrors.isNotEmpty) { return listErrors; } return _validateListElements(get>(key), key); } List? optionalList(String key) => switch (get?>(key)?.cast()) { null => null, final l => _castList(l, key), }; List validateOptionalList(String key) { final listErrors = validate?>(key); if (listErrors.isNotEmpty) { return listErrors; } final list = get?>(key); if (list == null) { return []; } return _validateListElements(list, key); } /// [List.cast] but with [FormatException]s. List _castList(List list, String key) { for (final (index, value) in list.indexed) { if (value is! T) { throwFormatException(value, T, [key, index]); } } return list.cast(); } List _validateListElements( List list, String key, ) { final result = []; for (final (index, value) in list.indexed) { if (value is! T) { result.add(errorString(value, T, [key, index])); } } return result; } Map map$(String key, {RegExp? keyPattern}) { final map = get>(key); final keyErrors = _validateMapKeys(map, key, keyPattern: keyPattern); if (keyErrors.isNotEmpty) { throw FormatException(keyErrors.join('\n')); } return _castMap(map, key); } List validateMap( String key, { RegExp? keyPattern, }) { final mapErrors = validate>(key); if (mapErrors.isNotEmpty) { return mapErrors; } final map = get>(key); return [ ..._validateMapKeys(map, key, keyPattern: keyPattern), ..._validateMapElements(map, key), ]; } Map? optionalMap( String key, { RegExp? keyPattern, }) { final map = get?>(key); if (map == null) return null; final keyErrors = _validateMapKeys(map, key, keyPattern: keyPattern); if (keyErrors.isNotEmpty) { throw FormatException(keyErrors.join('\n')); } return _castMap(map, key); } List validateOptionalMap( String key, { RegExp? keyPattern, }) { final mapErrors = validate?>(key); if (mapErrors.isNotEmpty) { return mapErrors; } final map = get?>(key); if (map == null) { return []; } return [ ..._validateMapKeys(map, key, keyPattern: keyPattern), ..._validateMapElements(map, key), ]; } /// [Map.cast] but with [FormatException]s. Map _castMap( Map map_, String parentKey, ) { for (final MapEntry(:key, :value) in map_.entries) { if (value is! T) { throwFormatException(value, T, [parentKey, key]); } } return map_.cast(); } List _validateMapKeys( Map map_, String parentKey, { required RegExp? keyPattern, }) { if (keyPattern == null) return []; final result = []; for (final key in map_.keys) { if (!keyPattern.hasMatch(key)) { result.add( keyErrorString(key, pattern: keyPattern, pathExtension: [parentKey]), ); } } return result; } List _validateMapElements( Map map_, String parentKey, ) { final result = []; for (final MapEntry(:key, :value) in map_.entries) { if (value is! T) { result.add(errorString(value, T, [parentKey, key])); } } return result; } List validateMapStringElements( Map map_, String parentKey, { RegExp? valuePattern, }) { final result = []; for (final MapEntry(:key, :value) in map_.entries) { if (value != null && valuePattern != null && !valuePattern.hasMatch(value)) { result.add( errorString(value, T, [parentKey, key], pattern: valuePattern), ); } } return result; } String string(String key, RegExp? pattern) { final value = get(key); if (pattern != null && !pattern.hasMatch(value)) { throwFormatException(value, String, [key], pattern: pattern); } return value; } String? optionalString(String key, RegExp? pattern) { final value = get(key); if (value == null) return null; if (pattern != null && !pattern.hasMatch(value)) { throwFormatException(value, String, [key], pattern: pattern); } return value; } List validateString(String key, RegExp? pattern) { final errors = validate(key); if (errors.isNotEmpty) { return errors; } final value = get(key); if (pattern != null && !pattern.hasMatch(value)) { return [ errorString(value, String, [key], pattern: pattern), ]; } return []; } List validateOptionalString(String key, RegExp? pattern) { final errors = validate(key); if (errors.isNotEmpty) { return errors; } final value = get(key); if (value == null) return []; if (pattern != null && !pattern.hasMatch(value)) { return [ errorString(value, String, [key], pattern: pattern), ]; } return []; } List? optionalStringList(String key) => optionalList(key); List validateOptionalStringList(String key) => validateOptionalList(key); List stringList(String key) => list(key); List validateStringList(String key) => validateList(key); Uri path$(String key) => _fileSystemPathToUri(get(key)); List validatePath(String key) => validate(key); Uri? optionalPath(String key) { final value = get(key); if (value == null) return null; return _fileSystemPathToUri(value); } List validateOptionalPath(String key) => validate(key); List? optionalPathList(String key) { final strings = optionalStringList(key); if (strings == null) { return null; } return [for (final string in strings) _fileSystemPathToUri(string)]; } List validateOptionalPathList(String key) => validateOptionalStringList(key); static Uri _fileSystemPathToUri(String path) { if (path.endsWith(Platform.pathSeparator)) { return Uri.directory(path); } return Uri.file(path); } String _jsonPathToString(List pathEnding) => [...path, ...pathEnding].join('.'); Never throwFormatException( Object? value, Type expectedType, List pathExtension, { RegExp? pattern, }) { throw FormatException( errorString(value, expectedType, pathExtension, pattern: pattern), ); } String errorString( Object? value, Type expectedType, List pathExtension, { RegExp? pattern, }) { final pathString = _jsonPathToString(pathExtension); if (value == null) { return "No value was provided for '$pathString'." ' Expected a $expectedType.'; } final satisfying = pattern == null ? '' : ' satisfying ${pattern.pattern}'; return "Unexpected value '$value' (${value.runtimeType}) for '$pathString'." ' Expected a $expectedType$satisfying.'; } String keyErrorString( String key, { required RegExp pattern, List pathExtension = const [], }) { final pathString = _jsonPathToString(pathExtension); return "Unexpected key '$key' in '$pathString'." ' Expected a key satisfying ${pattern.pattern}.'; } /// Traverses a JSON path, returns `null` if the path cannot be traversed. Object? tryTraverse(List path) { Object? json = this.json; for (final key in path) { if (json is! Map) { return null; } json = json[key]; } return json; } } extension on Map { void setOrRemove(String key, Object? value) { if (value == null) { remove(key); } else { this[key] = value; } } } extension on List { List toJson() => [for (final uri in this) uri.toFilePath()]; } extension, V extends Object?> on Map { void sortOnKey() { final result = {}; final keysSorted = keys.toList()..sort(); for (final key in keysSorted) { result[key] = this[key] as V; } clear(); addAll(result); } } void _checkArgumentMapKeys(Map? map, {RegExp? keyPattern}) { if (map == null) return; if (keyPattern == null) return; for (final key in map.keys) { if (!keyPattern.hasMatch(key)) { throw ArgumentError.value( map, "Unexpected key '$key'." ' Expected a key satisfying ${keyPattern.pattern}.', ); } } } void _checkArgumentMapStringElements( Map? map, { RegExp? valuePattern, }) { if (map == null) return; if (valuePattern == null) return; for (final entry in map.entries) { final value = entry.value; if (value != null && !valuePattern.hasMatch(value)) { throw ArgumentError.value( map, "Unexpected value '$value' under key '${entry.key}'." ' Expected a value satisfying ${valuePattern.pattern}.', ); } } }