1
0
mirror of https://github.com/minio/mc.git synced 2025-11-12 01:02:26 +03:00

Refactoring console printing style, supports --json even for error messages now - fixes #418

This commit is contained in:
Harshavardhana
2015-05-22 18:15:16 -07:00
parent 190afa70a1
commit 54d0f9c553
16 changed files with 428 additions and 270 deletions

View File

@@ -17,6 +17,7 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"time" "time"
@@ -31,42 +32,51 @@ func runAccessCmd(ctx *cli.Context) {
cli.ShowCommandHelpAndExit(ctx, "access", 1) // last argument is exit code cli.ShowCommandHelpAndExit(ctx, "access", 1) // last argument is exit code
} }
if !isMcConfigExist() { if !isMcConfigExist() {
console.Fatalln("\"mc\" is not configured. Please run \"mc config generate\".") console.Fatalln(console.ErrorMessage{
Message: "Please run \"mc config generate\"",
Error: iodine.New(errors.New("\"mc\" is not configured"), nil),
})
} }
config, err := getMcConfig() config, err := getMcConfig()
if err != nil { if err != nil {
console.Fatalf("loading config file failed with following reason: [%s]\n", iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: "loading config file failed",
Error: iodine.New(err, nil),
})
} }
targetURLConfigMap := make(map[string]*hostConfig) targetURLConfigMap := make(map[string]*hostConfig)
targetURLs, err := getExpandedURLs(ctx.Args(), config.Aliases) targetURLs, err := getExpandedURLs(ctx.Args(), config.Aliases)
if err != nil { if err != nil {
switch e := iodine.ToError(err).(type) { console.Fatalln(console.ErrorMessage{
case errUnsupportedScheme: Message: "Unknown type of URL ",
console.Fatalf("Unknown type of URL %s. Reason: %s.\n", e.url, e) Error: iodine.New(err, nil),
default: })
console.Fatalf("reading URLs failed with following Reason: %s\n", e)
}
} }
acl := bucketACL(ctx.Args().First()) acl := bucketACL(ctx.Args().First())
if !acl.isValidBucketACL() { if !acl.isValidBucketACL() {
console.Fatalf("Access type %s is not supported. Valid types are [private, public, readonly].\n", acl) console.Fatalln(console.ErrorMessage{
Message: "Valid types are [private, public, readonly].",
Error: iodine.New(errors.New("Invalid ACL Type "+acl.String()+""), nil),
})
} }
targetURLs = targetURLs[1:] // 1 or more target URLs targetURLs = targetURLs[1:] // 1 or more target URLs
for _, targetURL := range targetURLs { for _, targetURL := range targetURLs {
targetConfig, err := getHostConfig(targetURL) targetConfig, err := getHostConfig(targetURL)
if err != nil { if err != nil {
console.Fatalf("Unable to read configuration for host %s. Reason: %s.\n", targetURL, iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: "Unable to read configuration for host " + "" + targetURL + "",
Error: iodine.New(err, nil),
})
} }
targetURLConfigMap[targetURL] = targetConfig targetURLConfigMap[targetURL] = targetConfig
} }
for targetURL, targetConfig := range targetURLConfigMap { for targetURL, targetConfig := range targetURLConfigMap {
errorMsg, err := doUpdateAccessCmd(targetURL, acl.String(), targetConfig) errorMsg, err := doUpdateAccessCmd(targetURL, acl.String(), targetConfig)
err = iodine.New(err, nil)
if err != nil { if err != nil {
if errorMsg == "" { console.Errorln(console.ErrorMessage{
errorMsg = "Empty error message. Please rerun this command with --debug and file a bug report." Message: errorMsg,
} Error: iodine.New(err, nil),
console.Errorf("%s", errorMsg) })
} }
} }
} }
@@ -76,10 +86,8 @@ func doUpdateAccessCmd(targetURL, targetACL string, targetConfig *hostConfig) (s
var clnt client.Client var clnt client.Client
clnt, err = getNewClient(targetURL, targetConfig) clnt, err = getNewClient(targetURL, targetConfig)
if err != nil { if err != nil {
err := iodine.New(err, nil) msg := fmt.Sprintf("Unable to initialize client for %s", targetURL)
msg := fmt.Sprintf("Unable to initialize client for %s. Reason: %s.\n", return msg, iodine.New(err, nil)
targetURL, iodine.ToError(err))
return msg, err
} }
return doUpdateAccess(clnt, targetURL, targetACL) return doUpdateAccess(clnt, targetURL, targetACL)
} }
@@ -87,15 +95,14 @@ func doUpdateAccessCmd(targetURL, targetACL string, targetConfig *hostConfig) (s
func doUpdateAccess(clnt client.Client, targetURL, targetACL string) (string, error) { func doUpdateAccess(clnt client.Client, targetURL, targetACL string) (string, error) {
err := clnt.SetBucketACL(targetACL) err := clnt.SetBucketACL(targetACL)
for i := 0; i < globalMaxRetryFlag && err != nil && isValidRetry(err); i++ { for i := 0; i < globalMaxRetryFlag && err != nil && isValidRetry(err); i++ {
fmt.Println(console.Retry("Retrying ... %d", i)) console.Retry("Retrying ...", i)
// Progressively longer delays // Progressively longer delays
time.Sleep(time.Duration(i*i) * time.Second) time.Sleep(time.Duration(i*i) * time.Second)
err = clnt.SetBucketACL(targetACL) err = clnt.SetBucketACL(targetACL)
} }
if err != nil { if err != nil {
err := iodine.New(err, nil) msg := fmt.Sprintf("Failed to add bucket access policy for URL %s", targetURL)
msg := fmt.Sprintf("Failed to add bucket access policy for URL %s. Reason: %s.\n", targetURL, iodine.ToError(err)) return msg, iodine.New(err, nil)
return msg, err
} }
return "", nil return "", nil
} }

View File

@@ -18,6 +18,7 @@ package main
import ( import (
"errors" "errors"
"fmt"
"io" "io"
"os" "os"
@@ -31,33 +32,42 @@ func runCatCmd(ctx *cli.Context) {
cli.ShowCommandHelpAndExit(ctx, "cat", 1) // last argument is exit code cli.ShowCommandHelpAndExit(ctx, "cat", 1) // last argument is exit code
} }
if !isMcConfigExist() { if !isMcConfigExist() {
console.Fatalln("\"mc\" is not configured. Please run \"mc config generate\".") console.Fatalln(console.ErrorMessage{
Message: "Please run \"mc config generate\"",
Error: iodine.New(errors.New("\"mc\" is not configured"), nil),
})
} }
config, err := getMcConfig() config, err := getMcConfig()
if err != nil { if err != nil {
console.Fatalf("Unable to read config file %s. Reason: %s.\n", mustGetMcConfigPath(), iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to read config file %s", mustGetMcConfigPath()),
Error: iodine.New(err, nil),
})
} }
// Convert arguments to URLs: expand alias, fix format... // Convert arguments to URLs: expand alias, fix format...
urls, err := getExpandedURLs(ctx.Args(), config.Aliases) urls, err := getExpandedURLs(ctx.Args(), config.Aliases)
if err != nil { if err != nil {
switch e := iodine.ToError(err).(type) { console.Fatalln(console.ErrorMessage{
case errUnsupportedScheme: Message: fmt.Sprintf("Unknown type of URL %s", urls),
console.Fatalf("Unknown type of URL %s. Reason: %s.\n", e.url, e) Error: iodine.New(err, nil),
default: })
console.Fatalf("Unable to parse arguments. Reason: %s.\n", iodine.ToError(err))
}
} }
sourceURLs := urls sourceURLs := urls
sourceURLConfigMap, err := getHostConfigs(sourceURLs) sourceURLConfigMap, err := getHostConfigs(sourceURLs)
if err != nil { if err != nil {
console.Fatalf("Unable to read host configuration for %s from config file %s. Reason: %s.\n", console.Fatalln(console.ErrorMessage{
sourceURLs, mustGetMcConfigPath(), iodine.ToError(err)) Message: fmt.Sprintf("Unable to read host configuration for %s from config file %s", sourceURLs, mustGetMcConfigPath()),
Error: iodine.New(err, nil),
})
} }
humanReadable, err := doCatCmd(sourceURLConfigMap) humanReadable, err := doCatCmd(sourceURLConfigMap)
if err != nil { if err != nil {
console.Fatalln(humanReadable) console.Fatalln(console.ErrorMessage{
Message: humanReadable,
Error: iodine.New(err, nil),
})
} }
} }

