cmd/cue/cmd: allow passing args to API

doesn't fully work yet because commands and
flags are still global.

Issue #50

Change-Id: I281684d0a9bfc72c9c852cf17f1552587e127efb
Reviewed-on: https://cue-review.googlesource.com/c/cue/+/2160
Reviewed-by: Marcel van Lohuizen <mpvl@google.com>
diff --git a/cmd/cue/cmd/common_test.go b/cmd/cue/cmd/common_test.go
index ff751cf..01bcb7a 100644
--- a/cmd/cue/cmd/common_test.go
+++ b/cmd/cue/cmd/common_test.go
@@ -70,12 +70,14 @@
 			g.Go(func() error {
 				defer wOut.Close()
 				defer func() {
-					if e := recover(); e != nil {
-						if err, ok := e.(error); ok {
-							errors.Print(wOut, err)
-						} else {
-							fmt.Fprintln(wOut, e)
-						}
+					switch err := recover().(type) {
+					case nil:
+					case panicError:
+						errors.Print(wOut, err.Err)
+					case error:
+						errors.Print(wOut, err)
+					default:
+						fmt.Fprintln(wOut, err)
 					}
 				}()
 				cmd.Execute()
diff --git a/cmd/cue/cmd/root.go b/cmd/cue/cmd/root.go
index 0f76eeb..df8b155 100644
--- a/cmd/cue/cmd/root.go
+++ b/cmd/cue/cmd/root.go
@@ -15,6 +15,8 @@
 package cmd
 
 import (
+	"context"
+	"errors"
 	"fmt"
 	logger "log"
 	"os"
@@ -67,9 +69,8 @@
 	SilenceUsage: true,
 }
 
-// Execute adds all child commands to the root command sets flags appropriately.
-// This is called by main.main(). It only needs to happen once to the rootCmd.
-func Execute() {
+// Main runs the cue tool. It loads the tool flags.
+func Main(ctx context.Context, args []string) (err error) {
 	log.SetFlags(0)
 	// Three categories of commands:
 	// - normal
@@ -77,17 +78,17 @@
 	// - help
 	// For the latter two, we need to use the default loading.
 	defer func() {
-		switch err := recover(); err {
+		switch e := recover().(type) {
 		case nil:
-		case panicSentinel:
-			log.Fatal(err)
-			os.Exit(1)
+		case panicError:
+			err = e.Err
 		default:
 			panic(err)
 		}
 		// We use panic to escape, instead of os.Exit
 	}()
-	if args := os.Args[1:]; len(args) >= 1 && args[0] != "help" {
+	rootCmd.SetArgs(args)
+	if len(args) >= 1 && args[0] != "help" {
 		// TODO: for now we only allow one instance. Eventually, we can allow
 		// more if they all belong to the same package and we merge them
 		// before computing commands.
@@ -112,11 +113,13 @@
 				// list available commands
 				commands := tools.Lookup(sub.name)
 				i, err := commands.Fields()
-				must(err)
+				if err != nil {
+					return err
+				}
 				for i.Next() {
 					_, _ = addCustom(sub.cmd, sub.name, i.Label(), tools)
 				}
-				return // TODO: will this trigger the help?
+				return nil
 			}
 			tools := buildTools(rootCmd, args[1:])
 			_, err := addCustom(sub.cmd, sub.name, args[0], tools)
@@ -126,22 +129,16 @@
 			}
 		}
 	}
-	if err := rootCmd.Execute(); err != nil {
-		// log.Fatal(err)
-		os.Exit(1)
-	}
+	return rootCmd.Execute()
 }
 
-var panicSentinel = "terminating because of errors"
-
-func must(err error) {
-	if err != nil {
-		log.Print(err)
-		exit()
-	}
+type panicError struct {
+	Err error
 }
 
-func exit() { panic(panicSentinel) }
+func exit() {
+	panic(panicError{errors.New("terminating because of errors")})
+}
 
 func init() {
 	cobra.OnInitialize(initConfig)
diff --git a/cmd/cue/main.go b/cmd/cue/main.go
index 2edd4b1..4c7450f 100644
--- a/cmd/cue/main.go
+++ b/cmd/cue/main.go
@@ -14,8 +14,18 @@
 
 package main
 
-import "cuelang.org/go/cmd/cue/cmd"
+import (
+	"context"
+	"log"
+	"os"
+
+	"cuelang.org/go/cmd/cue/cmd"
+)
 
 func main() {
-	cmd.Execute()
+	err := cmd.Main(context.Background(), os.Args[1:])
+	if err != nil {
+		log.Println(err)
+		os.Exit(1)
+	}
 }