Skip to content

Commit db81d94

Browse files
authored
Move ToolConfiguration wholesale to it's own file (#3911)
1 parent d073f43 commit db81d94

File tree

4 files changed

+140
-129
lines changed

4 files changed

+140
-129
lines changed

lib/src/dartdoc_options.dart

+2-127
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
/// line arguments.
1313
library;
1414

15-
import 'dart:io' show Platform, exitCode, stderr, stdout;
15+
import 'dart:io' show exitCode, stderr, stdout;
1616

1717
import 'package:analyzer/dart/element/element.dart';
1818
import 'package:analyzer/file_system/file_system.dart';
@@ -26,8 +26,7 @@ import 'package:dartdoc/src/logging.dart';
2626
import 'package:dartdoc/src/model/model.dart';
2727
import 'package:dartdoc/src/package_meta.dart';
2828
import 'package:dartdoc/src/source_linker.dart';
29-
import 'package:dartdoc/src/tool_definition.dart';
30-
import 'package:dartdoc/src/tool_runner.dart';
29+
import 'package:dartdoc/src/tool_configuration.dart';
3130
import 'package:dartdoc/src/warnings.dart';
3231
import 'package:path/path.dart' as p show Context, canonicalize;
3332
import 'package:yaml/yaml.dart';
@@ -152,130 +151,6 @@ class CategoryConfiguration {
152151
}
153152
}
154153