View File

@@ -34,13 +34,19 @@ func runConfigCmd(ctx *cli.Context) {
arg := ctx.Args().First() arg := ctx.Args().First()
tailArgs := ctx.Args().Tail() tailArgs := ctx.Args().Tail()
if len(tailArgs) > 2 { if len(tailArgs) > 2 {
console.Fatalln("Incorrect number of arguments, please use \"mc config help\"") console.Fatalln(console.ErrorMessage{
Message: "Incorrect number of arguments, please use \"mc config help\"",
Error: iodine.New(errInvalidArgument{}, nil),
})
} }
msg, err := doConfig(arg, tailArgs) msg, err := doConfig(arg, tailArgs)
if err != nil { if err != nil {
console.Fatalln(msg) console.Fatalln(console.ErrorMessage{
Message: msg,
Error: err,
})
} }
console.Infoln(msg) console.Infoln(console.Message(msg))
} }
// saveConfig writes configuration data in json format to config file. // saveConfig writes configuration data in json format to config file.

View File

@@ -17,6 +17,8 @@
package main package main
import ( import (
"errors"
"fmt"
"runtime" "runtime"
"sync" "sync"
@@ -33,7 +35,7 @@ func doCopy(sourceURL string, sourceConfig *hostConfig, targetURL string, target
} }
switch globalQuietFlag { switch globalQuietFlag {
case true: case true:
console.Infof("%s -> %s\n", sourceURL, targetURL) console.Infoln(console.Message(fmt.Sprintf("%s -> %s", sourceURL, targetURL)))
default: default:
// set up progress // set up progress
reader = bar.NewProxyReader(reader) reader = bar.NewProxyReader(reader)
@@ -117,13 +119,21 @@ func runCopyCmd(ctx *cli.Context) {
if len(ctx.Args()) < 2 || ctx.Args().First() == "help" { if len(ctx.Args()) < 2 || ctx.Args().First() == "help" {
cli.ShowCommandHelpAndExit(ctx, "cp", 1) // last argument is exit code cli.ShowCommandHelpAndExit(ctx, "cp", 1) // last argument is exit code
} }
if !isMcConfigExist() { if !isMcConfigExist() {
console.Fatalln("\"mc\" is not configured. Please run \"mc config generate\".") console.Fatalln(console.ErrorMessage{
Message: "Please run \"mc config generate\"",
Error: iodine.New(errors.New("\"mc\" is not configured"), nil),
})
} }
// extract URLs. // extract URLs.
URLs, err := args2URLs(ctx.Args()) URLs, err := args2URLs(ctx.Args())
if err != nil { if err != nil {
console.Fatalln(iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unknown URL types: %s", URLs),
Error: iodine.New(err, nil),
})
} }
// Separate source and target. 'cp' can take only one target, // Separate source and target. 'cp' can take only one target,
@@ -138,7 +148,10 @@ func runCopyCmd(ctx *cli.Context) {
} }
for err := range doCopyCmd(sourceURLs, targetURL, bar) { for err := range doCopyCmd(sourceURLs, targetURL, bar) {
if err != nil { if err != nil {
console.Errorln(iodine.ToError(err)) console.Errorln(console.ErrorMessage{
Message: "Failed with",
Error: iodine.New(err, nil),
})
} }
} }
if !globalQuietFlag { if !globalQuietFlag {

View File

@@ -17,6 +17,9 @@
package main package main
import ( import (
"errors"
"fmt"
"github.com/minio/cli" "github.com/minio/cli"
"github.com/minio/mc/pkg/console" "github.com/minio/mc/pkg/console"
"github.com/minio/minio/pkg/iodine" "github.com/minio/minio/pkg/iodine"
@@ -28,11 +31,17 @@ func runDiffCmd(ctx *cli.Context) {
cli.ShowCommandHelpAndExit(ctx, "diff", 1) // last argument is exit code cli.ShowCommandHelpAndExit(ctx, "diff", 1) // last argument is exit code
} }
if !isMcConfigExist() { if !isMcConfigExist() {
console.Fatalln("\"mc\" is not configured. Please run \"mc config generate\".") console.Fatalln(console.ErrorMessage{
Message: "Please run \"mc config generate\"",
Error: iodine.New(errors.New("\"mc\" is not configured"), nil),
})
} }
config, err := getMcConfig() config, err := getMcConfig()
if err != nil { if err != nil {
console.Fatalf("Unable to read config file %s. Reason: %s.\n", mustGetMcConfigPath(), iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to read config file %s", mustGetMcConfigPath()),
Error: err,
})
} }
firstURL := ctx.Args().First() firstURL := ctx.Args().First()
@@ -42,40 +51,59 @@ func runDiffCmd(ctx *cli.Context) {
if err != nil { if err != nil {
switch iodine.ToError(err).(type) { switch iodine.ToError(err).(type) {
case errUnsupportedScheme: case errUnsupportedScheme:
console.Fatalf("Unknown type of URL %s.\n", firstURL) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unknown type of URL %s.", firstURL),
Error: err,
})
default: default:
console.Fatalf("Unable to parse argument %s. Reason: %s.\n", firstURL, iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to parse argument %s.", firstURL),
Error: err,
})
} }
} }
_, err = getHostConfig(firstURL) _, err = getHostConfig(firstURL)
if err != nil { if err != nil {
console.Fatalf("Unable to read host configuration for %s from config file %s. Reason: %s.\n", console.Fatalln(console.ErrorMessage{
firstURL, mustGetMcConfigPath(), iodine.ToError(err)) Message: fmt.Sprintf("Unable to read host configuration for %s from config file %s.", firstURL, mustGetMcConfigPath()),
Error: err,
})
} }
_, err = getHostConfig(secondURL) _, err = getHostConfig(secondURL)
if err != nil { if err != nil {
console.Fatalf("Unable to read host configuration for %s from config file %s. Reason: %s.\n", console.Fatalln(console.ErrorMessage{
secondURL, mustGetMcConfigPath(), iodine.ToError(err)) Message: fmt.Sprintf("Unable to read host configuration for %s from config file %s. Reason: %s.", secondURL, mustGetMcConfigPath()),
Error: err,
})
} }
secondURL, err = getExpandedURL(secondURL, config.Aliases) secondURL, err = getExpandedURL(secondURL, config.Aliases)
if err != nil { if err != nil {
switch iodine.ToError(err).(type) { switch iodine.ToError(err).(type) {
case errUnsupportedScheme: case errUnsupportedScheme:
console.Fatalf("Unknown type of URL %s. Reason: %s.\n", secondURL, iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unknown type of URL %s.", secondURL),
Error: err,
})
default: default:
console.Fatalf("Unable to parse argument %s. Reason: %s.\n", secondURL, iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to parse argument %s.", secondURL),
Error: err,
})
} }
} }
// TODO recursive is not working yet // TODO recursive is not working yet
newFirstURL := stripRecursiveURL(firstURL) newFirstURL := stripRecursiveURL(firstURL)
for diff := range doDiffCmd(newFirstURL, secondURL, isURLRecursive(firstURL)) { for diff := range doDiffCmd(newFirstURL, secondURL, isURLRecursive(firstURL)) {
if diff.err != nil { if diff.err != nil {
console.Fatalln(diff.message) console.Fatalln(console.ErrorMessage{
Message: diff.message,
Error: diff.err,
})
} }
console.Infoln(diff.message) console.Infoln(console.Message(diff.message))
} }
} }

