From 726fc877d1593b526cf8d1b197228c53e781ee82 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Marvin=20L=C3=B6bel?= <loebel.marvin@gmail.com>
Date: Mon, 18 Feb 2013 20:18:34 +0100
Subject: [PATCH] Added rust convenience tool similar to go's go tool

---
 Makefile.in          |   8 +-
 mk/clean.mk          |   3 +
 mk/dist.mk           |   1 +
 mk/install.mk        |   8 ++
 mk/pp.mk             |   3 +-
 mk/tests.mk          |   8 +-
 mk/tools.mk          |  36 +++++++
 src/driver/driver.rs |   3 +
 src/librust/rust.rc  | 235 +++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 302 insertions(+), 3 deletions(-)
 create mode 100644 src/librust/rust.rc

diff --git a/Makefile.in b/Makefile.in
index cf6fcd42021..30aefd02cd2 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -134,6 +134,7 @@ CFG_LIBFUZZER :=$(call CFG_LIB_NAME,fuzzer)
 CFG_LIBRUSTPKG :=$(call CFG_LIB_NAME,rustpkg)
 CFG_LIBRUSTDOC :=$(call CFG_LIB_NAME,rustdoc)
 CFG_LIBRUSTI :=$(call CFG_LIB_NAME,rusti)
+CFG_LIBRUST :=$(call CFG_LIB_NAME,rust)
 
 STDLIB_GLOB :=$(call CFG_LIB_GLOB,std)
 CORELIB_GLOB :=$(call CFG_LIB_GLOB,core)
@@ -143,6 +144,7 @@ LIBFUZZER_GLOB :=$(call CFG_LIB_GLOB,fuzzer)
 LIBRUSTPKG_GLOB :=$(call CFG_LIB_GLOB,rustpkg)
 LIBRUSTDOC_GLOB :=$(call CFG_LIB_GLOB,rustdoc)
 LIBRUSTI_GLOB :=$(call CFG_LIB_GLOB,rusti)
+LIBRUST_GLOB :=$(call CFG_LIB_GLOB,rust)
 STDLIB_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,std)
 CORELIB_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,core)
 LIBRUSTC_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,rustc)
@@ -151,6 +153,7 @@ LIBFUZZER_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,fuzzer)
 LIBRUSTPKG_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,rustpkg)
 LIBRUSTDOC_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,rustdoc)
 LIBRUSTI_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,rusti)
+LIBRUST_DSYM_GLOB :=$(call CFG_LIB_DSYM_GLOB,rust)
 
 # version-string calculation
 CFG_GIT_DIR := $(CFG_SRC_DIR).git
@@ -374,10 +377,12 @@ CSREQ$(1)_T_$(2)_H_$(3) = \
 	$$(HBIN$(1)_H_$(3))/rustpkg$$(X) \
 	$$(HBIN$(1)_H_$(3))/rustdoc$$(X) \
 	$$(HBIN$(1)_H_$(3))/rusti$$(X) \
+	$$(HBIN$(1)_H_$(3))/rust$$(X) \
 	$$(HLIB$(1)_H_$(3))/$$(CFG_LIBFUZZER) \
 	$$(HLIB$(1)_H_$(3))/$$(CFG_LIBRUSTPKG) \
 	$$(HLIB$(1)_H_$(3))/$$(CFG_LIBRUSTDOC) \
 	$$(HLIB$(1)_H_$(3))/$$(CFG_LIBRUSTI) \
+	$$(HLIB$(1)_H_$(3))/$$(CFG_LIBRUST) \
 	$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_CORELIB) \
 	$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_STDLIB)  \
 	$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBSYNTAX)  \
@@ -385,7 +390,8 @@ CSREQ$(1)_T_$(2)_H_$(3) = \
 	$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBFUZZER) \
 	$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTPKG) \
 	$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTDOC) \
-	$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTI)
+	$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTI) \
+	$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUST)
 
 ifeq ($(1),0)
 # Don't run the the stage0 compiler under valgrind - that ship has sailed
diff --git a/mk/clean.mk b/mk/clean.mk
index 3b35071c818..f02e2427a0c 100644
--- a/mk/clean.mk
+++ b/mk/clean.mk
@@ -68,6 +68,7 @@ clean$(1)_H_$(2):
 	$(Q)rm -f $$(HBIN$(1)_H_$(2))/serializer$(X)
 	$(Q)rm -f $$(HBIN$(1)_H_$(2))/rustdoc$(X)
 	$(Q)rm -f $$(HBIN$(1)_H_$(2))/rusti$(X)
