Skip to content

Commit 2952f6b

Browse files
authored
Simplify ToolConfiguration and other options code (#3414)
1 parent 0cf21c1 commit 2952f6b

File tree

1 file changed

+108
-122
lines changed

1 file changed

+108
-122
lines changed

lib/src/dartdoc_options.dart

Lines changed: 108 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -134,82 +134,66 @@ class ToolConfiguration {
134134
for (var entry in yamlMap.entries) {
135135
var name = entry.key.toString();
136136
var toolMap = entry.value;
137-
String? description;
138-
List<String>? compileArgs;
139-
List<String>? command;
140-
List<String>? setupCommand;
141-
if (toolMap is Map) {
142-
description = toolMap['description'].toString();
143-
List<String>? findCommand([String prefix = '']) {
144-
List<String>? command;
145-
// If the command key is given, then it applies to all platforms.
146-
var commandFromKey = toolMap.containsKey('${prefix}command')
147-
? '${prefix}command'
148-
: '$prefix${Platform.operatingSystem}';
149-
if (toolMap.containsKey(commandFromKey)) {
150-
var commandFrom = toolMap[commandFromKey] as YamlNode;
151-
if (commandFrom.value is String) {
152-
command = [commandFrom.toString()];
153-
if (command[0].isEmpty) {
154-
throw DartdocOptionError(
155-
'Tool commands must not be empty. Tool $name command entry '
156-
'"$commandFromKey" must contain at least one path.');
157-
}
158-
} else if (commandFrom is YamlList) {
159-
command =
160-
commandFrom.map<String>((node) => node.toString()).toList();
161-
if (command.isEmpty) {
162-
throw DartdocOptionError(
163-
'Tool commands must not be empty. Tool $name command entry '
164-
'"$commandFromKey" must contain at least one path.');
165-
}
166-
} else {
167-
throw DartdocOptionError(
168-
'Tool commands must be a path to an executable, or a list of '
169-
'strings that starts with a path to an executable. '
170-
'The tool $name has a $commandFromKey entry that is a '
171-
'${commandFrom.runtimeType}');
172-
}
173-
}
174-
return command;
175-
}
176-
177-
command = findCommand();
178-
setupCommand = findCommand('setup_');
179-
180-
List<String>? findArgs() {
181-
List<String>? args;
182-
if (toolMap.containsKey(compileArgsTagName)) {
183-
var compileArgs = toolMap[compileArgsTagName];
184-
if (compileArgs is String) {
185-
args = [toolMap[compileArgsTagName].toString()];
186-
} else if (compileArgs is YamlList) {
187-
args = compileArgs
188-
.map<String>((node) => node.toString())
189-
.toList(growable: false);
190-
} else {
191-
throw DartdocOptionError(
192-
'Tool compile arguments must be a list of strings. The tool '
193-
'$name has a $compileArgsTagName entry that is a '
194-
'${compileArgs.runtimeType}');
195-
}
196-
}
197-
return args;
198-
}
199-
200-
compileArgs = findArgs();
201-
} else {
137+
if (toolMap is! Map) {
202138
throw DartdocOptionError(
203139
'Tools must be defined as a map of tool names to definitions. Tool '
204140
'$name is not a map.');
205141
}
142+
143+
var description = toolMap['description'].toString();
144+
145+
List<String>? findCommand([String prefix = '']) {
146+
// If the command key is given, then it applies to all platforms.
147+
var commandFromKey = toolMap.containsKey('${prefix}command')
148+
? '${prefix}command'
149+
: '$prefix${Platform.operatingSystem}';
150+
if (!toolMap.containsKey(commandFromKey)) {
151+
return null;
152+
}
153+
var commandFrom = toolMap[commandFromKey] as YamlNode;
154+
List<String> command;
155+
if (commandFrom.value is String) {
156+
command = [commandFrom.toString()];
157+
} else if (commandFrom is YamlList) {
158+
command = commandFrom.map((node) => node.toString()).toList();
159+
} else {
160+
throw DartdocOptionError(
161+
'Tool commands must be a path to an executable, or a list of '
162+
'strings that starts with a path to an executable. The tool '
163+
"'$name' has a '$commandFromKey' entry that is a "
164+
'${commandFrom.runtimeType}');
165+
}
166+
if (command.isEmpty || command[0].isEmpty) {
167+
throw DartdocOptionError(
168+
'Tool commands must not be empty. Tool $name command entry '
169+
'"$commandFromKey" must contain at least one path.');
170+
}
171+
return command;
172+
}
173+
174+
var command = findCommand();
206175
if (command == null) {
207176
throw DartdocOptionError(
208177
'At least one of "command" or "${Platform.operatingSystem}" must '
209178
'be defined for the tool $name.');
210179
}
211-
212-
bool validateExecutable(String executable) {
180+
var setupCommand = findCommand('setup_');
181+
182+
var rawCompileArgs = toolMap[compileArgsTagName];
183+
var compileArgs = switch (rawCompileArgs) {
184+
null => const <String>[],
185+
String() => [toolMap[compileArgsTagName].toString()],
186+
YamlList() =>
187+
rawCompileArgs.map((node) => node.toString()).toList(growable: false),
188+
_ => throw DartdocOptionError(
189+
'Tool compile arguments must be a list of strings. The tool '
190+
"'$name' has a '$compileArgsTagName' entry that is a "
191+
'${rawCompileArgs.runtimeType}',
192+
),
193+
};
194+
195+
/// Validates [executable] and returns whether it is a Dart script.
196+
bool isDartScript(String executable) {
213197
var executableFile = resourceProvider.getFile(executable);
214198
if (resourceProvider.isNotFound(executableFile)) {
215199
throw DartdocOptionError('Command executables must exist. '
@@ -230,24 +214,24 @@ class ToolConfiguration {
230214
var executableRelativePath = command.removeAt(0);
231215
var executable = pathContext.canonicalize(
232216
pathContext.join(canonicalYamlPath, executableRelativePath));
233-
validateExecutable(executable);
217+
var isDartSetupScript = isDartScript(executable);
234218
if (setupCommand != null) {
235219
var setupExecutableRelativePath = setupCommand.removeAt(0);
236220
var setupExecutable = pathContext.canonicalize(
237221
pathContext.join(canonicalYamlPath, setupExecutableRelativePath));
238-
var isDartSetupCommand = validateExecutable(executable);
239222
// Setup commands aren't snapshotted, since they're only run once.
240-
setupCommand = (isDartSetupCommand
241-
? [Platform.resolvedExecutable, setupExecutable]
242-
: [setupExecutable]) +
243-
setupCommand;
223+
setupCommand = [
224+
if (isDartSetupScript) Platform.resolvedExecutable,
225+
setupExecutable,
226+
...setupCommand,
227+
];
244228
}
245229
newToolDefinitions[name] = ToolDefinition.fromCommand(
246-
[executable] + command,
230+
[executable, ...command],
247231
setupCommand ?? const [],
248232
description,
249233
resourceProvider,
250-
compileArgs: compileArgs ?? const []);
234+
compileArgs: compileArgs);
251235
}
252236
return ToolConfiguration._(newToolDefinitions, resourceProvider);
253237
}
@@ -267,12 +251,19 @@ class _YamlFileData {
267251
/// An enum to specify the multiple different kinds of data an option might
268252
/// represent.
269253
enum OptionKind {
270-
other, // Make no assumptions about the option data; it may be of any type
271-
// or semantic.
272-
file, // Option data references a filename or filenames with strings.
273-
dir, // Option data references a directory name or names with strings.
274-
glob, // Option data references globs with strings that may cover many
275-
// filenames and/or directories.
254+
/// Make no assumptions about the option data; it may be of any type or
255+
/// semantic.
256+
other,
257+
258+
/// Option data references a filename or filenames with strings.
259+
file,
260+
261+
/// Option data references a directory name or names with strings.
262+
dir,
263+
264+
/// Option data references globs with strings that may cover many filenames
265+
/// and/or directories.
266+
glob,
276267
}
277268

278269
/// Some DartdocOption subclasses need to keep track of where they
@@ -289,7 +280,7 @@ class _OptionValueWithContext<T> {
289280
/// If non-null, the basename of the configuration file the value came from.
290281
String? definingFile;
291282

292-
/// A [pathLib.Context] variable initialized with canonicalDirectoryPath.
283+
/// A [pathLib.Context] variable initialized with 'canonicalDirectoryPath'.
293284
p.Context pathContext;
294285

295286
/// Build a _OptionValueWithContext.
@@ -507,7 +498,6 @@ abstract class DartdocOption<T extends Object?> {
507498
dynamic valueAt(Folder dir);
508499

509500
/// Calls [valueAt] with the working directory at the start of the program.
510-
// TODO(jcollins-g): use of dynamic. https://github.com/dart-lang/dartdoc/issues/2814
511501
Object? valueAtCurrent() => valueAt(_directoryCurrent);
512502

513503
late final Folder _directoryCurrent =
@@ -539,7 +529,7 @@ abstract class DartdocOption<T extends Object?> {
539529
}
540530

541531
/// Get the immediate child of this node named [name] and its value at [dir].
542-
U getValueAs<U extends Object?>(String name, Folder dir) =>
532+
U getValueAs<U>(String name, Folder dir) =>
543533
_children[name]?.valueAt(dir) as U;
544534

545535
/// Apply the function [visit] to [this] and all children.
@@ -556,19 +546,16 @@ abstract class DartdocOption<T extends Object?> {
556546
class DartdocOptionFileSynth<T> extends DartdocOption<T>
557547
with DartdocSyntheticOption<T>, _DartdocFileOption<T> {
558548
@override
559-
final bool parentDirOverridesChild;
560-
@override
561549
final T Function(DartdocSyntheticOption<T>, Folder) _compute;
562550

563551
DartdocOptionFileSynth(
564552
String name, this._compute, ResourceProvider resourceProvider,
565-
{bool mustExist = false,
566-
String help = '',
567-
OptionKind optionIs = OptionKind.other,
568-
this.parentDirOverridesChild = false,
569-
ConvertYamlToType<T>? convertYamlToType})
570-
: super(name, null, help, optionIs, mustExist, convertYamlToType,
571-
resourceProvider);
553+
{String help = ''})
554+
: super(
555+
name, null, help, OptionKind.other, false, null, resourceProvider);
556+
557+
@override
558+
bool get parentDirOverridesChild => false;
572559

573560
@override
574561
T? valueAt(Folder dir) {
@@ -580,7 +567,7 @@ class DartdocOptionFileSynth<T> extends DartdocOption<T>
580567
}
581568

582569
@override
583-
void _onMissing(
570+
Never _onMissing(
584571
_OptionValueWithContext<T> valueWithContext, String missingPath) {
585572
if (valueWithContext.definingFile != null) {
586573
_onMissingFromFiles(valueWithContext, missingPath);
@@ -597,11 +584,7 @@ class DartdocOptionArgSynth<T> extends DartdocOption<T>
597584
@override
598585
final String? abbr;
599586
@override
600-
final bool hide;
601-
@override
602587
final bool negatable;
603-
@override
604-
final bool splitCommas;
605588

606589
@override
607590
final T Function(DartdocSyntheticOption<T>, Folder) _compute;
@@ -611,15 +594,19 @@ class DartdocOptionArgSynth<T> extends DartdocOption<T>
611594
{this.abbr,
612595
bool mustExist = false,
613596
String help = '',
614-
this.hide = false,
615597
OptionKind optionIs = OptionKind.other,
616-
this.negatable = false,
617-
this.splitCommas = false})
618-
: assert(T is! Iterable || splitCommas == true),
598+
this.negatable = false})
599+
: assert(T is! Iterable),
619600
super(name, null, help, optionIs, mustExist, null, resourceProvider);
620601

621602
@override
622-
void _onMissing(
603+
bool get hide => false;
604+
605+
@override
606+
bool get splitCommas => false;
607+
608+
@override
609+
Never _onMissing(
623610
_OptionValueWithContext<T> valueWithContext, String missingPath) {
624611
_onMissingFromArgs(valueWithContext, missingPath);
625612
}
@@ -663,11 +650,11 @@ mixin DartdocSyntheticOption<T> implements DartdocOption<T> {
663650
}
664651

665652
@override
666-
void _onMissing(
653+
Never _onMissing(
667654
_OptionValueWithContext<T> valueWithContext, String missingPath) =>
668655
_onMissingFromSynthetic(valueWithContext, missingPath);
669656

670-
void _onMissingFromSynthetic(
657+
Never _onMissingFromSynthetic(
671658
_OptionValueWithContext<T> valueWithContext, String missingPath) {
672659
var description = 'Synthetic configuration option $name from <internal>';
673660
throw DartdocFileMissing(
@@ -784,31 +771,31 @@ class DartdocOptionArgOnly<T> extends DartdocOption<T>
784771
class DartdocOptionArgFile<T> extends DartdocOption<T>
785772
with _DartdocArgOption<T>, _DartdocFileOption<T> {
786773
@override
787-
final String? abbr;
788-
@override
789-
final bool hide;
790-
@override
791774
final bool negatable;
792775
@override
793-
final bool parentDirOverridesChild;
794-
@override
795776
final bool splitCommas;
796777

797778
DartdocOptionArgFile(
798779
String name, T defaultsTo, ResourceProvider resourceProvider,
799-
{this.abbr,
800-
bool mustExist = false,
780+
{bool mustExist = false,
801781
String help = '',
802-
this.hide = false,
803782
OptionKind optionIs = OptionKind.other,
804783
this.negatable = false,
805-
this.parentDirOverridesChild = false,
806784
this.splitCommas = false})
807785
: super(name, defaultsTo, help, optionIs, mustExist, null,
808786
resourceProvider);
809787

810788
@override
811-
void _onMissing(
789+
String? get abbr => null;
790+
791+
@override
792+
bool get hide => false;
793+
794+
@override
795+
bool get parentDirOverridesChild => false;
796+
797+
@override
798+
Never _onMissing(
812799
_OptionValueWithContext<T> valueWithContext, String missingPath) {
813800
if (valueWithContext.definingFile != null) {
814801
_onMissingFromFiles(valueWithContext, missingPath);
@@ -858,22 +845,21 @@ mixin _DartdocFileOption<T> implements DartdocOption<T> {
858845
String get fieldName => keys.join('.');
859846

860847
@override
861-
void _onMissing(
848+
Never _onMissing(
862849
_OptionValueWithContext<T> valueWithContext, String missingPath) =>
863850
_onMissingFromFiles(valueWithContext, missingPath);
864851

865-
void _onMissingFromFiles(
852+
Never _onMissingFromFiles(
866853
_OptionValueWithContext<T> valueWithContext, String missingPath) {
867854
var dartdocYaml = resourceProvider.pathContext.join(
868855
valueWithContext.canonicalDirectoryPath, valueWithContext.definingFile);
869856
throw DartdocFileMissing('Field $fieldName from $dartdocYaml, set to '
870857
'${valueWithContext.value}, resolves to missing path: "$missingPath"');
871858
}
872859

873-
@override
874-
875860
/// Searches for a value in configuration files relative to [dir], and if not
876861
/// found, returns [defaultsTo].
862+
@override
877863
T? valueAt(Folder dir) => _valueAtFromFiles(dir) ?? defaultsTo;
878864

879865
/// The backing store for [_valueAtFromFiles].
@@ -1064,11 +1050,11 @@ mixin _DartdocArgOption<T> implements DartdocOption<T> {
10641050
}
10651051

10661052
@override
1067-
void _onMissing(
1053+
Never _onMissing(
10681054
_OptionValueWithContext<T> valueWithContext, String missingPath) =>
10691055
_onMissingFromArgs(valueWithContext, missingPath);
10701056

1071-
void _onMissingFromArgs(
1057+
Never _onMissingFromArgs(
10721058
_OptionValueWithContext<T> valueWithContext, String missingPath) {
10731059
throw DartdocFileMissing(
10741060
'Argument --$argName, set to ${valueWithContext.value}, resolves to '

0 commit comments

Comments
 (0)