View File

@@ -17,6 +17,9 @@
package main package main
import ( import (
"errors"
"fmt"
"github.com/minio/cli" "github.com/minio/cli"
"github.com/minio/mc/pkg/console" "github.com/minio/mc/pkg/console"
"github.com/minio/minio/pkg/iodine" "github.com/minio/minio/pkg/iodine"
@@ -27,12 +30,20 @@ func runListCmd(ctx *cli.Context) {
if !ctx.Args().Present() || ctx.Args().First() == "help" { if !ctx.Args().Present() || ctx.Args().First() == "help" {
cli.ShowCommandHelpAndExit(ctx, "ls", 1) // last argument is exit code cli.ShowCommandHelpAndExit(ctx, "ls", 1) // last argument is exit code
} }
if !isMcConfigExist() { if !isMcConfigExist() {
console.Fatalln("\"mc\" is not configured. Please run \"mc config generate\".") console.Fatalln(console.ErrorMessage{
Message: "Please run \"mc config generate\"",
Error: iodine.New(errors.New("\"mc\" is not configured"), nil),
})
} }
config, err := getMcConfig() config, err := getMcConfig()
if err != nil { if err != nil {
console.Fatalf("Unable to read config file %s. Reason: %s.\n", mustGetMcConfigPath(), iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to read config file %s", mustGetMcConfigPath()),
Error: iodine.New(err, nil),
})
} }
targetURLConfigMap := make(map[string]*hostConfig) targetURLConfigMap := make(map[string]*hostConfig)
for _, arg := range ctx.Args() { for _, arg := range ctx.Args() {
@@ -40,15 +51,23 @@ func runListCmd(ctx *cli.Context) {
if err != nil { if err != nil {
switch e := iodine.ToError(err).(type) { switch e := iodine.ToError(err).(type) {
case errUnsupportedScheme: case errUnsupportedScheme:
console.Fatalf("Unknown type of URL %s. Reason: %s.\n", e.url, e) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unknown type of URL %s", e.url),
Error: iodine.New(e, nil),
})
default: default:
console.Fatalf("Unable to parse argument %s. Reason: %s.\n", arg, iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to parse argument %s", arg),
Error: iodine.New(err, nil),
})
} }
} }
targetConfig, err := getHostConfig(targetURL) targetConfig, err := getHostConfig(targetURL)
if err != nil { if err != nil {
console.Fatalf("Unable to read host configuration for %s from config file %s. Reason: %s.\n", console.Fatalln(console.ErrorMessage{
targetURL, mustGetMcConfigPath(), iodine.ToError(err)) Message: fmt.Sprintf("Unable to read host configuration for %s from config file %s", targetURL, mustGetMcConfigPath()),
Error: iodine.New(err, nil),
})
} }
targetURLConfigMap[targetURL] = targetConfig targetURLConfigMap[targetURL] = targetConfig
} }
@@ -56,9 +75,11 @@ func runListCmd(ctx *cli.Context) {
// if recursive strip off the "..." // if recursive strip off the "..."
newTargetURL := stripRecursiveURL(targetURL) newTargetURL := stripRecursiveURL(targetURL)
err = doListCmd(newTargetURL, targetConfig, isURLRecursive(targetURL)) err = doListCmd(newTargetURL, targetConfig, isURLRecursive(targetURL))
err = iodine.New(err, nil)
if err != nil { if err != nil {
console.Fatalf("Failed to list %s. Reason: %s.\n", targetURL, iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Failed to list %s", targetURL),
Error: iodine.New(err, nil),
})
} }
} }
} }

