Support command return value forwarding. (#59)

This was unintentionally supported prior to 0.13.6+1. This re-adds
support, documents and tests it, and adds type annotations to make it
type-safe.

Closes #57
Closes #58
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9127332..5f5c8c6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,11 @@
+## 0.13.7
+
+* Add explicit support for forwarding the value returned by `Command.run()` to
+  `CommandRunner.run()`. This worked unintentionally prior to 0.13.6+1.
+
+* Add type arguments to `CommandRunner` and `Command` to indicate the return
+  values of the `run()` functions.
+
 ## 0.13.6+1
 
 * When a `CommandRunner` is passed `--help` before any commands, it now prints
diff --git a/lib/command_runner.dart b/lib/command_runner.dart
index 338ad94..6a6d282 100644
--- a/lib/command_runner.dart
+++ b/lib/command_runner.dart
@@ -16,7 +16,11 @@
 export 'src/usage_exception.dart';
 
 /// A class for invoking [Commands] based on raw command-line arguments.
-class CommandRunner {
+///
+/// The type argument `T` represents the type returned by [Command.run] and
+/// [CommandRunner.run]; it can be ommitted if you're not using the return
+/// values.
+class CommandRunner<T> {
   /// The name of the executable being run.
   ///
   /// Used for error reporting and [usage].
@@ -60,8 +64,8 @@
   }
 
   /// An unmodifiable view of all top-level commands defined for this runner.
-  Map<String, Command> get commands => new UnmodifiableMapView(_commands);
-  final _commands = <String, Command>{};
+  Map<String, Command<T>> get commands => new UnmodifiableMapView(_commands);
+  final _commands = <String, Command<T>>{};
 
   /// The top-level argument parser.
   ///
@@ -74,7 +78,7 @@
   CommandRunner(this.executableName, this.description) {
     argParser.addFlag('help',
         abbr: 'h', negatable: false, help: 'Print this usage information.');
-    addCommand(new HelpCommand());
+    addCommand(new HelpCommand<T>());
   }
 
   /// Prints the usage information for this runner.
@@ -88,7 +92,7 @@
       throw new UsageException(message, _usageWithoutDescription);
 
   /// Adds [Command] as a top-level command to this runner.
-  void addCommand(Command command) {
+  void addCommand(Command<T> command) {
     var names = [command.name]..addAll(command.aliases);
     for (var name in names) {
       _commands[name] = command;
@@ -101,7 +105,7 @@
   ///
   /// This always returns a [Future] in case the command is asynchronous. The
   /// [Future] will throw a [UsageException] if [args] was invalid.
-  Future run(Iterable<String> args) =>
+  Future<T> run(Iterable<String> args) =>
       new Future.sync(() => runCommand(parse(args)));
 
   /// Parses [args] and returns the result, converting an [ArgParserException]
@@ -133,7 +137,9 @@
   /// It's useful to override this to handle global flags and/or wrap the entire
   /// command in a block. For example, you might handle the `--verbose` flag
   /// here to enable verbose logging before running the command.
-  Future runCommand(ArgResults topLevelResults) async {
+  ///
+  /// This returns the return value of [Command.run]. 
+  Future<T> runCommand(ArgResults topLevelResults) async {
     var argResults = topLevelResults;
     var commands = _commands;
     Command command;
@@ -145,7 +151,7 @@
           if (command == null) {
             // No top-level command was chosen.
             printUsage();
-            return;
+            return null;
           }
 
           command.usageException('Missing subcommand for "$commandString".');
@@ -170,13 +176,13 @@
 
       if (argResults['help']) {
         command.printUsage();
-        return;
+        return null;
       }
     }
 
     if (topLevelResults['help']) {
       command.printUsage();
-      return;
+      return null;
     }
 
     // Make sure there aren't unexpected arguments.
@@ -185,7 +191,7 @@
           'Command "${argResults.name}" does not take any arguments.');
     }
 
-    await command.run();
+    return (await command.run()) as T;
   }
 }
 
@@ -197,7 +203,7 @@
 /// A command with subcommands is known as a "branch command" and cannot be run
 /// itself. It should call [addSubcommand] (often from the constructor) to
 /// register subcommands.
-abstract class Command {
+abstract class Command<T> {
   /// The name of this command.
   String get name;
 
@@ -229,18 +235,18 @@
   ///
   /// This will be `null` until [Command.addSubcommmand] has been called with
   /// this command.
-  Command get parent => _parent;
-  Command _parent;
+  Command<T> get parent => _parent;
+  Command<T> _parent;
 
   /// The command runner for this command.
   ///
   /// This will be `null` until [CommandRunner.addCommand] has been called with
   /// this command or one of its parents.
-  CommandRunner get runner {
+  CommandRunner<T> get runner {
     if (parent == null) return _runner;
     return parent.runner;
   }
-  CommandRunner _runner;
+  CommandRunner<T> _runner;
 
   /// The parsed global argument results.
   ///
@@ -298,8 +304,9 @@
   }
 
   /// An unmodifiable view of all sublevel commands of this command.
-  Map<String, Command> get subcommands => new UnmodifiableMapView(_subcommands);
-  final _subcommands = <String, Command>{};
+  Map<String, Command<T>> get subcommands =>
+      new UnmodifiableMapView(_subcommands);
+  final _subcommands = <String, Command<T>>{};
 
   /// Whether or not this command should be hidden from help listings.
   ///
@@ -341,14 +348,15 @@
 
   /// Runs this command.
   ///
-  /// If this returns a [Future], [CommandRunner.run] won't complete until the
-  /// returned [Future] does. Otherwise, the return value is ignored.
+  /// This must return a `T`, a `Future<T>`, or `null`. The value is returned by
+  /// [CommandRunner.runCommand]. Subclasses must explicitly declare a return
+  /// type for `run()`, and may not use `void` if `T` is defined.
   run() {
     throw new UnimplementedError("Leaf command $this must implement run().");
   }
 
   /// Adds [Command] as a subcommand of this.
-  void addSubcommand(Command command) {
+  void addSubcommand(Command<T> command) {
     var names = [command.name]..addAll(command.aliases);
     for (var name in names) {
       _subcommands[name] = command;
diff --git a/lib/src/help_command.dart b/lib/src/help_command.dart
index 085e444..104327c 100644
--- a/lib/src/help_command.dart
+++ b/lib/src/help_command.dart
@@ -7,17 +7,17 @@
 /// The built-in help command that's added to every [CommandRunner].
 ///
 /// This command displays help information for the various subcommands.
-class HelpCommand extends Command {
+class HelpCommand<T> extends Command<T> {
   final name = "help";
   String get description =>
       "Display help information for ${runner.executableName}.";
   String get invocation => "${runner.executableName} help [command]";
 
-  void run() {
+  T run() {
     // Show the default help if no command was specified.
     if (argResults.rest.isEmpty) {
       runner.printUsage();
-      return;
+      return null;
     }
 
     // Walk the command tree to show help for the selected command or
@@ -47,5 +47,6 @@
     }
 
     command.printUsage();
+    return null;
   }
 }
diff --git a/pubspec.yaml b/pubspec.yaml
index 4156049..e1a6b05 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: args
-version: 0.13.6+1
+version: 0.13.7
 author: "Dart Team <misc@dartlang.org>"
 homepage: https://github.com/dart-lang/args
 description: >
diff --git a/test/command_runner_test.dart b/test/command_runner_test.dart
index 8875d8c..9b0713a 100644
--- a/test/command_runner_test.dart
+++ b/test/command_runner_test.dart
@@ -161,6 +161,22 @@
       }), completes);
     });
 
+    test("runs a command with a return value", () {
+      var runner = new CommandRunner<int>("test", "");
+      var command = new ValueCommand();
+      runner.addCommand(command);
+
+      expect(runner.run(["foo"]), completion(equals(12)));
+    });
+
+    test("runs a command with an asynchronous return value", () {
+      var runner = new CommandRunner<String>("test", "");
+      var command = new AsyncValueCommand();
+      runner.addCommand(command);
+
+      expect(runner.run(["foo"]), completion(equals("hi")));
+    });
+
     test("runs a hidden comand", () {
       var command = new HiddenCommand();
       runner.addCommand(command);
diff --git a/test/utils.dart b/test/utils.dart
index e07f8f3..a874eee 100644
--- a/test/utils.dart
+++ b/test/utils.dart
@@ -27,6 +27,22 @@
   }
 }
 
+class ValueCommand extends Command<int> {
+  final name = "foo";
+  final description = "Return a value.";
+  final takesArguments = false;
+
+  int run() => 12;
+}
+
+class AsyncValueCommand extends Command<String> {
+  final name = "foo";
+  final description = "Return a future.";
+  final takesArguments = false;
+
+  Future<String> run() async => "hi";
+}
+
 class MultilineCommand extends Command {
   var hasRun = false;