Skip to content

Commit c3305c4

Browse files
committed
1 parent 3c8fa78 commit c3305c4

File tree

3 files changed

+197
-125
lines changed

3 files changed

+197
-125
lines changed

lib/src/script.dart

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,9 @@ part of unscripted;
4949
/// sub-command was specified on the command-line, it is invoked with it's
5050
/// corresponding command-line arguments, and so forth (recursively). If a
5151
/// model has sub-commands, but no sub-command was specified, this is treated
52-
/// as an error, and the help text is displayed.
52+
/// as an error, and the help text is displayed. The method call's return
53+
/// value is captured, and if a future, resolved to a completion value. A
54+
/// future for this value is then returned.
5355
///
5456
/// Basic example:
5557
///
@@ -115,8 +117,9 @@ abstract class Script {
115117
/// First, the [arguments] are parsed. If the arguments were invalid *or*
116118
/// if help was requested, help text is printed and the method returns.
117119
/// Otherwise, script-specific logic is executed on the successfully parsed
118-
/// arguments.
119-
execute(
120+
/// arguments, from which a result is captured. A future for this result
121+
/// is returned.
122+
Future execute(
120123
List<String> arguments,
121124
{Map<String, String> environment,
122125
bool isWindows});

lib/src/script_impl.dart

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11

22
library unscripted.declaration_script;
33

4+
import 'dart:async';
45
import 'dart:io';
56
import 'dart:mirrors';
67

@@ -19,32 +20,34 @@ abstract class ScriptImpl implements Script {
1920

2021
List<Plugin> get plugins;
2122

22-
execute(
23+
Future execute(
2324
List<String> arguments,
2425
{Map<String, String> environment,
25-
bool isWindows}) {
26+
bool isWindows}) => new Future.sync(() {
2627

27-
if(isWindows == null) isWindows = Platform.isWindows && Platform.environment['SHELL'] == null;
28+
if (isWindows == null) isWindows = Platform.isWindows && Platform.environment['SHELL'] == null;
2829

29-
CommandInvocation commandInvocation;
30-
31-
try {
32-
commandInvocation = usage.parse(arguments);
30+
return new Future.sync(() {
31+
var commandInvocation = usage.parse(arguments);
3332
var reversedPlugins = plugins.reversed;
34-
if(!reversedPlugins.every((plugin) => plugin.onParse(usage, commandInvocation, environment, isWindows))) return;
33+
if (!reversedPlugins.every((plugin) => plugin.onParse(
34+
usage, commandInvocation, environment, isWindows))) return null;
3535
commandInvocation = usage.validate(commandInvocation);
36-
if(!reversedPlugins.every((plugin) => plugin.onValidate(usage, commandInvocation, environment, isWindows))) return;
37-
} catch (e) {
36+
if (!reversedPlugins.every((plugin) => plugin.onValidate(
37+
usage, commandInvocation, environment, isWindows))) return null;
38+
return commandInvocation;
39+
})
40+
.catchError((e) {
3841
// TODO: ArgParser.parse throws FormatException which does not indicate
3942
// which sub-command was trying to be executed.
4043
var helpUsage = e is UsageException ? e.usage : usage;
4144
_handleUsageError(helpUsage, e, isWindows);
42-
return;
43-
}
44-
45-
_handleResults(commandInvocation, isWindows);
46-
47-
}
45+
// TODO: Rethrow to give visibility into usage errors as well?
46+
})
47+
.then((CommandInvocation commandInvocation) => commandInvocation == null
48+
? null
49+
: _handleResults(commandInvocation, isWindows));
50+
});
4851

4952
/// Handles successfully validated [commandInvocation].
5053
_handleResults(CommandInvocation commandInvocation, bool isWindows);
@@ -100,14 +103,15 @@ abstract class DeclarationScript extends ScriptImpl {
100103

101104
var topResult = _getTopCommandResult(topInvocation);
102105

103-
_handleSubCommands(topResult, commandInvocation.subCommand, usage, isWindows);
106+
var result = _handleSubCommands(topResult, commandInvocation.subCommand, usage, isWindows);
107+
return result == null ? result : result.reflectee;
104108
}
105109

106110
_getTopCommandResult(Invocation invocation);
107111

108112
_handleSubCommands(InstanceMirror result, CommandInvocation commandInvocation, Usage usage, bool isWindows) {
109113

110-
if(commandInvocation == null) {
114+
if (commandInvocation == null) {
111115
// TODO: Move this to an earlier UsageException instead ?
112116
if(usage != null && usage.commands.keys.any((commandName) => !['help', 'completion'].contains(commandName))) {
113117
_handleUsageError(
@@ -116,8 +120,10 @@ abstract class DeclarationScript extends ScriptImpl {
116120
usage: usage,
117121
cause: 'Must specify a sub-command.'),
118122
isWindows);
123+
return null;
124+
} else {
125+
return result;
119126
}
120-
return;
121127
}
122128

123129
var commandName = commandInvocation.name;
@@ -128,8 +134,8 @@ abstract class DeclarationScript extends ScriptImpl {
128134
var invocation = convertCommandInvocationToInvocation(commandInvocation, commandMethod, memberName: commandSymbol);
129135
var subResult = result.delegate(invocation);
130136
Usage subUsage;
131-
if(commandInvocation.subCommand != null) subUsage = usage.commands[commandInvocation.subCommand.name];
132-
_handleSubCommands(reflect(subResult), commandInvocation.subCommand, subUsage, isWindows);
137+
if (commandInvocation.subCommand != null) subUsage = usage.commands[commandInvocation.subCommand.name];
138+
return _handleSubCommands(reflect(subResult), commandInvocation.subCommand, subUsage, isWindows);
133139
}
134140

135141
}

0 commit comments

Comments
 (0)