View File

@@ -17,6 +17,7 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"time" "time"
@@ -32,37 +33,51 @@ func runMakeBucketCmd(ctx *cli.Context) {
cli.ShowCommandHelpAndExit(ctx, "mb", 1) // last argument is exit code cli.ShowCommandHelpAndExit(ctx, "mb", 1) // last argument is exit code
} }
if !isMcConfigExist() { if !isMcConfigExist() {
console.Fatalln("\"mc\" is not configured. Please run \"mc config generate\".") console.Fatalln(console.ErrorMessage{
Message: "Please run \"mc config generate\"",
Error: errors.New("\"mc\" is not configured"),
})
} }
config, err := getMcConfig() config, err := getMcConfig()
if err != nil { if err != nil {
console.Fatalf("Unable to read config file %s. Reason: %s\n", mustGetMcConfigPath(), iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: "Unable to read config file " + mustGetMcConfigPath() + "",
Error: err,
})
} }
targetURLConfigMap := make(map[string]*hostConfig) targetURLConfigMap := make(map[string]*hostConfig)
targetURLs, err := getExpandedURLs(ctx.Args(), config.Aliases) for _, arg := range ctx.Args() {
if err != nil { targetURL, err := getExpandedURL(arg, config.Aliases)
switch e := iodine.ToError(err).(type) { if err != nil {
case errUnsupportedScheme: switch e := iodine.ToError(err).(type) {
console.Fatalf("Unknown URL type %s passed. Reason: %s.\n", e.url, e) case errUnsupportedScheme:
default: console.Fatalln(console.ErrorMessage{
console.Fatalf("Error in parsing path or URL. Reason: %s.\n", e) Message: fmt.Sprintf("Unknown type of URL %s", e.url),
Error: e,
})
default:
console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to parse argument %s", arg),
Error: err,
})
}
} }
}
for _, targetURL := range targetURLs {
targetConfig, err := getHostConfig(targetURL) targetConfig, err := getHostConfig(targetURL)
if err != nil { if err != nil {
console.Fatalf("Unable to read configuration for host %s. Reason: %s.\n", targetURL, iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to read host configuration for %s from config file %s", targetURL, mustGetMcConfigPath()),
Error: err,
})
} }
targetURLConfigMap[targetURL] = targetConfig targetURLConfigMap[targetURL] = targetConfig
} }
for targetURL, targetConfig := range targetURLConfigMap { for targetURL, targetConfig := range targetURLConfigMap {
errorMsg, err := doMakeBucketCmd(targetURL, targetConfig) errorMsg, err := doMakeBucketCmd(targetURL, targetConfig)
err = iodine.New(err, nil)
if err != nil { if err != nil {
if errorMsg == "" { console.Errorln(console.ErrorMessage{
errorMsg = "Empty error message. Please rerun this command with --debug and file a bug report." Message: errorMsg,
} Error: err,
console.Errorf("%s", errorMsg) })
} }
} }
} }
@@ -73,10 +88,8 @@ func doMakeBucketCmd(targetURL string, targetConfig *hostConfig) (string, error)
var clnt client.Client var clnt client.Client
clnt, err = getNewClient(targetURL, targetConfig) clnt, err = getNewClient(targetURL, targetConfig)
if err != nil { if err != nil {
err := iodine.New(err, nil) msg := fmt.Sprintf("Unable to initialize client for %s", targetURL)
msg := fmt.Sprintf("Unable to initialize client for %s. Reason: %s.\n", return msg, iodine.New(err, nil)
targetURL, iodine.ToError(err))
return msg, err
} }
return doMakeBucket(clnt, targetURL) return doMakeBucket(clnt, targetURL)
} }
@@ -85,15 +98,14 @@ func doMakeBucketCmd(targetURL string, targetConfig *hostConfig) (string, error)
func doMakeBucket(clnt client.Client, targetURL string) (string, error) { func doMakeBucket(clnt client.Client, targetURL string) (string, error) {
err := clnt.MakeBucket() err := clnt.MakeBucket()
for i := 0; i < globalMaxRetryFlag && err != nil && isValidRetry(err); i++ { for i := 0; i < globalMaxRetryFlag && err != nil && isValidRetry(err); i++ {
fmt.Println(console.Retry("Retrying ... %d", i)) console.Retry("Retrying ...", i)
// Progressively longer delays // Progressively longer delays
time.Sleep(time.Duration(i*i) * time.Second) time.Sleep(time.Duration(i*i) * time.Second)
err = clnt.MakeBucket() err = clnt.MakeBucket()
} }
if err != nil { if err != nil {
err := iodine.New(err, nil) msg := fmt.Sprintf("Failed to create bucket for URL %s", targetURL)
msg := fmt.Sprintf("Failed to create bucket for URL %s. Reason: %s.\n", targetURL, iodine.ToError(err)) return msg, iodine.New(err, nil)
return msg, err
} }
return "", nil return "", nil
} }

View File

