Commit c92ca42

Richard Luby <richluby@gmail.com>
2016-11-04 13:07:56
extracted command structure to external library
command structure resides in external library due to extended functionality required.
1 parent 93ab003
Changed files (2)
client.go
@@ -5,6 +5,7 @@ import (
 	"flag"
 	"fmt"
 	"github.com/BurntSushi/toml"
+	"github.com/richluby/commander"
 	"log"
 	"os"
 	"strconv"
@@ -49,7 +50,7 @@ func initUserSession() {
 	//client := &http.Client{}
 	fmt.Printf("%s", "Welcome to the question interface. Use 'help' for more information.")
 	var input string
-	var command Command
+	var command commander.Command
 	var err error
 	reader := bufio.NewReader(os.Stdin)
 	for {
@@ -64,12 +65,19 @@ func initUserSession() {
 			continue
 		}
 		// select a command from the list of available commands
-		if command, err = selectCommand(strings.TrimSpace(args[0])); err != nil {
+		if command, err = commander.SelectCommand(strings.TrimSpace(args[0]), commandArray); err != nil {
 			log.Printf(COLOR_RED+"Error while parsing command: %+v"+COLOR_RESET, err)
 			continue
 		}
+		// parse the flags for the command
+		err = commander.ParseUserCommands(args[1:], &command)
+		if err != nil {
+			log.Printf(COLOR_RED+"Error while parsing parameters: %+v"+COLOR_RESET, err)
+			continue
+		}
+		log.Printf("Flags: %+v", command.Flags)
 		// execute the command and check for any errors that may have occurred
-		if err = command.Run(args[1:]); err != nil {
+		if err = command.Run(command); err != nil {
 			log.Printf(COLOR_RED+"Error while executing command: %+v"+COLOR_RESET, err)
 		}
 	}
command.go
@@ -5,6 +5,7 @@ import (
 	"bytes"
 	"encoding/json"
 	"fmt"
+	"github.com/richluby/commander"
 	"io/ioutil"
 	"log"
 	"net/http"
@@ -13,108 +14,92 @@ import (
 	"strings"
 )
 
-// Command stores the information for running a command
-type Command struct {
-	Command,
-	Description,
-	Usage string
-	Flags []Flag
-	Run   func(args []string) error
-}
-
-// Flag servers to store command flags for the program commands
-type Flag struct {
-	Flag,
-	Description,
-	Usage,
-	Value string
-}
-
-// selectCommand takes a string and converts it to the correct command
-func selectCommand(str string) (Command, error) {
-	for _, value := range commandArray {
-		if strings.Compare(value.Command, str) == 0 {
-			return value, nil
-		}
-	}
-	return Command{}, fmt.Errorf("No command found for: %s", str)
-}
-
 // commandArray contains the full list of commands available to the application
 // note that subcommands are not currently supported
-var commandArray = []Command{
-	Command{Command: "exit",
-		Description: "Exit the self-assessment application and close the connection.",
-		Run: func(args []string) error {
-			fmt.Print("Exiting application.\n")
-			os.Exit(0)
-			return nil
-		}},
-	Command{Command: "score",
+var commandArray = []commander.Command{commander.Command{Command: "exit",
+	Description: "Exit the self-assessment application and close the connection.",
+	Run: func(command commander.Command) error {
+		fmt.Print("Exiting application.\n")
+		os.Exit(0)
+		return nil
+	}},
+	commander.Command{Command: "score",
 		Description: "Display the score for the user.",
-		Run: func(args []string) error {
+		Run: func(command commander.Command) error {
 			fmt.Print("score was run.")
 			return nil
 		}},
-	Command{Command: "test",
+	commander.Command{Command: "test",
 		Usage: "test [flags]",
-		Flags: []Flag{Flag{
+		Flags: map[string]commander.Flag{"blueprint": commander.Flag{
 			Flag:        "blueprint",
 			Usage:       "--blueprint",
 			Description: "Allows to define a list of categories to use",
-			Value:       "NONE"},
-			Flag{
+			Value:       "false"},
+			"questions": commander.Flag{
 				Flag:        "questions",
 				Usage:       "--questions [n]",
+				HasArgs:     true,
 				Description: "Specify the number of questions to pull. Default is 20.",
 				Value:       ""}},
 		Description: "Execute a test. Given a number, will go through a test with [n] questions." +
 			" Type 'exit' to stop test.",
 		Run: executeTest},
-	Command{
+	commander.Command{
 		Command: "user",
 		Usage:   "user <username> <token>",
+		Flags: map[string]commander.Flag{"username": commander.Flag{Flag: "username",
+			Description: "The username to use for this session",
+			Value:       "anonymous",
+			Usage:       ""}},
 		Description: "Set the user name to utilize during this session. If not set, anonymous will be used." +
 			COLOR_GREEN + " token" + COLOR_RESET + " is a unique, non-secret key to prevent duplicate users from occurring.",
-		Run: func(args []string) error {
-			if len(args) < 1 {
-				return fmt.Errorf("Could not set the user due to empty username.")
-			} else if len(args) < 2 {
-				return fmt.Errorf("Could not set the user due to the lack of a user token.")
-			}
-			clientConfig.USER = args[0]
-			return nil
-		}}}
+		Run: setUserName}}
+
+// sets the user name for this session
+func setUserName(command commander.Command) error {
+	if command.Flags["username"].Value == "" {
+		return fmt.Errorf("Could not set the user due to empty username.")
+	} else if command.Flags[""].Value == "" {
+		return fmt.Errorf("Could not set the user due to the lack of a user token.")
+	}
+	clientConfig.USER = command.Flags["username"].Value
+	return nil
+}
 
 // helpCommand prints the help for the list of available commands.
 // it cannot exist in the initialization due to initialization loops
-var helpCommand = Command{Command: "help",
+var helpCommand = commander.Command{Command: "help",
 	Usage: "help [command]",
 	Description: "Display help for all known commands. If " + COLOR_GREEN + "command" + COLOR_RESET + " is specified, " +
 		"displays help for that command.",
-	Run: func(args []string) error {
-		for _, command := range commandArray {
-			if len(args) > 0 && args[0] != command.Command { // only print help for desired command
-				continue
-			}
-			if strings.Compare(command.Usage, "") == 0 { // print command help
-				fmt.Printf("> %s\n\t%s\n", COLOR_GREEN+command.Command+COLOR_RESET, command.Description)
-			} else {
-				fmt.Printf("> %s\n\t%s\n", COLOR_GREEN+command.Usage+COLOR_RESET, command.Description)
-			}
-			if len(command.Flags) > 0 { // print flag help
-				fmt.Print(COLOR_BOLD + "\tFLAGS\n" + COLOR_RESET)
-				for _, commandFlag := range command.Flags {
-					if commandFlag.Usage != "" {
-						fmt.Printf("\t%s\n\t\t%s\n", COLOR_BLUE+commandFlag.Usage+COLOR_RESET, commandFlag.Description)
-					} else {
-						fmt.Printf("\t%s\n\t\t%s\n", COLOR_BLUE+commandFlag.Flag+COLOR_RESET, commandFlag.Description)
-					}
+	Run: displayHelp}
+
+// displayHelp prints the help. if a command is specified,
+// help is displayed for that particular command
+func displayHelp(command commander.Command) error {
+	for _, checkCommand := range commandArray {
+		if command.Flags["command"].Value != "" && command.Flags["command"].Value != checkCommand.Command { // only print help for desired command
+			continue
+		}
+		if strings.Compare(checkCommand.Usage, "") == 0 { // print command help
+			fmt.Printf("> %s\n\t%s\n", COLOR_GREEN+checkCommand.Command+COLOR_RESET, checkCommand.Description)
+		} else {
+			fmt.Printf("> %s\n\t%s\n", COLOR_GREEN+checkCommand.Usage+COLOR_RESET, checkCommand.Description)
+		}
+		if len(checkCommand.Flags) > 0 { // print flag help
+			fmt.Print(COLOR_BOLD + "\tFLAGS\n" + COLOR_RESET)
+			for _, commandFlag := range checkCommand.Flags {
+				if commandFlag.Usage != "" {
+					fmt.Printf("\t%s\n\t\t%s\n", COLOR_BLUE+commandFlag.Usage+COLOR_RESET, commandFlag.Description)
+				} else {
+					fmt.Printf("\t%s\n\t\t%s\n", COLOR_BLUE+commandFlag.Flag+COLOR_RESET, commandFlag.Description)
 				}
 			}
 		}
-		return nil
-	}}
+	}
+	return nil
+}
 
 // init initializes the commandArray to provide a single interface
 func init() {
@@ -170,7 +155,7 @@ func postRecordsToServer(client *http.Client, recordArray *ClientTest) error {
 }
 
 // getRecordFromServer retrieves a record from the server
-func getRecordFromServer(client *http.Client, config CLIENT_CONFIG, numQuestions int) ([]ClientRecord, error) {
+func getRecordFromServer(client *http.Client, config CLIENT_CONFIG, numQuestions int, useBlueprint bool) ([]ClientRecord, error) {
 	// allows adding configuration for port specific (ie HTTPS) requests
 	resp, err := client.Get(clientConfig.SERVER_URL + API_ROOT + "/test" + "?questions=" + strconv.Itoa(numQuestions))
 	if err != nil {
@@ -189,7 +174,7 @@ func getRecordFromServer(client *http.Client, config CLIENT_CONFIG, numQuestions
 // getCategoriesFromServer requests a new list of the available
 // categories from the server. functionality provided for
 // command integration as well
-func getCategoriesFromServer(args []string) error {
+func getCategoriesFromServer(command commander.Command) error {
 	client := &http.Client{}
 	resp, err := client.Get(clientConfig.SERVER_URL + API_ROOT + "/questions/categories")
 	if err != nil {
@@ -206,29 +191,43 @@ func getCategoriesFromServer(args []string) error {
 }
 
 // parseTestCommandFlags parses the flags for the test command
-func parseTestCommandFlags(args []string) {
-
+func parseTestCommandFlags(command commander.Command) (int, bool, error) {
+	questions := 20
+	var err error
+	useBlueprint := false
+	for key, commandFlag := range command.Flags {
+		switch key {
+		case "blueprint":
+			if commandFlag.Value != "false" {
+				useBlueprint = true
+			}
+		case "questions":
+			if commandFlag.Value != "" {
+				questions, err = strconv.Atoi(commandFlag.Value)
+			}
+		}
+	}
+	return questions, useBlueprint, err
 }
 
 // executeTest runs a user through a test
 // from retrieving the records to returning the answers
-func executeTest(args []string) error {
+func executeTest(command commander.Command) error {
 	client := &http.Client{}
 	var clientTest ClientTest
 	var err error
 	questions := 20
-	if len(args) > 0 {
-		questions, err = strconv.Atoi(args[0])
-		if err != nil {
-			return fmt.Errorf("Error while converting to number: %+v", err)
-		}
+	useBluprint := false
+	questions, useBluprint, err = parseTestCommandFlags(command)
+	if err != nil {
+		return fmt.Errorf("Error while parsing flags for test: %+v", err)
 	}
-	clientTest.Records, err = getRecordFromServer(client, clientConfig, questions)
+	clientTest.Records, err = getRecordFromServer(client, clientConfig, questions, useBluprint)
 	if err != nil {
 		return fmt.Errorf("Error while requesting test from server: %+v", err)
 	}
 	clientTest.Username = clientConfig.USER
-	if err = getCategoriesFromServer(nil); err != nil {
+	if err = getCategoriesFromServer(commander.Command{}); err != nil {
 		return fmt.Errorf("Error while pulling server categories: %+v", err)
 	}
 	runTest(&clientTest)