network: implement asyncblock for polling job results

This implements async blocking if config option is enabled by polling
the async job id and showing a cursor.

Add vendor dependency: briandowns/spinner

Signed-off-by: Rohit Yadav <rohit@apache.org>
diff --git a/cli/completer.go b/cli/completer.go
index 9fb8bd8..bd2822a 100644
--- a/cli/completer.go
+++ b/cli/completer.go
@@ -212,7 +212,7 @@
 				autocompleteAPIArgs = append(autocompleteAPIArgs, "templatefilter=all")
 			}
 			fmt.Printf("\nFetching options, please wait...")
-			response, _ := cmd.NewAPIRequest(r, autocompleteAPI.Name, autocompleteAPIArgs)
+			response, _ := cmd.NewAPIRequest(r, autocompleteAPI.Name, autocompleteAPIArgs, false)
 			fmt.Printf("\r")
 
 			var autocompleteOptions []selectOption
diff --git a/cmd/api.go b/cmd/api.go
index ba7b70d..9237ff5 100644
--- a/cmd/api.go
+++ b/cmd/api.go
@@ -160,7 +160,7 @@
 				return nil
 			}
 
-			response, err := NewAPIRequest(r, api.Name, apiArgs)
+			response, err := NewAPIRequest(r, api.Name, apiArgs, api.Async)
 			if err != nil {
 				return err
 			}
diff --git a/cmd/network.go b/cmd/network.go
index 3361526..c55998f 100644
--- a/cmd/network.go
+++ b/cmd/network.go
@@ -26,15 +26,25 @@
 	"encoding/json"
 	"errors"
 	"fmt"
+	"github.com/briandowns/spinner"
 	"io/ioutil"
 	"net/http"
 	"net/http/cookiejar"
 	"net/url"
+	"runtime"
 	"sort"
 	"strings"
 	"time"
 )
 