@@ -17,6 +17,8 @@
package main package main
import ( import (
"errors"
"fmt"
"runtime" "runtime"
"sync" "sync"
@@ -29,12 +31,20 @@ func runSyncCmd(ctx *cli.Context) {
if len(ctx.Args()) < 2 || ctx.Args().First() == "help" { if len(ctx.Args()) < 2 || ctx.Args().First() == "help" {
cli.ShowCommandHelpAndExit(ctx, "sync", 1) // last argument is exit code cli.ShowCommandHelpAndExit(ctx, "sync", 1) // last argument is exit code
} }
if !isMcConfigExist() { if !isMcConfigExist() {
console.Fatalln("\"mc\" is not configured. Please run \"mc config generate\".") console.Fatalln(console.ErrorMessage{
Message: "Please run \"mc config generate\"",
Error: iodine.New(errors.New("\"mc\" is not configured"), nil),
})
} }
URLs, err := args2URLs(ctx.Args()) URLs, err := args2URLs(ctx.Args())
if err != nil { if err != nil {
console.Fatalln(iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unknown URL types found: %s", URLs),
Error: iodine.New(err, nil),
})
} }
// Separate source and target. 'sync' can take only one source. // Separate source and target. 'sync' can take only one source.
@@ -66,7 +76,10 @@ func runSyncCmd(ctx *cli.Context) {
for syncURLs := range prepareSyncURLs(sourceURL, targetURLs) { for syncURLs := range prepareSyncURLs(sourceURL, targetURLs) {
if syncURLs.Error != nil { if syncURLs.Error != nil {
console.Errorln(iodine.ToError(syncURLs.Error)) console.Errorln(console.ErrorMessage{
Message: "Failed with",
Error: iodine.New(syncURLs.Error, nil),
})
continue continue
} }
syncQueue <- true syncQueue <- true
@@ -75,16 +88,25 @@ func runSyncCmd(ctx *cli.Context) {
defer wg.Done() defer wg.Done()
srcConfig, err := getHostConfig(syncURLs.SourceContent.Name) srcConfig, err := getHostConfig(syncURLs.SourceContent.Name)
if err != nil { if err != nil {
console.Errorln(iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: "Failed with",
Error: iodine.New(err, nil),
})
return return
} }
tgtConfig, err := getHostConfig(syncURLs.TargetContent.Name) tgtConfig, err := getHostConfig(syncURLs.TargetContent.Name)
if err != nil { if err != nil {
console.Errorln(iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: "Failed with",
Error: iodine.New(err, nil),
})
return return
} }
if err := doCopy(syncURLs.SourceContent.Name, srcConfig, syncURLs.TargetContent.Name, tgtConfig, bar); err != nil { if err := doCopy(syncURLs.SourceContent.Name, srcConfig, syncURLs.TargetContent.Name, tgtConfig, bar); err != nil {
console.Errorln(iodine.ToError(err)) console.Errorln(console.ErrorMessage{
Message: "Failed with",
Error: iodine.New(err, nil),
})
} }
<-syncQueue <-syncQueue
}(syncURLs, &bar) }(syncURLs, &bar)

View File

@@ -17,6 +17,8 @@
package main package main
import ( import (
"errors"
"fmt"
"runtime" "runtime"
"time" "time"
@@ -56,17 +58,24 @@ func runUpdateCmd(ctx *cli.Context) {
cli.ShowCommandHelpAndExit(ctx, "update", 1) // last argument is exit code cli.ShowCommandHelpAndExit(ctx, "update", 1) // last argument is exit code
} }
if !isMcConfigExist() { if !isMcConfigExist() {
console.Fatalln("\"mc\" is not configured. Please run \"mc config generate\".") console.Fatalln(console.ErrorMessage{
Message: "Please run \"mc config generate\"",
Error: iodine.New(errors.New("\"mc\" is not configured"), nil),
})
} }
hostConfig, err := getHostConfig(mcUpdateURL) hostConfig, err := getHostConfig(mcUpdateURL)
if err != nil { if err != nil {
console.Fatalf("Unable to read configuration for host %s. Reason: %s.\n", mcUpdateURL, iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to read configuration for host %s", mcUpdateURL),
Error: iodine.New(err, nil),
})
} }
msg, err := doUpdateCheck(hostConfig) msg, err := doUpdateCheck(hostConfig)
if err != nil { if err != nil {
console.Fatalln(msg) console.Fatalln(console.ErrorMessage{
} Message: msg,
if msg != "" { Error: iodine.New(err, nil),
console.Infoln(msg) })
} }
console.Infoln(console.Message(msg))
} }

View File