155-
/// A configuration class that can interpret [ToolDefinition]s from a YAML map.
156-
class ToolConfiguration {
157-
final Map<String, ToolDefinition> tools;
158-
159-
final ResourceProvider resourceProvider;
160-
161-
late ToolRunner runner = ToolRunner(this);
162-
163-
ToolConfiguration._(this.tools, this.resourceProvider);
164-
165-
static ToolConfiguration empty(ResourceProvider resourceProvider) {
166-
return ToolConfiguration._(const {}, resourceProvider);
167-
}
168-
169-
// TODO(jcollins-g): consider caching these.
170-
static ToolConfiguration fromYamlMap(YamlMap yamlMap,
171-
String canonicalYamlPath, ResourceProvider resourceProvider) {
172-
var newToolDefinitions = <String, ToolDefinition>{};
173-
var pathContext = resourceProvider.pathContext;
174-
for (var MapEntry(:key, value: toolMap) in yamlMap.entries) {
175-
var name = key.toString();
176-
if (toolMap is! Map) {
177-
throw DartdocOptionError(
178-
'Tools must be defined as a map of tool names to definitions. Tool '
179-
'$name is not a map.');
180-
}
181-
182-
var description = toolMap['description'].toString();
183-
184-
List<String>? findCommand([String prefix = '']) {
185-
// If the command key is given, then it applies to all platforms.
186-
var commandFromKey = toolMap.containsKey('${prefix}command')
187-
? '${prefix}command'
188-
: '$prefix${Platform.operatingSystem}';
189-
if (!toolMap.containsKey(commandFromKey)) {
190-
return null;
191-
}
192-
var commandFrom = toolMap[commandFromKey] as YamlNode;
193-
List<String> command;
194-
if (commandFrom.value is String) {
195-
command = [commandFrom.toString()];
196-
} else if (commandFrom is YamlList) {
197-
command = commandFrom.map((node) => node.toString()).toList();
198-
} else {
199-
throw DartdocOptionError(
200-
'Tool commands must be a path to an executable, or a list of '
201-
'strings that starts with a path to an executable. The tool '
202-
"'$name' has a '$commandFromKey' entry that is a "
203-
'${commandFrom.runtimeType}');
204-
}
205-
if (command.isEmpty || command[0].isEmpty) {
206-
throw DartdocOptionError(
207-
'Tool commands must not be empty. Tool $name command entry '
208-
'"$commandFromKey" must contain at least one path.');
209-
}
210-
return command;
211-
}
212-
213-
var command = findCommand();
214-
if (command == null) {
215-
throw DartdocOptionError(
216-
'At least one of "command" or "${Platform.operatingSystem}" must '
217-
'be defined for the tool $name.');
218-
}
219-
var setupCommand = findCommand('setup_');
220-
221-
var rawCompileArgs = toolMap[compileArgsTagName];
222-
var compileArgs = switch (rawCompileArgs) {
223-
null => const <String>[],
224-
String() => [toolMap[compileArgsTagName].toString()],
225-
YamlList() =>
226-
rawCompileArgs.map((node) => node.toString()).toList(growable: false),
227-
_ => throw DartdocOptionError(
228-
'Tool compile arguments must be a list of strings. The tool '
229-
"'$name' has a '$compileArgsTagName' entry that is a "
230-
'${rawCompileArgs.runtimeType}',
231-
),
232-
};
233-
234-
/// Validates [executable] and returns whether it is a Dart script.
235-
bool isDartScript(String executable) {
236-
var executableFile = resourceProvider.getFile(executable);
237-
if (resourceProvider.isNotFound(executableFile)) {
238-
throw DartdocOptionError('Command executables must exist. '
239-
'The file "$executable" does not exist for tool $name.');
240-
}
241-
242-
var isDartCommand = ToolDefinition.isDartExecutable(executable);
243-
// Dart scripts don't need to be executable, because they'll be
244-
// executed with the Dart binary.
245-
if (!isDartCommand && !resourceProvider.isExecutable(executableFile)) {
246-
throw DartdocOptionError('Non-Dart commands must be '
247-
'executable. The file "$executable" for tool $name does not have '
248-
'execute permission.');
249-
}
250-
return isDartCommand;
251-
}
252-
253-
var executableRelativePath = command.removeAt(0);
254-
var executable = pathContext.canonicalize(
255-
pathContext.join(canonicalYamlPath, executableRelativePath));
256-
var isDartSetupScript = isDartScript(executable);
257-
if (setupCommand != null) {
258-
var setupExecutableRelativePath = setupCommand.removeAt(0);
259-
var setupExecutable = pathContext.canonicalize(
260-
pathContext.join(canonicalYamlPath, setupExecutableRelativePath));
261-
// Setup commands aren't snapshotted, since they're only run once.
262-
setupCommand = [
263-
if (isDartSetupScript) Platform.resolvedExecutable,
264-
setupExecutable,
265-
...setupCommand,
266-
];
267-
}
268-
newToolDefinitions[name] = ToolDefinition.fromCommand(
269-
[executable, ...command],
270-
setupCommand ?? const [],
271-
description,
272-
resourceProvider,
273-
compileArgs: compileArgs);
274-
}
275-
return ToolConfiguration._(newToolDefinitions, resourceProvider);
276-
}
277-
}
278-
279154
/// A container class to keep track of where our yaml data came from.
280155
class _YamlFileData {
281156
/// The map from the yaml file.

lib/src/tool_configuration.dart

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
// Copyright (c) 2024, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
import 'dart:io' show Platform;
6+
7+
import 'package:analyzer/file_system/file_system.dart';
8+
import 'package:dartdoc/src/dartdoc_options.dart';
9+
import 'package:dartdoc/src/io_utils.dart';
10+
import 'package:dartdoc/src/tool_definition.dart';
11+
import 'package:dartdoc/src/tool_runner.dart';
12+
import 'package:yaml/yaml.dart';
13+
14+
/// A configuration class that can interpret [ToolDefinition]s from a YAML map.
15+
class ToolConfiguration {
16+
final Map<String, ToolDefinition> tools;
17+
18+
final ResourceProvider resourceProvider;
19+
20+
late final ToolRunner runner = ToolRunner(this);
21+
22+
ToolConfiguration._(this.tools, this.resourceProvider);
23+
24+
static ToolConfiguration empty(ResourceProvider resourceProvider) {
25+
return ToolConfiguration._(const {}, resourceProvider);
26+
}
27+
28+
// TODO(jcollins-g): consider caching these.
29+
static ToolConfiguration fromYamlMap(YamlMap yamlMap,
30+
String canonicalYamlPath, ResourceProvider resourceProvider) {
31+
var newToolDefinitions = <String, ToolDefinition>{};
32+
var pathContext = resourceProvider.pathContext;
33+
for (var MapEntry(:key, value: toolMap) in yamlMap.entries) {
34+
var name = key.toString();
35+
if (toolMap is! Map) {
36+
throw DartdocOptionError(
37+
'Tools must be defined as a map of tool names to definitions. Tool '
38+
'$name is not a map.');
39+
}
40+
41+
var description = toolMap['description'].toString();
42+
43+
List<String>? findCommand([String prefix = '']) {
44+
// If the command key is given, then it applies to all platforms.
45+
var commandFromKey = toolMap.containsKey('${prefix}command')
46+
? '${prefix}command'
47+
: '$prefix${Platform.operatingSystem}';
48+
if (!toolMap.containsKey(commandFromKey)) {
49+
return null;
50+
}
51+
var commandFrom = toolMap[commandFromKey] as YamlNode;
52+
List<String> command;
53+
if (commandFrom.value is String) {
54+
command = [commandFrom.toString()];
55+
} else if (commandFrom is YamlList) {
56+
command = commandFrom.map((node) => node.toString()).toList();
57+
} else {
58+
throw DartdocOptionError(
59+
'Tool commands must be a path to an executable, or a list of '
60+
'strings that starts with a path to an executable. The tool '
61+
"'$name' has a '$commandFromKey' entry that is a "
62+
'${commandFrom.runtimeType}');
63+
}
64+
if (command.isEmpty || command[0].isEmpty) {
65+
throw DartdocOptionError(
66+
'Tool commands must not be empty. Tool $name command entry '
67+
'"$commandFromKey" must contain at least one path.');
68+
}
69+
return command;
70+
}
71+
72+
var command = findCommand();
73+
if (command == null) {
74+
throw DartdocOptionError(
75+
'At least one of "command" or "${Platform.operatingSystem}" must '
76+
'be defined for the tool $name.');
77+
}
78+
var setupCommand = findCommand('setup_');
79+
80+
var rawCompileArgs = toolMap[compileArgsTagName];
81+
var compileArgs = switch (rawCompileArgs) {
82+
null => const <String>[],
83+
String() => [toolMap[compileArgsTagName].toString()],
84+
YamlList() =>
85+
rawCompileArgs.map((node) => node.toString()).toList(growable: false),
86+
_ => throw DartdocOptionError(
87+
'Tool compile arguments must be a list of strings. The tool '
88+
"'$name' has a '$compileArgsTagName' entry that is a "
89+
'${rawCompileArgs.runtimeType}',
90+
),
91+
};
92+
93+
/// Validates [executable] and returns whether it is a Dart script.
94+
bool isDartScript(String executable) {
95+
var executableFile = resourceProvider.getFile(executable);
96+
if (resourceProvider.isNotFound(executableFile)) {
97+
throw DartdocOptionError('Command executables must exist. '
98+
'The file "$executable" does not exist for tool $name.');
99+
}
100+
101+
var isDartCommand = ToolDefinition.isDartExecutable(executable);
102+
// Dart scripts don't need to be executable, because they'll be
103+
// executed with the Dart binary.
104+
if (!isDartCommand && !resourceProvider.isExecutable(executableFile)) {
105+
throw DartdocOptionError('Non-Dart commands must be '
106+
'executable. The file "$executable" for tool $name does not have '
107+
'execute permission.');
108+
}
109+
return isDartCommand;
110+
}
111+
112+
var executableRelativePath = command.removeAt(0);
113+
var executable = pathContext.canonicalize(
114+
pathContext.join(canonicalYamlPath, executableRelativePath));
115+
var isDartSetupScript = isDartScript(executable);
116+
if (setupCommand != null) {
117+
var setupExecutableRelativePath = setupCommand.removeAt(0);
118+
var setupExecutable = pathContext.canonicalize(
119+
pathContext.join(canonicalYamlPath, setupExecutableRelativePath));
120+
// Setup commands aren't snapshotted, since they're only run once.
121+
setupCommand = [
122+
if (isDartSetupScript) Platform.resolvedExecutable,
123+
setupExecutable,
124+
...setupCommand,
125+
];
126+
}
127+
newToolDefinitions[name] = ToolDefinition.fromCommand(
128+
[executable, ...command],
129+
setupCommand ?? const [],
130+
description,
131+
resourceProvider,
132+
compileArgs: compileArgs);
133+
}
134+
return ToolConfiguration._(newToolDefinitions, resourceProvider);
135+
}
136+
}

lib/src/tool_runner.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@
55
import 'dart:io' show Process, ProcessException;
66

77
import 'package:analyzer/file_system/file_system.dart';
8-
import 'package:dartdoc/src/dartdoc_options.dart';
98
import 'package:dartdoc/src/io_utils.dart';
9+
import 'package:dartdoc/src/tool_configuration.dart';
1010
import 'package:dartdoc/src/tool_definition.dart';
1111
import 'package:path/path.dart' as path;
1212

test/tool_runner_test.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@
44

55
import 'dart:io';
66

7-
import 'package:dartdoc/src/dartdoc_options.dart';
87
import 'package:dartdoc/src/package_meta.dart';
8+
import 'package:dartdoc/src/tool_configuration.dart';
99
import 'package:dartdoc/src/tool_definition.dart';
1010
import 'package:dartdoc/src/tool_runner.dart';
1111
import 'package:path/path.dart' as path;

0 commit comments

Comments
 (0)