+var cursor = []string{"\r⣷ 😸", "\r⣯ 😹", "\r⣟ 😺", "\r⡿ 😻", "\r⢿ 😼", "\r⣻ 😽", "\r⣽ 😾", "\r⣾ 😻"}
+
+func init() {
+	if runtime.GOOS == "windows" {
+		cursor = []string{"|", "/", "-", "\\"}
+	}
+}
+
 func encodeRequestParams(params url.Values) string {
 	if params == nil {
 		return ""
@@ -98,8 +108,49 @@
 	return client, sessionKey, nil
 }
 
+func getResponseData(data map[string]interface{}) map[string]interface{} {
+	for k := range data {
+		if strings.HasSuffix(k, "response") {
+			return data[k].(map[string]interface{})
+		}
+	}
+	return nil
+}
+
+func pollAsyncJob(r *Request, jobId string) (map[string]interface{}, error) {
+	for timeout := float64(r.Config.Core.Timeout); timeout > 0.0; {
+		startTime := time.Now()
+		s := spinner.New(cursor, 200*time.Millisecond)
+		s.Color("blue", "bold")
+		s.Suffix = " polling for async API job result"
+		s.Start()
+		queryResult, queryError := NewAPIRequest(r, "queryAsyncJobResult", []string{"jobid=" + jobId}, false)
+		diff := time.Duration(1*time.Second).Nanoseconds() - time.Now().Sub(startTime).Nanoseconds()
+		if diff > 0 {
+			time.Sleep(time.Duration(diff) * time.Nanosecond)
+		}
+		s.Stop()
+		timeout = timeout - time.Now().Sub(startTime).Seconds()
+		if queryError != nil {
+			return queryResult, queryError
+		}
+		jobStatus := queryResult["jobstatus"].(float64)
+		if jobStatus == 0 {
+			continue
+		}
+		if jobStatus == 1 {
+			return queryResult["jobresult"].(map[string]interface{}), nil
+
+		}
+		if jobStatus == 2 {
+			return queryResult, errors.New("async API job failed")
+		}
+	}
+	return nil, errors.New("async API job query timed out")
+}
+
 // NewAPIRequest makes an API request to configured management server
-func NewAPIRequest(r *Request, api string, args []string) (map[string]interface{}, error) {
+func NewAPIRequest(r *Request, api string, args []string, isAsync bool) (map[string]interface{}, error) {
 	params := make(url.Values)
 	params.Add("command", api)
 	for _, arg := range args {
@@ -156,10 +207,16 @@
 	var data map[string]interface{}
 	_ = json.Unmarshal([]byte(body), &data)
 
-	for k := range data {
-		if strings.HasSuffix(k, "response") {
-			return data[k].(map[string]interface{}), nil
+	if isAsync && r.Config.Core.AsyncBlock {
+		if jobResponse := getResponseData(data); jobResponse != nil && jobResponse["jobid"] != nil {
+			jobId := jobResponse["jobid"].(string)
+			return pollAsyncJob(r, jobId)
 		}
 	}
+
+	if apiResponse := getResponseData(data); apiResponse != nil {
+		return apiResponse, nil
+	}
+
 	return nil, errors.New("failed to decode response")
 }
diff --git a/cmd/set.go b/cmd/set.go
index 1755227..70c7de9 100644
--- a/cmd/set.go
+++ b/cmd/set.go
@@ -27,7 +27,7 @@
 		Name: "set",
 		Help: "Configures options for cmk",
 		SubCommands: map[string][]string{
-			"prompt":     {"🐵", "random"},
+			"prompt":     {"🐵", "🐱", "random"},
 			"asyncblock": {"true", "false"},
 			"timeout":    {"600", "1800", "3600"},
 			"output":     {"json", "text", "table", "xml"},
diff --git a/cmd/sync.go b/cmd/sync.go
index 3bad31e..160aa3f 100644
--- a/cmd/sync.go
+++ b/cmd/sync.go
@@ -26,7 +26,7 @@
 		Name: "sync",
 		Help: "Discovers and updates APIs",
 		Handle: func(r *Request) error {
-			response, err := NewAPIRequest(r, "listApis", []string{"listall=true"})
+			response, err := NewAPIRequest(r, "listApis", []string{"listall=true"}, false)
 			if err != nil {
 				return err
 			}
diff --git a/vendor/github.com/briandowns/spinner/LICENSE b/vendor/github.com/briandowns/spinner/LICENSE
new file mode 100644
index 0000000..dd5b3a5
--- /dev/null
+++ b/vendor/github.com/briandowns/spinner/LICENSE
@@ -0,0 +1,174 @@
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
diff --git a/vendor/github.com/briandowns/spinner/character_sets.go b/vendor/github.com/briandowns/spinner/character_sets.go
new file mode 100644
index 0000000..1efe6e6
--- /dev/null
+++ b/vendor/github.com/briandowns/spinner/character_sets.go
@@ -0,0 +1,59 @@
+package spinner
+
+const (
+	clockOneOClock = '\U0001F550'
+	clockOneThirty = '\U0001F55C'
+)
+
+// CharSets contains the available character sets
+var CharSets = map[int][]string{
+	0:  {"←", "↖", "↑", "↗", "→", "↘", "↓", "↙"},
+	1:  {"▁", "▃", "▄", "▅", "▆", "▇", "█", "▇", "▆", "▅", "▄", "▃", "▁"},
+	2:  {"▖", "▘", "▝", "▗"},
+	3:  {"┤", "┘", "┴", "└", "├", "┌", "┬", "┐"},
+	4:  {"◢", "◣", "◤", "◥"},
+	5:  {"◰", "◳", "◲", "◱"},
+	6:  {"◴", "◷", "◶", "◵"},
+	7:  {"◐", "◓", "◑", "◒"},
+	8:  {".", "o", "O", "@", "*"},
+	9:  {"|", "/", "-", "\\"},
+	10: {"◡◡", "⊙⊙", "◠◠"},
+	11: {"⣾", "⣽", "⣻", "⢿", "⡿", "⣟", "⣯", "⣷"},
+	12: {">))'>", " >))'>", "  >))'>", "   >))'>", "    >))'>", "   <'((<", "  <'((<", " <'((<"},
+	13: {"⠁", "⠂", "⠄", "⡀", "⢀", "⠠", "⠐", "⠈"},
+	14: {"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"},
+	15: {"a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"},
+	16: {"▉", "▊", "▋", "▌", "▍", "▎", "▏", "▎", "▍", "▌", "▋", "▊", "▉"},
+	17: {"■", "□", "▪", "▫"},
+	18: {"←", "↑", "→", "↓"},
+	19: {"╫", "╪"},
+	20: {"⇐", "⇖", "⇑", "⇗", "⇒", "⇘", "⇓", "⇙"},
+	21: {"⠁", "⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈", "⠈"},
+	22: {"⠈", "⠉", "⠋", "⠓", "⠒", "⠐", "⠐", "⠒", "⠖", "⠦", "⠤", "⠠", "⠠", "⠤", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋", "⠉", "⠈"},
+	23: {"⠁", "⠉", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠤", "⠄", "⠄", "⠤", "⠴", "⠲", "⠒", "⠂", "⠂", "⠒", "⠚", "⠙", "⠉", "⠁"},
+	24: {"⠋", "⠙", "⠚", "⠒", "⠂", "⠂", "⠒", "⠲", "⠴", "⠦", "⠖", "⠒", "⠐", "⠐", "⠒", "⠓", "⠋"},
+	25: {"ヲ", "ァ", "ィ", "ゥ", "ェ", "ォ", "ャ", "ュ", "ョ", "ッ", "ア", "イ", "ウ", "エ", "オ", "カ", "キ", "ク", "ケ", "コ", "サ", "シ", "ス", "セ", "ソ", "タ", "チ", "ツ", "テ", "ト", "ナ", "ニ", "ヌ", "ネ", "ノ", "ハ", "ヒ", "フ", "ヘ", "ホ", "マ", "ミ", "ム", "メ", "モ", "ヤ", "ユ", "ヨ", "ラ", "リ", "ル", "レ", "ロ", "ワ", "ン"},
+	26: {".", "..", "..."},
+	27: {"▁", "▂", "▃", "▄", "▅", "▆", "▇", "█", "▉", "▊", "▋", "▌", "▍", "▎", "▏", "▏", "▎", "▍", "▌", "▋", "▊", "▉", "█", "▇", "▆", "▅", "▄", "▃", "▂", "▁"},
+	28: {".", "o", "O", "°", "O", "o", "."},
+	29: {"+", "x"},
+	30: {"v", "<", "^", ">"},
+	31: {">>--->", " >>--->", "  >>--->", "   >>--->", "    >>--->", "    <---<<", "   <---<<", "  <---<<", " <---<<", "<---<<"},
+	32: {"|", "||", "|||", "||||", "|||||", "|||||||", "||||||||", "|||||||", "||||||", "|||||", "||||", "|||", "||", "|"},
+	33: {"[          ]", "[=         ]", "[==        ]", "[===       ]", "[====      ]", "[=====     ]", "[======    ]", "[=======   ]", "[========  ]", "[========= ]", "[==========]"},
+	34: {"(*---------)", "(-*--------)", "(--*-------)", "(---*------)", "(----*-----)", "(-----*----)", "(------*---)", "(-------*--)", "(--------*-)", "(---------*)"},
+	35: {"█▒▒▒▒▒▒▒▒▒", "███▒▒▒▒▒▒▒", "█████▒▒▒▒▒", "███████▒▒▒", "██████████"},
+	36: {"[                    ]", "[=>                  ]", "[===>                ]", "[=====>              ]", "[======>             ]", "[========>           ]", "[==========>         ]", "[============>       ]", "[==============>     ]", "[================>   ]", "[==================> ]", "[===================>]"},
+	39: {"🌍", "🌎", "🌏"},
+	40: {"◜", "◝", "◞", "◟"},
+	41: {"⬒", "⬔", "⬓", "⬕"},
+	42: {"⬖", "⬘", "⬗", "⬙"},
+	43: {"[>>>          >]", "[]>>>>        []", "[]  >>>>      []", "[]    >>>>    []", "[]      >>>>  []", "[]        >>>>[]", "[>>          >>]"},
+}
+
+func init() {
+	for i := rune(0); i < 12; i++ {
+		CharSets[37] = append(CharSets[37], string([]rune{clockOneOClock + i}))
+		CharSets[38] = append(CharSets[38], string([]rune{clockOneOClock + i}), string([]rune{clockOneThirty + i}))
+	}
+}
diff --git a/vendor/github.com/briandowns/spinner/spinner.go b/vendor/github.com/briandowns/spinner/spinner.go
new file mode 100644
index 0000000..2cd0414
--- /dev/null
+++ b/vendor/github.com/briandowns/spinner/spinner.go
@@ -0,0 +1,316 @@
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+// http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Package spinner is a simple package to add a spinner / progress indicator to any terminal application.
+package spinner
+
+import (
+	"errors"
+	"fmt"
+	"io"
+	"strconv"
+	"sync"
+	"time"
+	"unicode/utf8"
+
+	"github.com/fatih/color"
+)
+
+// errInvalidColor is returned when attempting to set an invalid color
+var errInvalidColor = errors.New("invalid color")
+
+// validColors holds an array of the only colors allowed
+var validColors = map[string]bool{
+	// default colors for backwards compatibility
+	"black":   true,
+	"red":     true,
+	"green":   true,
+	"yellow":  true,
+	"blue":    true,
+	"magenta": true,
+	"cyan":    true,
+	"white":   true,
+
+	// attributes
+	"reset":        true,
+	"bold":         true,
+	"faint":        true,
+	"italic":       true,
+	"underline":    true,
+	"blinkslow":    true,
+	"blinkrapid":   true,
+	"reversevideo": true,
+	"concealed":    true,
+	"crossedout":   true,
+
+	// foreground text
+	"fgBlack":   true,
+	"fgRed":     true,
+	"fgGreen":   true,
+	"fgYellow":  true,
+	"fgBlue":    true,
+	"fgMagenta": true,
+	"fgCyan":    true,
+	"fgWhite":   true,
+
+	// foreground Hi-Intensity text
+	"fgHiBlack":   true,
+	"fgHiRed":     true,
+	"fgHiGreen":   true,
+	"fgHiYellow":  true,
+	"fgHiBlue":    true,
+	"fgHiMagenta": true,
+	"fgHiCyan":    true,
+	"fgHiWhite":   true,
+
+	// background text
+	"bgBlack":   true,
+	"bgRed":     true,
+	"bgGreen":   true,
+	"bgYellow":  true,
+	"bgBlue":    true,
+	"bgMagenta": true,
+	"bgCyan":    true,
+	"bgWhite":   true,
+
+	// background Hi-Intensity text
+	"bgHiBlack":   true,
+	"bgHiRed":     true,
+	"bgHiGreen":   true,
+	"bgHiYellow":  true,
+	"bgHiBlue":    true,
+	"bgHiMagenta": true,
+	"bgHiCyan":    true,
+	"bgHiWhite":   true,
+}
+
+// returns a valid color's foreground text color attribute
+var colorAttributeMap = map[string]color.Attribute{
+	// default colors for backwards compatibility
+	"black":   color.FgBlack,
+	"red":     color.FgRed,
+	"green":   color.FgGreen,
+	"yellow":  color.FgYellow,
+	"blue":    color.FgBlue,
+	"magenta": color.FgMagenta,
+	"cyan":    color.FgCyan,
+	"white":   color.FgWhite,
+
+	// attributes
+	"reset":        color.Reset,
+	"bold":         color.Bold,
+	"faint":        color.Faint,
+	"italic":       color.Italic,
+	"underline":    color.Underline,
+	"blinkslow":    color.BlinkSlow,
+	"blinkrapid":   color.BlinkRapid,
+	"reversevideo": color.ReverseVideo,
+	"concealed":    color.Concealed,
+	"crossedout":   color.CrossedOut,
+
+	// foreground text colors
+	"fgBlack":   color.FgBlack,
+	"fgRed":     color.FgRed,
+	"fgGreen":   color.FgGreen,
+	"fgYellow":  color.FgYellow,
+	"fgBlue":    color.FgBlue,
+	"fgMagenta": color.FgMagenta,
+	"fgCyan":    color.FgCyan,
+	"fgWhite":   color.FgWhite,
+
+	// foreground Hi-Intensity text colors
+	"fgHiBlack":   color.FgHiBlack,
+	"fgHiRed":     color.FgHiRed,
+	"fgHiGreen":   color.FgHiGreen,
+	"fgHiYellow":  color.FgHiYellow,
+	"fgHiBlue":    color.FgHiBlue,
+	"fgHiMagenta": color.FgHiMagenta,
+	"fgHiCyan":    color.FgHiCyan,
+	"fgHiWhite":   color.FgHiWhite,
+
+	// background text colors
+	"bgBlack":   color.BgBlack,
+	"bgRed":     color.BgRed,
+	"bgGreen":   color.BgGreen,
+	"bgYellow":  color.BgYellow,
+	"bgBlue":    color.BgBlue,
+	"bgMagenta": color.BgMagenta,
+	"bgCyan":    color.BgCyan,
+	"bgWhite":   color.BgWhite,
+
+	// background Hi-Intensity text colors
+	"bgHiBlack":   color.BgHiBlack,
+	"bgHiRed":     color.BgHiRed,
+	"bgHiGreen":   color.BgHiGreen,
+	"bgHiYellow":  color.BgHiYellow,
+	"bgHiBlue":    color.BgHiBlue,
+	"bgHiMagenta": color.BgHiMagenta,
+	"bgHiCyan":    color.BgHiCyan,
+	"bgHiWhite":   color.BgHiWhite,
+}
+
+// validColor will make sure the given color is actually allowed
+func validColor(c string) bool {
+	valid := false
+	if validColors[c] {
+		valid = true
+	}
+	return valid
+}
+
+// Spinner struct to hold the provided options
+type Spinner struct {
+	Delay      time.Duration                 // Delay is the speed of the indicator
+	chars      []string                      // chars holds the chosen character set
+	Prefix     string                        // Prefix is the text preppended to the indicator
+	Suffix     string                        // Suffix is the text appended to the indicator
+	FinalMSG   string                        // string displayed after Stop() is called
+	lastOutput string                        // last character(set) written
+	color      func(a ...interface{}) string // default color is white
+	lock       *sync.RWMutex                 //
+	Writer     io.Writer                     // to make testing better, exported so users have access
+	active     bool                          // active holds the state of the spinner
+	stopChan   chan struct{}                 // stopChan is a channel used to stop the indicator
+}
+
+// New provides a pointer to an instance of Spinner with the supplied options
+func New(cs []string, d time.Duration) *Spinner {
+	return &Spinner{
+		Delay:    d,
+		chars:    cs,
+		color:    color.New(color.FgWhite).SprintFunc(),
+		lock:     &sync.RWMutex{},
+		Writer:   color.Output,
+		active:   false,
+		stopChan: make(chan struct{}, 1),
+	}
+}
+
+// Active will return whether or not the spinner is currently active
+func (s *Spinner) Active() bool {
+	return s.active
+}
+
+// Start will start the indicator
+func (s *Spinner) Start() {
+	if s.active {
+		return
+	}
+	s.active = true
+
+	go func() {
+		for {
+			for i := 0; i < len(s.chars); i++ {
+				select {
+				case <-s.stopChan:
+					return
+				default:
+					s.lock.Lock()
+					s.erase()
+					outColor := fmt.Sprintf("%s%s%s ", s.Prefix, s.color(s.chars[i]), s.Suffix)
+					outPlain := fmt.Sprintf("%s%s%s ", s.Prefix, s.chars[i], s.Suffix)
+					fmt.Fprint(s.Writer, outColor)
+					s.lastOutput = outPlain
+					delay := s.Delay
+					s.lock.Unlock()
+
+					time.Sleep(delay)
+				}
+			}
+		}
+	}()
+}
+
+// Stop stops the indicator
+func (s *Spinner) Stop() {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	if s.active {
+		s.active = false
+		s.erase()
+		if s.FinalMSG != "" {
+			fmt.Fprintf(s.Writer, s.FinalMSG)
+		}
+		s.stopChan <- struct{}{}
+	}
+}
+
+// Restart will stop and start the indicator
+func (s *Spinner) Restart() {
+	s.Stop()
+	s.Start()
+}
+
+// Reverse will reverse the order of the slice assigned to the indicator
+func (s *Spinner) Reverse() {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	for i, j := 0, len(s.chars)-1; i < j; i, j = i+1, j-1 {
+		s.chars[i], s.chars[j] = s.chars[j], s.chars[i]
+	}
+}
+
+// Color will set the struct field for the given color to be used
+func (s *Spinner) Color(colors ...string) error {
+
+	colorAttributes := make([]color.Attribute, len(colors))
+
+	// Verify colours are valid and place the appropriate attribute in the array
+	for index, c := range colors {
+		if !validColor(c) {
+			return errInvalidColor
+		}
+
+		colorAttributes[index] = colorAttributeMap[c]
+	}
+
+	s.color = color.New(colorAttributes...).SprintFunc()
+	s.Restart()
+	return nil
+}
+
+// UpdateSpeed will set the indicator delay to the given value
+func (s *Spinner) UpdateSpeed(d time.Duration) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	s.Delay = d
+}
+
+// UpdateCharSet will change the current character set to the given one
+func (s *Spinner) UpdateCharSet(cs []string) {
+	s.lock.Lock()
+	defer s.lock.Unlock()
+	s.chars = cs
+}
+
+// erase deletes written characters
+//
+// Caller must already hold s.lock.
+func (s *Spinner) erase() {
+	n := utf8.RuneCountInString(s.lastOutput)
+	for _, c := range []string{"\b", " ", "\b"} {
+		for i := 0; i < n; i++ {
+			fmt.Fprintf(s.Writer, c)
+		}
+	}
+	s.lastOutput = ""
+}
+
+// GenerateNumberSequence will generate a slice of integers at the
+// provided length and convert them each to a string
+func GenerateNumberSequence(length int) []string {
+	numSeq := make([]string, length)
+	for i := 0; i < length; i++ {
+		numSeq[i] = strconv.Itoa(i)
+	}
+	return numSeq
+}
diff --git a/vendor/github.com/fatih/color/LICENSE.md b/vendor/github.com/fatih/color/LICENSE.md
new file mode 100644
index 0000000..25fdaf6
--- /dev/null
+++ b/vendor/github.com/fatih/color/LICENSE.md
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) 2013 Fatih Arslan
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/vendor/github.com/fatih/color/color.go b/vendor/github.com/fatih/color/color.go
new file mode 100644
index 0000000..91c8e9f
--- /dev/null
+++ b/vendor/github.com/fatih/color/color.go
@@ -0,0 +1,603 @@
+package color
+
+import (
+	"fmt"
+	"io"
+	"os"
+	"strconv"
+	"strings"
+	"sync"
+
+	"github.com/mattn/go-colorable"
+	"github.com/mattn/go-isatty"
+)
+
+var (
+	// NoColor defines if the output is colorized or not. It's dynamically set to
+	// false or true based on the stdout's file descriptor referring to a terminal
+	// or not. This is a global option and affects all colors. For more control
+	// over each color block use the methods DisableColor() individually.
+	NoColor = os.Getenv("TERM") == "dumb" ||
+		(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
+
+	// Output defines the standard output of the print functions. By default
+	// os.Stdout is used.
+	Output = colorable.NewColorableStdout()
+
+	// Error defines a color supporting writer for os.Stderr.
+	Error = colorable.NewColorableStderr()
+
+	// colorsCache is used to reduce the count of created Color objects and
+	// allows to reuse already created objects with required Attribute.
+	colorsCache   = make(map[Attribute]*Color)
+	colorsCacheMu sync.Mutex // protects colorsCache
+)
+
+// Color defines a custom color object which is defined by SGR parameters.
+type Color struct {
+	params  []Attribute
+	noColor *bool
+}
+
+// Attribute defines a single SGR Code
+type Attribute int
+
+const escape = "\x1b"
+
+// Base attributes
+const (
+	Reset Attribute = iota
+	Bold
+	Faint
+	Italic
+	Underline
+	BlinkSlow
+	BlinkRapid
+	ReverseVideo
+	Concealed
+	CrossedOut
+)
+
+// Foreground text colors
+const (
+	FgBlack Attribute = iota + 30
+	FgRed
+	FgGreen
+	FgYellow
+	FgBlue
+	FgMagenta
+	FgCyan
+	FgWhite
+)
+
+// Foreground Hi-Intensity text colors
+const (
+	FgHiBlack Attribute = iota + 90
+	FgHiRed
+	FgHiGreen
+	FgHiYellow
+	FgHiBlue
+	FgHiMagenta
+	FgHiCyan
+	FgHiWhite
+)
+
+// Background text colors
+const (
+	BgBlack Attribute = iota + 40
+	BgRed
+	BgGreen
+	BgYellow
+	BgBlue
+	BgMagenta
+	BgCyan
+	BgWhite
+)
+
+// Background Hi-Intensity text colors
+const (
+	BgHiBlack Attribute = iota + 100
+	BgHiRed
+	BgHiGreen
+	BgHiYellow
+	BgHiBlue
+	BgHiMagenta
+	BgHiCyan
+	BgHiWhite
+)
+
+// New returns a newly created color object.
+func New(value ...Attribute) *Color {
+	c := &Color{params: make([]Attribute, 0)}
+	c.Add(value...)
+	return c
+}
+
+// Set sets the given parameters immediately. It will change the color of
+// output with the given SGR parameters until color.Unset() is called.
+func Set(p ...Attribute) *Color {
+	c := New(p...)
+	c.Set()
+	return c
+}
+
+// Unset resets all escape attributes and clears the output. Usually should
+// be called after Set().
+func Unset() {
+	if NoColor {
+		return
+	}
+
+	fmt.Fprintf(Output, "%s[%dm", escape, Reset)
+}
+
+// Set sets the SGR sequence.
+func (c *Color) Set() *Color {
+	if c.isNoColorSet() {
+		return c
+	}
+
+	fmt.Fprintf(Output, c.format())
+	return c
+}
+
+func (c *Color) unset() {
+	if c.isNoColorSet() {
+		return
+	}
+
+	Unset()
+}
+
+func (c *Color) setWriter(w io.Writer) *Color {
+	if c.isNoColorSet() {
+		return c
+	}
+
+	fmt.Fprintf(w, c.format())
+	return c
+}
+
+func (c *Color) unsetWriter(w io.Writer) {
+	if c.isNoColorSet() {
+		return
+	}
+
+	if NoColor {
+		return
+	}
+
+	fmt.Fprintf(w, "%s[%dm", escape, Reset)
+}
+
+// Add is used to chain SGR parameters. Use as many as parameters to combine
+// and create custom color objects. Example: Add(color.FgRed, color.Underline).
+func (c *Color) Add(value ...Attribute) *Color {
+	c.params = append(c.params, value...)
+	return c
+}
+
+func (c *Color) prepend(value Attribute) {
+	c.params = append(c.params, 0)
+	copy(c.params[1:], c.params[0:])
+	c.params[0] = value
+}
+
+// Fprint formats using the default formats for its operands and writes to w.
+// Spaces are added between operands when neither is a string.
+// It returns the number of bytes written and any write error encountered.
+// On Windows, users should wrap w with colorable.NewColorable() if w is of
+// type *os.File.
+func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
+	c.setWriter(w)
+	defer c.unsetWriter(w)
+
+	return fmt.Fprint(w, a...)
+}
+
+// Print formats using the default formats for its operands and writes to
+// standard output. Spaces are added between operands when neither is a
+// string. It returns the number of bytes written and any write error
+// encountered. This is the standard fmt.Print() method wrapped with the given
+// color.
+func (c *Color) Print(a ...interface{}) (n int, err error) {
+	c.Set()
+	defer c.unset()
+
+	return fmt.Fprint(Output, a...)
+}
+
+// Fprintf formats according to a format specifier and writes to w.
+// It returns the number of bytes written and any write error encountered.
+// On Windows, users should wrap w with colorable.NewColorable() if w is of
+// type *os.File.
+func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
+	c.setWriter(w)
+	defer c.unsetWriter(w)
+
+	return fmt.Fprintf(w, format, a...)
+}
+
+// Printf formats according to a format specifier and writes to standard output.
+// It returns the number of bytes written and any write error encountered.
+// This is the standard fmt.Printf() method wrapped with the given color.
+func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
+	c.Set()
+	defer c.unset()
+
+	return fmt.Fprintf(Output, format, a...)
+}
+
+// Fprintln formats using the default formats for its operands and writes to w.
+// Spaces are always added between operands and a newline is appended.
+// On Windows, users should wrap w with colorable.NewColorable() if w is of
+// type *os.File.
+func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
+	c.setWriter(w)
+	defer c.unsetWriter(w)
+
+	return fmt.Fprintln(w, a...)
+}
+
+// Println formats using the default formats for its operands and writes to
+// standard output. Spaces are always added between operands and a newline is
+// appended. It returns the number of bytes written and any write error
+// encountered. This is the standard fmt.Print() method wrapped with the given
+// color.
+func (c *Color) Println(a ...interface{}) (n int, err error) {
+	c.Set()
+	defer c.unset()
+
+	return fmt.Fprintln(Output, a...)
+}
+
+// Sprint is just like Print, but returns a string instead of printing it.
+func (c *Color) Sprint(a ...interface{}) string {
+	return c.wrap(fmt.Sprint(a...))
+}
+
+// Sprintln is just like Println, but returns a string instead of printing it.
+func (c *Color) Sprintln(a ...interface{}) string {
+	return c.wrap(fmt.Sprintln(a...))
+}
+
+// Sprintf is just like Printf, but returns a string instead of printing it.
+func (c *Color) Sprintf(format string, a ...interface{}) string {
+	return c.wrap(fmt.Sprintf(format, a...))
+}
+
+// FprintFunc returns a new function that prints the passed arguments as
+// colorized with color.Fprint().
+func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
+	return func(w io.Writer, a ...interface{}) {
+		c.Fprint(w, a...)
+	}
+}
+
+// PrintFunc returns a new function that prints the passed arguments as
+// colorized with color.Print().
+func (c *Color) PrintFunc() func(a ...interface{}) {
+	return func(a ...interface{}) {
+		c.Print(a...)
+	}
+}
+
+// FprintfFunc returns a new function that prints the passed arguments as
+// colorized with color.Fprintf().
+func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
+	return func(w io.Writer, format string, a ...interface{}) {
+		c.Fprintf(w, format, a...)
+	}
+}
+
+// PrintfFunc returns a new function that prints the passed arguments as
+// colorized with color.Printf().
+func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
+	return func(format string, a ...interface{}) {
+		c.Printf(format, a...)
+	}
+}
+
+// FprintlnFunc returns a new function that prints the passed arguments as
+// colorized with color.Fprintln().
+func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
+	return func(w io.Writer, a ...interface{}) {
+		c.Fprintln(w, a...)
+	}
+}
+
+// PrintlnFunc returns a new function that prints the passed arguments as
+// colorized with color.Println().
+func (c *Color) PrintlnFunc() func(a ...interface{}) {
+	return func(a ...interface{}) {
+		c.Println(a...)
+	}
+}
+
+// SprintFunc returns a new function that returns colorized strings for the
+// given arguments with fmt.Sprint(). Useful to put into or mix into other
+// string. Windows users should use this in conjunction with color.Output, example:
+//
+//	put := New(FgYellow).SprintFunc()
+//	fmt.Fprintf(color.Output, "This is a %s", put("warning"))
+func (c *Color) SprintFunc() func(a ...interface{}) string {
+	return func(a ...interface{}) string {
+		return c.wrap(fmt.Sprint(a...))
+	}
+}
+
+// SprintfFunc returns a new function that returns colorized strings for the
+// given arguments with fmt.Sprintf(). Useful to put into or mix into other
+// string. Windows users should use this in conjunction with color.Output.
+func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
+	return func(format string, a ...interface{}) string {
+		return c.wrap(fmt.Sprintf(format, a...))
+	}
+}
+
+// SprintlnFunc returns a new function that returns colorized strings for the
+// given arguments with fmt.Sprintln(). Useful to put into or mix into other
+// string. Windows users should use this in conjunction with color.Output.
+func (c *Color) SprintlnFunc() func(a ...interface{}) string {
+	return func(a ...interface{}) string {
+		return c.wrap(fmt.Sprintln(a...))
+	}
+}
+
+// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
+// an example output might be: "1;36" -> bold cyan
+func (c *Color) sequence() string {
+	format := make([]string, len(c.params))
+	for i, v := range c.params {
+		format[i] = strconv.Itoa(int(v))
+	}
+
+	return strings.Join(format, ";")
+}
+
+// wrap wraps the s string with the colors attributes. The string is ready to
+// be printed.
+func (c *Color) wrap(s string) string {
+	if c.isNoColorSet() {
+		return s
+	}
+
+	return c.format() + s + c.unformat()
+}
+
+func (c *Color) format() string {
+	return fmt.Sprintf("%s[%sm", escape, c.sequence())
+}
+
+func (c *Color) unformat() string {
+	return fmt.Sprintf("%s[%dm", escape, Reset)
+}
+
+// DisableColor disables the color output. Useful to not change any existing
+// code and still being able to output. Can be used for flags like
+// "--no-color". To enable back use EnableColor() method.
+func (c *Color) DisableColor() {
+	c.noColor = boolPtr(true)
+}
+
+// EnableColor enables the color output. Use it in conjunction with
+// DisableColor(). Otherwise this method has no side effects.
+func (c *Color) EnableColor() {
+	c.noColor = boolPtr(false)
+}
+
+func (c *Color) isNoColorSet() bool {
+	// check first if we have user setted action
+	if c.noColor != nil {
+		return *c.noColor
+	}
+
+	// if not return the global option, which is disabled by default
+	return NoColor
+}
+
+// Equals returns a boolean value indicating whether two colors are equal.
+func (c *Color) Equals(c2 *Color) bool {
+	if len(c.params) != len(c2.params) {
+		return false
+	}
+
+	for _, attr := range c.params {
+		if !c2.attrExists(attr) {
+			return false
+		}
+	}
+
+	return true
+}
+
+func (c *Color) attrExists(a Attribute) bool {
+	for _, attr := range c.params {
+		if attr == a {
+			return true
+		}
+	}
+
+	return false
+}
+
+func boolPtr(v bool) *bool {
+	return &v
+}
+
+func getCachedColor(p Attribute) *Color {
+	colorsCacheMu.Lock()
+	defer colorsCacheMu.Unlock()
+
+	c, ok := colorsCache[p]
+	if !ok {
+		c = New(p)
+		colorsCache[p] = c
+	}
+
+	return c
+}
+
+func colorPrint(format string, p Attribute, a ...interface{}) {
+	c := getCachedColor(p)
+
+	if !strings.HasSuffix(format, "\n") {
+		format += "\n"
+	}
+
+	if len(a) == 0 {
+		c.Print(format)
+	} else {
+		c.Printf(format, a...)
+	}
+}
+
+func colorString(format string, p Attribute, a ...interface{}) string {
+	c := getCachedColor(p)
+
+	if len(a) == 0 {
+		return c.SprintFunc()(format)
+	}
+
+	return c.SprintfFunc()(format, a...)
+}
+
+// Black is a convenient helper function to print with black foreground. A
+// newline is appended to format by default.
+func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
+
+// Red is a convenient helper function to print with red foreground. A
+// newline is appended to format by default.
+func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
+
+// Green is a convenient helper function to print with green foreground. A
+// newline is appended to format by default.
+func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
+
+// Yellow is a convenient helper function to print with yellow foreground.
+// A newline is appended to format by default.
+func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
+
+// Blue is a convenient helper function to print with blue foreground. A
+// newline is appended to format by default.
+func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
+
+// Magenta is a convenient helper function to print with magenta foreground.
+// A newline is appended to format by default.
+func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
+
+// Cyan is a convenient helper function to print with cyan foreground. A
+// newline is appended to format by default.
+func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
+
+// White is a convenient helper function to print with white foreground. A
+// newline is appended to format by default.
+func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
+
+// BlackString is a convenient helper function to return a string with black
+// foreground.
+func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
+
+// RedString is a convenient helper function to return a string with red
+// foreground.
+func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
+
+// GreenString is a convenient helper function to return a string with green
+// foreground.
+func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
+
+// YellowString is a convenient helper function to return a string with yellow
+// foreground.
+func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
+
+// BlueString is a convenient helper function to return a string with blue
+// foreground.
+func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
+
+// MagentaString is a convenient helper function to return a string with magenta
+// foreground.
+func MagentaString(format string, a ...interface{}) string {
+	return colorString(format, FgMagenta, a...)
+}
+
+// CyanString is a convenient helper function to return a string with cyan
+// foreground.
+func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
+
+// WhiteString is a convenient helper function to return a string with white
+// foreground.
+func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
+
+// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
+// newline is appended to format by default.
+func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
+
+// HiRed is a convenient helper function to print with hi-intensity red foreground. A
+// newline is appended to format by default.
+func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
+
+// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
+// newline is appended to format by default.
+func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
+
+// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
+// A newline is appended to format by default.
+func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
+
+// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
+// newline is appended to format by default.
+func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
+
+// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
+// A newline is appended to format by default.
+func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
+
+// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
+// newline is appended to format by default.
+func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
+
+// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
+// newline is appended to format by default.
+func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
+
+// HiBlackString is a convenient helper function to return a string with hi-intensity black
+// foreground.
+func HiBlackString(format string, a ...interface{}) string {
+	return colorString(format, FgHiBlack, a...)
+}
+
+// HiRedString is a convenient helper function to return a string with hi-intensity red
+// foreground.
+func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
+
+// HiGreenString is a convenient helper function to return a string with hi-intensity green
+// foreground.
+func HiGreenString(format string, a ...interface{}) string {
+	return colorString(format, FgHiGreen, a...)
+}
+
+// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
+// foreground.
+func HiYellowString(format string, a ...interface{}) string {
+	return colorString(format, FgHiYellow, a...)
+}
+
+// HiBlueString is a convenient helper function to return a string with hi-intensity blue
+// foreground.
+func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
+
+// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
+// foreground.
+func HiMagentaString(format string, a ...interface{}) string {
+	return colorString(format, FgHiMagenta, a...)
+}
+
+// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
+// foreground.
+func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
+
+// HiWhiteString is a convenient helper function to return a string with hi-intensity white
+// foreground.
+func HiWhiteString(format string, a ...interface{}) string {
+	return colorString(format, FgHiWhite, a...)
+}