@@ -125,7 +125,7 @@ func getMcConfig() (*configV1, error) {
} }
// isMcConfigExist returns true/false if config exists // isMcConfigExist returns err if config doesn't exist
func isMcConfigExist() bool { func isMcConfigExist() bool {
configFile, err := getMcConfigPath() configFile, err := getMcConfigPath()
if err != nil { if err != nil {

59
ls.go
View File

@@ -17,11 +17,7 @@
package main package main
import ( import (
"encoding/json"
"fmt"
"os"
"strings" "strings"
"time"
"github.com/dustin/go-humanize" "github.com/dustin/go-humanize"
"github.com/minio/mc/pkg/client" "github.com/minio/mc/pkg/client"
@@ -36,49 +32,22 @@ const (
printDate = "2006-01-02 15:04:05 MST" printDate = "2006-01-02 15:04:05 MST"
) )
// printJSON rather than colored output // printContent prints content meta-data
func printJSON(content *client.Content) { func printContent(c *client.Content) {
type jsonContent struct { content := console.Content{}
Filetype string `json:"content-type"` content.Filetype = func() string {
Date string `json:"last-modified"` if c.Type.IsDir() {
Size string `json:"size"`
Name string `json:"name"`
}
contentJSON := new(jsonContent)
contentJSON.Date = content.Time.Local().Format(printDate)
contentJSON.Size = humanize.IBytes(uint64(content.Size))
contentJSON.Filetype = func() string {
if content.Type.IsDir() {
return "inode/directory" return "inode/directory"
} }
if content.Type.IsRegular() { if c.Type.IsRegular() {
return "application/octet-stream" return "application/octet-stream"
} }
return "application/octet-stream" return "application/octet-stream"
}() }()
contentJSON.Name = content.Name content.Size = humanize.IBytes(uint64(c.Size))
contentBytes, _ := json.MarshalIndent(contentJSON, "", "\t") content.Name = strings.TrimSuffix(c.Name, "/")
fmt.Println(string(contentBytes)) content.Time = c.Time.Local().Format(printDate)
} console.ContentInfo(content)
// printContent prints content meta-data
func printContent(date time.Time, size int64, name string, fileType os.FileMode) {
fmt.Printf(console.Time("[%s] ", date.Local().Format(printDate)))
fmt.Printf(console.Size("%6s ", humanize.IBytes(uint64(size))))
// just making it explicit
switch {
case fileType.IsDir() == true:
// if one finds a prior suffix no need to append a new one
switch {
case strings.HasSuffix(name, "/") == true:
fmt.Println(console.Dir("%s", name))
default:
fmt.Println(console.Dir("%s/", name))
}
default:
fmt.Println(console.File("%s", name))
}
} }
// doList - list all entities inside a folder // doList - list all entities inside a folder
@@ -97,12 +66,8 @@ func doList(clnt client.Client, targetURL string, recursive bool) error {
// To be consistent we have to filter them out // To be consistent we have to filter them out
contentName = strings.TrimPrefix(contentName, strings.TrimSuffix(targetURL, "/")+"/") contentName = strings.TrimPrefix(contentName, strings.TrimSuffix(targetURL, "/")+"/")
} }
switch { contentCh.Content.Name = contentName
case globalJSONFlag == true: printContent(contentCh.Content)
printJSON(contentCh.Content)
default:
printContent(contentCh.Content.Time, contentCh.Content.Size, contentName, contentCh.Content.Type)
}
} }
if err != nil { if err != nil {
return iodine.New(err, map[string]string{"Target": targetURL}) return iodine.New(err, map[string]string{"Target": targetURL})

29
main.go
View File

@@ -17,6 +17,7 @@
package main package main
import ( import (
"errors"
"fmt" "fmt"
"os" "os"
"os/user" "os/user"
@@ -34,7 +35,10 @@ import (
func checkConfig() { func checkConfig() {
_, err := user.Current() _, err := user.Current()
if err != nil { if err != nil {
console.Fatalln("Unable to determine current user") console.Fatalln(console.ErrorMessage{
Message: "Unable to determine current user",
Error: err,
})
} }
// If config doesn't exist, do not attempt to read it // If config doesn't exist, do not attempt to read it
@@ -45,7 +49,10 @@ func checkConfig() {
// Ensures config file is sane // Ensures config file is sane
_, err = getMcConfig() _, err = getMcConfig()
if err != nil { if err != nil {
console.Fatalf("Unable to read config file: %s\n", mustGetMcConfigPath()) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to read config file: %s", mustGetMcConfigPath()),
Error: err,
})
} }
} }
@@ -107,14 +114,23 @@ func main() {
app.ExtraInfo = getSystemData() app.ExtraInfo = getSystemData()
console.NoDebugPrint = false console.NoDebugPrint = false
} }
if globalJSONFlag {
console.NoJSONPrint = false
}
themeName := ctx.GlobalString("theme") themeName := ctx.GlobalString("theme")
switch { switch {
case console.IsValidTheme(themeName) != true: case console.IsValidTheme(themeName) != true:
console.Fatalf("Theme %s is not supported. Please choose from this list: %s.\n", themeName, console.GetThemeNames()) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Please choose from this list: %s.", console.GetThemeNames()),
Error: fmt.Errorf("Theme %s is not supported.", themeName),
})
default: default:
err := console.SetTheme(themeName) err := console.SetTheme(themeName)
if err != nil { if err != nil {
console.Fatalf("Failed to set theme %s. Reason: %s.\n", themeName, iodine.ToError(err)) console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Failed to set theme %s.", themeName),
Error: err,
})
} }
} }
checkConfig() checkConfig()
@@ -122,7 +138,10 @@ func main() {
} }
app.After = func(ctx *cli.Context) error { app.After = func(ctx *cli.Context) error {
if !isMcConfigExist() { if !isMcConfigExist() {
console.Fatalln("\"mc\" is not configured. Please run \"mc config generate\".") console.Fatalln(console.ErrorMessage{
Message: "Please run \"mc config generate\"",
Error: iodine.New(errors.New("\"mc\" is not configured"), nil),
})
} }
return nil return nil
} }

View File