+	$(Q)rm -f $$(HBIN$(1)_H_$(2))/rust$(X)
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBFUZZER)
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTPKG)
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTDOC)
@@ -77,6 +78,7 @@ clean$(1)_H_$(2):
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTC)
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBSYNTAX)
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUSTI)
+	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_LIBRUST)
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CORELIB_GLOB)
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(STDLIB_GLOB)
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTC_GLOB)
@@ -85,6 +87,7 @@ clean$(1)_H_$(2):
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTPKG_GLOB)
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTDOC_GLOB)
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUSTI_GLOB)
+	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(LIBRUST_GLOB)
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/$(CFG_RUSTLLVM)
 	$(Q)rm -f $$(HLIB$(1)_H_$(2))/libstd.rlib
 
diff --git a/mk/dist.mk b/mk/dist.mk
index 5b6740461e6..3684b9c4544 100644
--- a/mk/dist.mk
+++ b/mk/dist.mk
@@ -27,6 +27,7 @@ PKG_FILES := \
       driver                                   \
       librustpkg                               \
       librusti                                 \
+      librust                                  \
       librustc                                 \
       compiletest                              \
       etc                                      \
diff --git a/mk/install.mk b/mk/install.mk
index 7f3ec816e02..ebbe5f007f1 100644
--- a/mk/install.mk
+++ b/mk/install.mk
@@ -60,6 +60,8 @@ install-target-$(1)-host-$(2): $$(CSREQ$$(ISTAGE)_T_$(1)_H_$(2))
 		$$(TL$(1)$(2)),$$(PTL$(1)$(2)),$$(LIBRUSTDOC_GLOB))
 	$$(Q)$$(call INSTALL_LIB, \
 		$$(TL$(1)$(2)),$$(PTL$(1)$(2)),$$(LIBRUSTI_GLOB))
+	$$(Q)$$(call INSTALL_LIB, \
+		$$(TL$(1)$(2)),$$(PTL$(1)$(2)),$$(LIBRUST_GLOB))
 	$$(Q)$$(call INSTALL,$$(TL$(1)$(2)),$$(PTL$(1)$(2)),libmorestack.a)
 
 endef
@@ -90,15 +92,18 @@ install-host: $(CSREQ$(ISTAGE)_T_$(CFG_HOST_TRIPLE)_H_$(CFG_HOST_TRIPLE))
 	$(Q)$(call INSTALL,$(HB2),$(PHB),rustpkg$(X))
 	$(Q)$(call INSTALL,$(HB2),$(PHB),rustdoc$(X))
 	$(Q)$(call INSTALL,$(HB2),$(PHB),rusti$(X))
+	$(Q)$(call INSTALL,$(HB2),$(PHB),rust$(X))
 	$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_LIBRUSTC))
 	$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_LIBRUSTPKG))
 	$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_LIBRUSTDOC))
 	$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_LIBRUSTI))
+	$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_LIBRUST))
 	$(Q)$(call INSTALL_LIB,$(HL),$(PHL),$(CORELIB_GLOB))
 	$(Q)$(call INSTALL_LIB,$(HL),$(PHL),$(STDLIB_GLOB))
 	$(Q)$(call INSTALL_LIB,$(HL),$(PHL),$(LIBRUSTC_GLOB))
 	$(Q)$(call INSTALL_LIB,$(HL),$(PHL),$(LIBSYNTAX_GLOB))
 	$(Q)$(call INSTALL_LIB,$(HL),$(PHL),$(LIBRUSTI_GLOB))
+	$(Q)$(call INSTALL_LIB,$(HL),$(PHL),$(LIBRUST_GLOB))
 	$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_RUNTIME))
 	$(Q)$(call INSTALL,$(HL),$(PHL),$(CFG_RUSTLLVM))
 	$(Q)$(call INSTALL,$(S)/man, \
@@ -114,12 +119,14 @@ uninstall:
 	$(Q)rm -f $(PHB)/rustc$(X)
 	$(Q)rm -f $(PHB)/rustpkg$(X)
 	$(Q)rm -f $(PHB)/rusti$(X)
+	$(Q)rm -f $(PHB)/rust$(X)
 	$(Q)rm -f $(PHB)/rustdoc$(X)
 	$(Q)rm -f $(PHL)/$(CFG_RUSTLLVM)
 	$(Q)rm -f $(PHL)/$(CFG_LIBRUSTPKG)
 	$(Q)rm -f $(PHL)/$(CFG_LIBRUSTC)
 	$(Q)rm -f $(PHL)/$(CFG_LIBRUSTDOC)
 	$(Q)rm -f $(PHL)/$(CFG_LIBRUSTI)
+	$(Q)rm -f $(PHL)/$(CFG_LIBRUST)
 	$(Q)rm -f $(PHL)/$(CFG_RUNTIME)
 	$(Q)for i in \
           $(call HOST_LIB_FROM_HL_GLOB,$(CORELIB_GLOB)) \
@@ -129,6 +136,7 @@ uninstall:
           $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTPKG_GLOB)) \
           $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTDOC_GLOB)) \
           $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUSTI_GLOB)) \