@@ -247,7 +247,7 @@ func (c *s3Client) listInRoutine(contentCh chan client.ContentOnChannel) {
content := new(client.Content) content := new(client.Content)
content.Name = object.Data.Key content.Name = object.Data.Key
switch { switch {
case object.Data.Size == 0: case strings.HasSuffix(object.Data.Key, "/"):
content.Time = time.Now() content.Time = time.Now()
content.Type = os.ModeDir content.Type = os.ModeDir
default: default:

View File

@@ -75,9 +75,10 @@ func (t Trace) Response(res *http.Response) (err error) {
// print HTTP Response // print HTTP Response
func (t Trace) print(data []byte) { func (t Trace) print(data []byte) {
if t.Writer != nil { switch {
case t.Writer != nil:
fmt.Fprintf(t.Writer, "%s", data) fmt.Fprintf(t.Writer, "%s", data)
} else { default:
console.Debugf("%s", data) console.Debugln(string(data))
} }
} }

View File

@@ -17,6 +17,7 @@
package console package console
import ( import (
"encoding/json"
"fmt" "fmt"
"os" "os"
"sync" "sync"
@@ -31,6 +32,40 @@ import (
// NoDebugPrint defines if the input should be printed in debug or not. By default it's set to true. // NoDebugPrint defines if the input should be printed in debug or not. By default it's set to true.
var NoDebugPrint = true var NoDebugPrint = true
// NoJsonPrint defines if the input should be printed in json formatted or not. By default it's set to true.
var NoJSONPrint = true
// Message info string
type Message string
// ErrorMessage error message structure
type ErrorMessage struct {
Message string `json:"Message"`
Error error `json:"Reason"`
}
// Content content message structure
type Content struct {
Filetype string `json:"ContentType"`
Time string `json:"LastModified"`
Size string `json:"Size"`
Name string `json:"Name"`
}
// Theme holds console color scheme
type Theme struct {
Fatal *color.Color
Error *color.Color
Info *color.Color
Debug *color.Color
Size *color.Color
Time *color.Color
File *color.Color
Dir *color.Color
Retry *color.Color
JSON *color.Color
}
var ( var (
mutex = &sync.RWMutex{} mutex = &sync.RWMutex{}
@@ -54,78 +89,134 @@ var (
return theme return theme
}() }()
// Print prints a error message and exits // Print prints a message
Print = themesDB[currThemeName].Info.Print Print = themesDB[currThemeName].Info.Print
// Println prints a error message with a new line and exits
Println = themesDB[currThemeName].Info.Println
// Printf prints a error message with formatting and exits
Printf = themesDB[currThemeName].Info.Printf
// Fatal prints a error message and exits // Fatal prints a error message and exits
Fatal = func(a ...interface{}) { print(themesDB[currThemeName].Error, a...); os.Exit(1) } Fatal = func(msg ErrorMessage) {
defer os.Exit(1)
if msg.Error != nil {
if NoJSONPrint {
reason := "Reason: " + iodine.ToError(msg.Error).Error()
message := msg.Message + ", " + reason
print(themesDB[currThemeName].Error, message)
if !NoDebugPrint {
print(themesDB[currThemeName].Error, msg.Error)
}
return
}
errorMessageBytes, _ := json.Marshal(&msg)
print(themesDB[currThemeName].JSON, string(errorMessageBytes))
}
}
// Fatalln prints a error message with a new line and exits // Fatalln prints a error message with a new line and exits
Fatalln = func(a ...interface{}) { println(themesDB[currThemeName].Error, a...); os.Exit(1) } Fatalln = func(msg ErrorMessage) {
// Fatalf prints a error message with formatting and exits defer os.Exit(1)
Fatalf = func(f string, a ...interface{}) { printf(themesDB[currThemeName].Error, f, a...); os.Exit(1) } if msg.Error != nil {
if NoJSONPrint {
reason := "Reason: " + iodine.ToError(msg.Error).Error()
message := msg.Message + ", " + reason
println(themesDB[currThemeName].Error, message)
if !NoDebugPrint {
println(themesDB[currThemeName].Error, msg.Error)
}
return
}
errorMessageBytes, _ := json.Marshal(&msg)
println(themesDB[currThemeName].JSON, string(errorMessageBytes))
}
}
// Error prints a error message // Error prints a error message
Error = func(a ...interface{}) { print(themesDB[currThemeName].Error, a...) } Error = func(msg ErrorMessage) {
if msg.Error != nil {
if NoJSONPrint {
reason := "Reason: " + iodine.ToError(msg.Error).Error()
message := msg.Message + ", " + reason
print(themesDB[currThemeName].Error, message)
if !NoDebugPrint {
print(themesDB[currThemeName].Error, msg.Error)
}
return
}
errorMessageBytes, _ := json.Marshal(&msg)
print(themesDB[currThemeName].JSON, string(errorMessageBytes))
}
}
// Errorln prints a error message with a new line // Errorln prints a error message with a new line
Errorln = func(a ...interface{}) { println(themesDB[currThemeName].Error, a...) } Errorln = func(msg ErrorMessage) {
// Errorf prints a error message with formatting if msg.Error != nil {
Errorf = func(f string, a ...interface{}) { printf(themesDB[currThemeName].Error, f, a...) } if NoJSONPrint {
reason := "Reason: " + iodine.ToError(msg.Error).Error()
message := msg.Message + ", " + reason
println(themesDB[currThemeName].Error, message)
if !NoDebugPrint {
println(themesDB[currThemeName].Error, msg.Error)
}
return
}
errorMessageBytes, _ := json.Marshal(&msg)
println(themesDB[currThemeName].JSON, string(errorMessageBytes))
}
}
// Info prints a informational message // Info prints a informational message
Info = func(a ...interface{}) { print(themesDB[currThemeName].Info, a...) } Info = func(msg Message) {
// Infoln prints a informational message with a new line if NoJSONPrint {
Infoln = func(a ...interface{}) { println(themesDB[currThemeName].Info, a...) } print(themesDB[currThemeName].Info, msg)
// Infof prints a informational message with formatting return
Infof = func(f string, a ...interface{}) { printf(themesDB[currThemeName].Info, f, a...) } }
infoBytes, _ := json.Marshal(&msg)
print(themesDB[currThemeName].JSON, string(infoBytes))
}
// Infoln prints a informational message with a new line
Infoln = func(msg Message) {
if NoJSONPrint {
println(themesDB[currThemeName].Info, msg)
return
}
infoBytes, _ := json.Marshal(&msg)
println(themesDB[currThemeName].JSON, string(infoBytes))
}
// Debug prints a debug message without a new line
// Debug prints a debug message // Debug prints a debug message
Debug = func(a ...interface{}) { Debug = func(a ...interface{}) {
if !NoDebugPrint { if !NoDebugPrint {
print(themesDB[currThemeName].Debug, a...) print(themesDB[currThemeName].Debug, a...)
} }
} }
// Debugln prints a debug message with a new line // Debugln prints a debug message with a new line
Debugln = func(a ...interface{}) { Debugln = func(a ...interface{}) {
if !NoDebugPrint { if !NoDebugPrint {
println(themesDB[currThemeName].Debug, a...) println(themesDB[currThemeName].Debug, a...)
} }
} }
// Debugf prints a debug message with formatting // ContentInfo prints a structure Content
Debugf = func(f string, a ...interface{}) { ContentInfo = func(c Content) {
if !NoDebugPrint { if NoJSONPrint {
printf(themesDB[currThemeName].Debug, f, a...) print(themesDB[currThemeName].Time, c.Time)
print(themesDB[currThemeName].Size, c.Size)
switch c.Filetype {
case "inode/directory":
println(themesDB[currThemeName].Dir, c.Name)
case "application/octet-stream":
println(themesDB[currThemeName].File, c.Name)
}
return
} }
contentBytes, _ := json.Marshal(&c)
println(themesDB[currThemeName].JSON, string(contentBytes))
} }
// File - File("foo.txt") // Retry prints a retry message
File = themesDB[currThemeName].File.SprintfFunc() Retry = func(a ...interface{}) {
// Dir - Dir("dir/") println(themesDB[currThemeName].Retry, a...)
Dir = themesDB[currThemeName].Dir.SprintfFunc() }
// Size - Size("12GB")
Size = themesDB[currThemeName].Size.SprintfFunc()
// Time - Time("12 Hours")
Time = themesDB[currThemeName].Time.SprintfFunc()
// Retry - Retry message number
Retry = themesDB[currThemeName].Retry.SprintfFunc()
) )
// Theme holds console color scheme
type Theme struct {
Fatal *color.Color
Error *color.Color
Info *color.Color
Debug *color.Color
Size *color.Color
Time *color.Color
File *color.Color
Dir *color.Color
Retry *color.Color
}
var ( var (
// wrap around standard fmt functions // wrap around standard fmt functions
// print prints a message prefixed with message type and program name // print prints a message prefixed with message type and program name
@@ -155,11 +246,24 @@ var (
c.Print(a...) c.Print(a...)
color.Output = output color.Output = output
mutex.Unlock() mutex.Unlock()
default: case themesDB[currThemeName].Info:
mutex.Lock() mutex.Lock()
c.Print(ProgramName() + ": ") c.Print(ProgramName() + ": ")
c.Print(a...) c.Print(a...)
mutex.Unlock() mutex.Unlock()
// special cases only for Content where it requires custom formatting
case themesDB[currThemeName].Time:
mutex.Lock()
c.Print(fmt.Sprintf("[%s]", a...))
mutex.Unlock()
case themesDB[currThemeName].Size:
mutex.Lock()
c.Printf(fmt.Sprintf("%6s ", a...))
mutex.Unlock()
default:
mutex.Lock()
c.Print(a...)
mutex.Unlock()
} }
} }
@@ -190,45 +294,23 @@ var (
c.Println(a...) c.Println(a...)
color.Output = output color.Output = output
mutex.Unlock() mutex.Unlock()
default: case themesDB[currThemeName].Info:
mutex.Lock() mutex.Lock()
c.Print(ProgramName() + ": ") c.Print(ProgramName() + ": ")
c.Println(a...) c.Println(a...)
mutex.Unlock() mutex.Unlock()
} case themesDB[currThemeName].Dir:
}
// printf - same as print, but takes a format specifier
printf = func(c *color.Color, f string, a ...interface{}) {
switch c {
case themesDB[currThemeName].Debug:
mutex.Lock() mutex.Lock()
output := color.Output // ugly but its needed
color.Output = stderrColoredOutput c.Printf("%s/\n", a...)
c.Print(ProgramName() + ": <DEBUG> ")
c.Printf(f, a...)
color.Output = output
mutex.Unlock() mutex.Unlock()
case themesDB[currThemeName].Fatal: case themesDB[currThemeName].File:
mutex.Lock() mutex.Lock()
output := color.Output c.Println(a...)
color.Output = stderrColoredOutput
c.Print(ProgramName() + ": <FATAL> ")
c.Printf(f, a...)
color.Output = output
mutex.Unlock()
case themesDB[currThemeName].Error:
mutex.Lock()
output := color.Output
color.Output = stderrColoredOutput
c.Print(ProgramName() + ": <ERROR> ")
c.Printf(f, a...)
color.Output = output
mutex.Unlock() mutex.Unlock()
default: default:
mutex.Lock() mutex.Lock()
c.Print(ProgramName() + ": ") c.Println(a...)
c.Printf(f, a...)
mutex.Unlock() mutex.Unlock()
} }
} }
@@ -262,46 +344,6 @@ func SetTheme(themeName string) error {
} }
currThemeName = themeName currThemeName = themeName
theme := themesDB[currThemeName]
// Error prints a error message
Error = func(a ...interface{}) { print(theme.Error, a...) }
// Errorln prints a error message with a new line
Errorln = func(a ...interface{}) { println(theme.Error, a...) }
// Errorf prints a error message with formatting
Errorf = func(f string, a ...interface{}) { printf(theme.Error, f, a...) }
// Info prints a informational message
Info = func(a ...interface{}) { print(theme.Info, a...) }
// Infoln prints a informational message with a new line
Infoln = func(a ...interface{}) { println(theme.Info, a...) }
// Infof prints a informational message with formatting
Infof = func(f string, a ...interface{}) { printf(theme.Info, f, a...) }
// Debug prints a debug message
Debug = func(a ...interface{}) {
if !NoDebugPrint {
print(theme.Debug, a...)
}
}
// Debugln prints a debug message with a new line
Debugln = func(a ...interface{}) {
if !NoDebugPrint {
println(theme.Debug, a...)
}
}
// Debugf prints a debug message with formatting
Debugf = func(f string, a ...interface{}) {
if !NoDebugPrint {
printf(theme.Debug, f, a...)
}
}
Dir = theme.Dir.SprintfFunc()
File = theme.File.SprintfFunc()
Size = theme.Size.SprintfFunc()
Time = theme.Time.SprintfFunc()
Retry = theme.Retry.SprintfFunc()
mutex.Unlock() mutex.Unlock()

View File

@@ -29,6 +29,7 @@ var MiniTheme = Theme{
Size: (color.New(color.FgYellow)), Size: (color.New(color.FgYellow)),
Time: (color.New(color.FgGreen)), Time: (color.New(color.FgGreen)),
Retry: (color.New(color.FgMagenta, color.Bold)), Retry: (color.New(color.FgMagenta, color.Bold)),
JSON: (color.New(color.FgWhite, color.Italic)),
} }
// WhiteTheme - All white color theme // WhiteTheme - All white color theme
@@ -42,6 +43,7 @@ var WhiteTheme = Theme{
Size: (color.New(color.FgWhite, color.Bold)), Size: (color.New(color.FgWhite, color.Bold)),
Time: (color.New(color.FgWhite, color.Bold)), Time: (color.New(color.FgWhite, color.Bold)),
Retry: (color.New(color.FgWhite, color.Bold)), Retry: (color.New(color.FgWhite, color.Bold)),
JSON: (color.New(color.FgWhite, color.Bold, color.Italic)),
} }
// NoColorTheme - Disables color theme // NoColorTheme - Disables color theme
@@ -55,4 +57,5 @@ var NoColorTheme = Theme{
Size: (color.New()), Size: (color.New()),
Time: (color.New()), Time: (color.New()),
Retry: (color.New()), Retry: (color.New()),
JSON: (color.New()),
} }