+          $(call HOST_LIB_FROM_HL_GLOB,$(LIBRUST_GLOB)) \
         ; \
         do rm -f $$i ; \
         done
diff --git a/mk/pp.mk b/mk/pp.mk
index 772365b1058..653cabfce5f 100644
--- a/mk/pp.mk
+++ b/mk/pp.mk
@@ -19,7 +19,8 @@ else
                          $(S)src/test/*/*/*.rs) \
               $(wildcard $(S)src/fuzzer/*.rs)   \
               $(wildcard $(S)src/rustpkg/*.rs) \
-              $(wildcard $(S)src/rusti/*.rs)
+              $(wildcard $(S)src/rusti/*.rs) \
+              $(wildcard $(S)src/rust/*.rs)
 
   PP_INPUTS_FILTERED = $(shell echo $(PP_INPUTS) | xargs grep -L \
                        "no-reformat\|xfail-pretty\|xfail-test")
diff --git a/mk/tests.mk b/mk/tests.mk
index 8bcf5d5ad10..5daddf98ee8 100644
--- a/mk/tests.mk
+++ b/mk/tests.mk
@@ -14,7 +14,7 @@
 ######################################################################
 
 # The names of crates that must be tested
-TEST_CRATES = core std syntax rustc rustdoc rusti rustpkg
+TEST_CRATES = core std syntax rustc rustdoc rusti rust rustpkg
 
 # Markdown files under doc/ that should have their code extracted and run
 DOC_TEST_NAMES = tutorial tutorial-ffi tutorial-macros tutorial-borrowed-ptr tutorial-tasks rust
@@ -241,6 +241,12 @@ $(3)/test/rustitest.stage$(1)-$(2)$$(X):					\
 	@$$(call E, compile_and_link: $$@)
 	$$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test
 
+$(3)/test/rusttest.stage$(1)-$(2)$$(X):					\
+		$$(RUST_LIB) $$(RUST_INPUTS)		\
+		$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC)
+	@$$(call E, compile_and_link: $$@)
+	$$(STAGE$(1)_T_$(2)_H_$(3)) -o $$@ $$< --test
+
 $(3)/test/rustdoctest.stage$(1)-$(2)$$(X):					\
 		$$(RUSTDOC_LIB) $$(RUSTDOC_INPUTS)		\
 		$$(TLIB$(1)_T_$(2)_H_$(3))/$$(CFG_LIBRUSTC)
diff --git a/mk/tools.mk b/mk/tools.mk
index 1554d760a49..22f109be47f 100644
--- a/mk/tools.mk
+++ b/mk/tools.mk
@@ -30,6 +30,10 @@ RUSTDOC_INPUTS := $(wildcard $(S)src/librustdoc/*.rs)
 RUSTI_LIB := $(S)src/librusti/rusti.rc
 RUSTI_INPUTS := $(wildcard $(S)src/librusti/*.rs)
 
+# Rust, the convenience tool
+RUST_LIB := $(S)src/librust/rust.rc
+RUST_INPUTS := $(wildcard $(S)src/librust/*.rs)
+
 # FIXME: These are only built for the host arch. Eventually we'll
 # have tools that need to built for other targets.
 define TOOLS_STAGE_N_TARGET
@@ -102,6 +106,21 @@ $$(TBIN$(1)_T_$(4)_H_$(3))/rusti$$(X):			\
 	@$$(call E, compile_and_link: $$@)
 	$$(STAGE$(1)_T_$(4)_H_$(3)) --cfg rusti -o $$@ $$<
 
+$$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBRUST):		\
+		$$(RUST_LIB) $$(RUST_INPUTS)			\
+		$$(TSREQ$(1)_T_$(4)_H_$(3))					\
+		$$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_CORELIB)	\
+		$$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_STDLIB)	\
+		$$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBRUSTC)
+	@$$(call E, compile_and_link: $$@)
+	$$(STAGE$(1)_T_$(4)_H_$(3)) -o $$@ $$< && touch $$@
+
+$$(TBIN$(1)_T_$(4)_H_$(3))/rust$$(X):			\
+		$$(DRIVER_CRATE) 							\
+		$$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBRUST)
+	@$$(call E, compile_and_link: $$@)
+	$$(STAGE$(1)_T_$(4)_H_$(3)) --cfg rust -o $$@ $$<
+
 endef
 
 define TOOLS_STAGE_N_HOST
@@ -185,6 +204,23 @@ $$(HBIN$(2)_H_$(4))/rusti$$(X):				\
 	@$$(call E, cp: $$@)
 	$$(Q)cp $$< $$@
 
+$$(HLIB$(2)_H_$(4))/$$(CFG_LIBRUST):					\
+		$$(TLIB$(1)_T_$(4)_H_$(3))/$$(CFG_LIBRUST)	\
+		$$(HLIB$(2)_H_$(4))/$$(CFG_LIBRUSTC)			\
+		$$(HSREQ$(2)_H_$(4))
+	@$$(call E, cp: $$@)
+	$$(Q)cp $$< $$@
+	$$(Q)cp -R $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUST_GLOB) \
+		$$(wildcard $$(TLIB$(1)_T_$(4)_H_$(3))/$(LIBRUST_DSYM_GLOB)) \
+	        $$(HLIB$(2)_H_$(4))
+
+$$(HBIN$(2)_H_$(4))/rust$$(X):				\
+		$$(TBIN$(1)_T_$(4)_H_$(3))/rust$$(X)	\
+		$$(HLIB$(2)_H_$(4))/$$(CFG_LIBRUST)	\
+		$$(HSREQ$(2)_H_$(4))
+	@$$(call E, cp: $$@)
+	$$(Q)cp $$< $$@
+
 endef
 
 $(foreach host,$(CFG_TARGET_TRIPLES),				\
diff --git a/src/driver/driver.rs b/src/driver/driver.rs
index b2c4f69d302..2fc50eb6e75 100644
--- a/src/driver/driver.rs
+++ b/src/driver/driver.rs
@@ -23,6 +23,9 @@ extern mod this(name = "rustdoc", vers = "0.6");
 #[cfg(rusti)]
 extern mod this(name = "rusti", vers = "0.6");
 
+#[cfg(rust)]
+extern mod this(name = "rust", vers = "0.6");
+
 #[cfg(rustc)]
 extern mod this(name = "rustc", vers = "0.6");
 
diff --git a/src/librust/rust.rc b/src/librust/rust.rc
new file mode 100644
index 00000000000..950623b8760
--- /dev/null
+++ b/src/librust/rust.rc
@@ -0,0 +1,235 @@
+// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// rust - central access to other rust tools
+// XXX: Make commands run and test emit proper file endings on winds
+// XXX: Make run only accept source that emits an executable
+
+#[link(name = "rust",
+       vers = "0.6",
+       uuid = "4a24da33-5cc8-4037-9352-2cbe9bd9d27c",
+       url = "https://github.com/mozilla/rust/tree/master/src/rust")];
+
+#[crate_type = "lib"];
+
+extern mod core(vers = "0.6");
+
+use core::run;
+
+enum ValidUsage {
+    Valid, Invalid
+}
+
+impl ValidUsage {
+    fn is_valid(&self) -> bool { match *self {
+        Valid   => true,
+        Invalid => false
+    }}
+}
+
+enum Action {
+    Exec(&str),
+    Call(&fn(args: &[~str]) -> ValidUsage)
+}
+
+enum UsageSource {
+    UsgExec(&str),
+    UsgStr(&str)
+}
+
+struct Command {
+    cmd: &str,
+    action: Action,
+    usage_line: &str,
+    usage_full: UsageSource
+}
+
+const commands: &[Command] = &[
+    Command{
+        cmd: "build",
+        action: Exec("rustc"),
+        usage_line: "compile rust source files",
+        usage_full: UsgExec("rustc --help")
+    },
+    Command{
+        cmd: "run",
+        action: Call(cmd_run),
+        usage_line: "build a executable, and run it",
+        usage_full: UsgStr(
+            "The run command is an shortcut for the command line \n\
+             \"rustc <filename> -o <filestem>~ && ./<filestem>~\".\
+            \n\nUsage:\trust run <filename>"
+        )
+    },
+    Command{
+        cmd: "test",
+        action: Call(cmd_test),
+        usage_line: "build a test executable, and run it",
+        usage_full: UsgStr(
+            "The test command is an shortcut for the command line \n\
+            \"rustc --test <filename> -o <filestem>test~ && \
+            ./<filestem>test~\"\n\nUsage:\trust test <filename>"
+        )
+    },
+    Command{
+        cmd: "doc",
+        action: Exec("rustdoc"),
+        usage_line: "generate documentation from doc comments",
+        usage_full: UsgExec("rustdoc --help")
+    },
+    Command{
+        cmd: "pkg",
+        action: Exec("rustpkg"),
+        usage_line: "download, build, install rust packages",
+        usage_full: UsgExec("rustpkg --help")
+    },
+    Command{
+        cmd: "sketch",
+        action: Exec("rusti"),
+        usage_line: "run a rust interpreter",
+        usage_full: UsgStr("\nUsage:\trusti")
+    },
+    Command{
+        cmd: "help",
+        action: Call(cmd_help),
+        usage_line: "show detailed usage of a command",
+        usage_full: UsgStr(
+            "The help command displays the usage text of another command.\n\
+            The text is either build in, or provided by the corresponding \
+            program.\n\nUsage:\trust help <command>"
+        )
+    }
+];
+
+fn find_cmd(command_string: &str) -> Option<Command> {
+    do commands.find |command| {
+        command.cmd == command_string
+    }
+}
+
+fn cmd_help(args: &[~str]) -> ValidUsage {
+    fn print_usage(command_string: ~str) -> ValidUsage {
+        match find_cmd(command_string) {
+            Some(command) => {
+                match command.action {
+                    Exec(s) => io::println(fmt!(
+                        "The %s command is an alias for the %s program.",
+                        command.cmd, s)),
+                    _       => ()
+                }
+                match command.usage_full {
+                    UsgStr(msg)          => io::println(fmt!("%s\n", msg)),
+                    UsgExec(commandline) => {
+                        let words = str::words(commandline);
+                        let (prog, args) = (words.head(), words.tail());
+                        run::run_program(prog, args);
+                    }
+                }
+                Valid
+            },
+            None => Invalid
+        }
+    }
+
+    match args {
+        [command_string] => print_usage(command_string),
+        _                => Invalid
+    }
+}
+
+fn cmd_test(args: &[~str]) -> ValidUsage {
+    match args {
+        [filename] => {
+            let test_exec = Path(filename).filestem().unwrap() + "test~";
+            if run::run_program("rustc", [
+                ~"--test",
+                filename.to_owned(),
+                ~"-o",
+                test_exec.to_owned()
+            ]) == 0 {
+                run::run_program(~"./" + test_exec, []);
+            }
+            Valid
+        }
+        _          => Invalid
+    }
+}
+
+fn cmd_run(args: &[~str]) -> ValidUsage {
+    match args {
+        [filename] => {
+            let exec = Path(filename).filestem().unwrap() + "~";
+            if run::run_program("rustc", [
+                filename.to_owned(),
+                ~"-o",
+                exec.to_owned()
+            ]) == 0 {
+                run::run_program(~"./"+exec, []);
+            }
+            Valid
+        }
+        _          => Invalid
+    }
+}
+
+fn do_command(command: &Command, args: &[~str]) -> ValidUsage {
+    match command.action {
+        Call(f) => f(args),
+        Exec(commandline) => {
+            let words = str::words(commandline);
+            let (prog, prog_args) = (words.head(), words.tail());
+            let exitstatus = run::run_program(prog, prog_args + args);
+            os::set_exit_status(exitstatus);
+            Valid
+        }
+    }
+}
+
+fn usage() {
+    const indent: uint = 8;
+
+    io::print(
+        "The rust tool is a convenience for managing rust source code.\n\
+        It acts as a shortcut for programs of the rust tool chain.\n\
+        \n\
+        Usage:\trust <command> [arguments]\n\
+        \n\
+        The commands are:\n\
+        \n"
+    );
+
+    for commands.each |command| {
+        let padding = str::repeat(" ", indent - command.cmd.len());
+        io::println(fmt!("    %s%s%s",
+                         command.cmd, padding, command.usage_line));
+    }
+
+    io::print(
+        "\n\
+        Use \"rust help <command>\" for more information about a command.\n\
+        \n"
+    );
+
+}
+
+fn main() {
+    let args = os::args().tail();
+
+    if !args.is_empty() {
+        for commands.each |command| {
+            if command.cmd == args.head() {
+                let result = do_command(command, args.tail());
+                if result.is_valid() { return; }
+            }
+        }
+    }
+
+    usage();
+}