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
import (
"errors"
"fmt"
"time"
@@ -31,42 +32,51 @@ func runAccessCmd(ctx *cli.Context) {
cli.ShowCommandHelpAndExit(ctx, "access", 1) // last argument is exit code
}
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()
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)
targetURLs, err := getExpandedURLs(ctx.Args(), config.Aliases)
if err != nil {
switch e := iodine.ToError(err).(type) {
case errUnsupportedScheme:
console.Fatalf("Unknown type of URL %s. Reason: %s.\n", e.url, e)
default:
console.Fatalf("reading URLs failed with following Reason: %s\n", e)
}
console.Fatalln(console.ErrorMessage{
Message: "Unknown type of URL ",
Error: iodine.New(err, nil),
})
}
acl := bucketACL(ctx.Args().First())
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
for _, targetURL := range targetURLs {
targetConfig, err := getHostConfig(targetURL)
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
}
for targetURL, targetConfig := range targetURLConfigMap {
errorMsg, err := doUpdateAccessCmd(targetURL, acl.String(), targetConfig)
err = iodine.New(err, nil)
if err != nil {
if errorMsg == "" {
errorMsg = "Empty error message. Please rerun this command with --debug and file a bug report."
}
console.Errorf("%s", errorMsg)
console.Errorln(console.ErrorMessage{
Message: errorMsg,
Error: iodine.New(err, nil),
})
}
}
}
@@ -76,10 +86,8 @@ func doUpdateAccessCmd(targetURL, targetACL string, targetConfig *hostConfig) (s
var clnt client.Client
clnt, err = getNewClient(targetURL, targetConfig)
if err != nil {
err := iodine.New(err, nil)
msg := fmt.Sprintf("Unable to initialize client for %s. Reason: %s.\n",
targetURL, iodine.ToError(err))
return msg, err
msg := fmt.Sprintf("Unable to initialize client for %s", targetURL)
return msg, iodine.New(err, nil)
}
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) {
err := clnt.SetBucketACL(targetACL)
for i := 0; i < globalMaxRetryFlag && err != nil && isValidRetry(err); i++ {
fmt.Println(console.Retry("Retrying ... %d", i))
console.Retry("Retrying ...", i)
// Progressively longer delays
time.Sleep(time.Duration(i*i) * time.Second)
err = clnt.SetBucketACL(targetACL)
}
if err != nil {
err := iodine.New(err, nil)
msg := fmt.Sprintf("Failed to add bucket access policy for URL %s. Reason: %s.\n", targetURL, iodine.ToError(err))
return msg, err
msg := fmt.Sprintf("Failed to add bucket access policy for URL %s", targetURL)
return msg, iodine.New(err, nil)
}
return "", nil
}

View File

@@ -18,6 +18,7 @@ package main
import (
"errors"
"fmt"
"io"
"os"
@@ -31,33 +32,42 @@ func runCatCmd(ctx *cli.Context) {
cli.ShowCommandHelpAndExit(ctx, "cat", 1) // last argument is exit code
}
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()
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...
urls, err := getExpandedURLs(ctx.Args(), config.Aliases)
if err != nil {
switch e := iodine.ToError(err).(type) {
case errUnsupportedScheme:
console.Fatalf("Unknown type of URL %s. Reason: %s.\n", e.url, e)
default:
console.Fatalf("Unable to parse arguments. Reason: %s.\n", iodine.ToError(err))
}
console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unknown type of URL %s", urls),
Error: iodine.New(err, nil),
})
}
sourceURLs := urls
sourceURLConfigMap, err := getHostConfigs(sourceURLs)
if err != nil {
console.Fatalf("Unable to read host configuration for %s from config file %s. Reason: %s.\n",
sourceURLs, mustGetMcConfigPath(), iodine.ToError(err))
console.Fatalln(console.ErrorMessage{
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)
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()
tailArgs := ctx.Args().Tail()
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)
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.

View File

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

View File

@@ -17,6 +17,9 @@
package main
import (
"errors"
"fmt"
"github.com/minio/cli"
"github.com/minio/mc/pkg/console"
"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
}
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()
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()
@@ -42,40 +51,59 @@ func runDiffCmd(ctx *cli.Context) {
if err != nil {
switch iodine.ToError(err).(type) {
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:
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)
if err != nil {
console.Fatalf("Unable to read host configuration for %s from config file %s. Reason: %s.\n",
firstURL, mustGetMcConfigPath(), iodine.ToError(err))
console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to read host configuration for %s from config file %s.", firstURL, mustGetMcConfigPath()),
Error: err,
})
}
_, err = getHostConfig(secondURL)
if err != nil {
console.Fatalf("Unable to read host configuration for %s from config file %s. Reason: %s.\n",
secondURL, mustGetMcConfigPath(), iodine.ToError(err))
console.Fatalln(console.ErrorMessage{
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)
if err != nil {
switch iodine.ToError(err).(type) {
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:
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
newFirstURL := stripRecursiveURL(firstURL)
for diff := range doDiffCmd(newFirstURL, secondURL, isURLRecursive(firstURL)) {
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
import (
"errors"
"fmt"
"github.com/minio/cli"
"github.com/minio/mc/pkg/console"
"github.com/minio/minio/pkg/iodine"
@@ -27,12 +30,20 @@ func runListCmd(ctx *cli.Context) {
if !ctx.Args().Present() || ctx.Args().First() == "help" {
cli.ShowCommandHelpAndExit(ctx, "ls", 1) // last argument is exit code
}
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()
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)
for _, arg := range ctx.Args() {
@@ -40,15 +51,23 @@ func runListCmd(ctx *cli.Context) {
if err != nil {
switch e := iodine.ToError(err).(type) {
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:
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)
if err != nil {
console.Fatalf("Unable to read host configuration for %s from config file %s. Reason: %s.\n",
targetURL, mustGetMcConfigPath(), iodine.ToError(err))
console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to read host configuration for %s from config file %s", targetURL, mustGetMcConfigPath()),
Error: iodine.New(err, nil),
})
}
targetURLConfigMap[targetURL] = targetConfig
}
@@ -56,9 +75,11 @@ func runListCmd(ctx *cli.Context) {
// if recursive strip off the "..."
newTargetURL := stripRecursiveURL(targetURL)
err = doListCmd(newTargetURL, targetConfig, isURLRecursive(targetURL))
err = iodine.New(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
import (
"errors"
"fmt"
"time"
@@ -32,37 +33,51 @@ func runMakeBucketCmd(ctx *cli.Context) {
cli.ShowCommandHelpAndExit(ctx, "mb", 1) // last argument is exit code
}
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()
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)
targetURLs, err := getExpandedURLs(ctx.Args(), config.Aliases)
for _, arg := range ctx.Args() {
targetURL, err := getExpandedURL(arg, config.Aliases)
if err != nil {
switch e := iodine.ToError(err).(type) {
case errUnsupportedScheme:
console.Fatalf("Unknown URL type %s passed. Reason: %s.\n", e.url, e)
console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unknown type of URL %s", e.url),
Error: e,
})
default:
console.Fatalf("Error in parsing path or URL. Reason: %s.\n", e)
console.Fatalln(console.ErrorMessage{
Message: fmt.Sprintf("Unable to parse argument %s", arg),
Error: err,
})
}
}
for _, targetURL := range targetURLs {
targetConfig, err := getHostConfig(targetURL)
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
}
for targetURL, targetConfig := range targetURLConfigMap {
errorMsg, err := doMakeBucketCmd(targetURL, targetConfig)
err = iodine.New(err, nil)
if err != nil {
if errorMsg == "" {
errorMsg = "Empty error message. Please rerun this command with --debug and file a bug report."
}
console.Errorf("%s", errorMsg)
console.Errorln(console.ErrorMessage{
Message: errorMsg,
Error: err,
})
}
}
}
@@ -73,10 +88,8 @@ func doMakeBucketCmd(targetURL string, targetConfig *hostConfig) (string, error)
var clnt client.Client
clnt, err = getNewClient(targetURL, targetConfig)
if err != nil {
err := iodine.New(err, nil)
msg := fmt.Sprintf("Unable to initialize client for %s. Reason: %s.\n",
targetURL, iodine.ToError(err))
return msg, err
msg := fmt.Sprintf("Unable to initialize client for %s", targetURL)
return msg, iodine.New(err, nil)
}
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) {
err := clnt.MakeBucket()
for i := 0; i < globalMaxRetryFlag && err != nil && isValidRetry(err); i++ {
fmt.Println(console.Retry("Retrying ... %d", i))
console.Retry("Retrying ...", i)
// Progressively longer delays
time.Sleep(time.Duration(i*i) * time.Second)
err = clnt.MakeBucket()
}
if err != nil {
err := iodine.New(err, nil)
msg := fmt.Sprintf("Failed to create bucket for URL %s. Reason: %s.\n", targetURL, iodine.ToError(err))
return msg, err
msg := fmt.Sprintf("Failed to create bucket for URL %s", targetURL)
return msg, iodine.New(err, nil)
}
return "", nil
}

View File

@@ -17,6 +17,8 @@
package main
import (
"errors"
"fmt"
"runtime"
"sync"
@@ -29,12 +31,20 @@ func runSyncCmd(ctx *cli.Context) {
if len(ctx.Args()) < 2 || ctx.Args().First() == "help" {
cli.ShowCommandHelpAndExit(ctx, "sync", 1) // last argument is exit code
}
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())
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.
@@ -66,7 +76,10 @@ func runSyncCmd(ctx *cli.Context) {
for syncURLs := range prepareSyncURLs(sourceURL, targetURLs) {
if syncURLs.Error != nil {
console.Errorln(iodine.ToError(syncURLs.Error))
console.Errorln(console.ErrorMessage{
Message: "Failed with",
Error: iodine.New(syncURLs.Error, nil),
})
continue
}
syncQueue <- true
@@ -75,16 +88,25 @@ func runSyncCmd(ctx *cli.Context) {
defer wg.Done()
srcConfig, err := getHostConfig(syncURLs.SourceContent.Name)
if err != nil {
console.Errorln(iodine.ToError(err))
console.Fatalln(console.ErrorMessage{
Message: "Failed with",
Error: iodine.New(err, nil),
})
return
}
tgtConfig, err := getHostConfig(syncURLs.TargetContent.Name)
if err != nil {
console.Errorln(iodine.ToError(err))
console.Fatalln(console.ErrorMessage{
Message: "Failed with",
Error: iodine.New(err, nil),
})
return
}
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
}(syncURLs, &bar)

View File

@@ -17,6 +17,8 @@
package main
import (
"errors"
"fmt"
"runtime"
"time"
@@ -56,17 +58,24 @@ func runUpdateCmd(ctx *cli.Context) {
cli.ShowCommandHelpAndExit(ctx, "update", 1) // last argument is exit code
}
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)
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)
if err != nil {
console.Fatalln(msg)
}
if msg != "" {
console.Infoln(msg)
console.Fatalln(console.ErrorMessage{
Message: msg,
Error: iodine.New(err, nil),
})
}
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 {
configFile, err := getMcConfigPath()
if err != nil {

59
ls.go
View File

@@ -17,11 +17,7 @@
package main
import (
"encoding/json"
"fmt"
"os"
"strings"
"time"
"github.com/dustin/go-humanize"
"github.com/minio/mc/pkg/client"
@@ -36,49 +32,22 @@ const (
printDate = "2006-01-02 15:04:05 MST"
)
// printJSON rather than colored output
func printJSON(content *client.Content) {
type jsonContent struct {
Filetype string `json:"content-type"`
Date string `json:"last-modified"`
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() {
// printContent prints content meta-data
func printContent(c *client.Content) {
content := console.Content{}
content.Filetype = func() string {
if c.Type.IsDir() {
return "inode/directory"
}
if content.Type.IsRegular() {
if c.Type.IsRegular() {
return "application/octet-stream"
}
return "application/octet-stream"
}()
contentJSON.Name = content.Name
contentBytes, _ := json.MarshalIndent(contentJSON, "", "\t")
fmt.Println(string(contentBytes))
}
// 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))
}
content.Size = humanize.IBytes(uint64(c.Size))
content.Name = strings.TrimSuffix(c.Name, "/")
content.Time = c.Time.Local().Format(printDate)
console.ContentInfo(content)
}
// 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
contentName = strings.TrimPrefix(contentName, strings.TrimSuffix(targetURL, "/")+"/")
}
switch {
case globalJSONFlag == true:
printJSON(contentCh.Content)
default:
printContent(contentCh.Content.Time, contentCh.Content.Size, contentName, contentCh.Content.Type)
}
contentCh.Content.Name = contentName
printContent(contentCh.Content)
}
if err != nil {
return iodine.New(err, map[string]string{"Target": targetURL})

29
main.go
View File

@@ -17,6 +17,7 @@
package main
import (
"errors"
"fmt"
"os"
"os/user"
@@ -34,7 +35,10 @@ import (
func checkConfig() {
_, err := user.Current()
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
@@ -45,7 +49,10 @@ func checkConfig() {
// Ensures config file is sane
_, err = getMcConfig()
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()
console.NoDebugPrint = false
}
if globalJSONFlag {
console.NoJSONPrint = false
}
themeName := ctx.GlobalString("theme")
switch {
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:
err := console.SetTheme(themeName)
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()
@@ -122,7 +138,10 @@ func main() {
}
app.After = func(ctx *cli.Context) error {
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
}

View File

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

View File

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

View File

@@ -17,6 +17,7 @@
package console
import (
"encoding/json"
"fmt"
"os"
"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.
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 (
mutex = &sync.RWMutex{}
@@ -54,78 +89,134 @@ var (
return theme
}()
// Print prints a error message and exits
// Print prints a message
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 = 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 = func(a ...interface{}) { println(themesDB[currThemeName].Error, a...); os.Exit(1) }
// Fatalf prints a error message with formatting and exits
Fatalf = func(f string, a ...interface{}) { printf(themesDB[currThemeName].Error, f, a...); os.Exit(1) }
Fatalln = func(msg ErrorMessage) {
defer 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 = 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 = func(a ...interface{}) { println(themesDB[currThemeName].Error, a...) }
// Errorf prints a error message with formatting
Errorf = func(f string, a ...interface{}) { printf(themesDB[currThemeName].Error, f, a...) }
Errorln = func(msg ErrorMessage) {
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))
}
}
// Info prints a informational message
Info = func(a ...interface{}) { print(themesDB[currThemeName].Info, a...) }
// Infoln prints a informational message with a new line
Infoln = func(a ...interface{}) { println(themesDB[currThemeName].Info, a...) }
// Infof prints a informational message with formatting
Infof = func(f string, a ...interface{}) { printf(themesDB[currThemeName].Info, f, a...) }
Info = func(msg Message) {
if NoJSONPrint {
print(themesDB[currThemeName].Info, msg)
return
}
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 = func(a ...interface{}) {
if !NoDebugPrint {
print(themesDB[currThemeName].Debug, a...)
}
}
// Debugln prints a debug message with a new line
Debugln = func(a ...interface{}) {
if !NoDebugPrint {
println(themesDB[currThemeName].Debug, a...)
}
}
// Debugf prints a debug message with formatting
Debugf = func(f string, a ...interface{}) {
if !NoDebugPrint {
printf(themesDB[currThemeName].Debug, f, a...)
// ContentInfo prints a structure Content
ContentInfo = func(c Content) {
if NoJSONPrint {
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")
File = themesDB[currThemeName].File.SprintfFunc()
// Dir - Dir("dir/")
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()
// Retry prints a retry message
Retry = func(a ...interface{}) {
println(themesDB[currThemeName].Retry, a...)
}
)
// 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 (
// wrap around standard fmt functions
// print prints a message prefixed with message type and program name
@@ -155,11 +246,24 @@ var (
c.Print(a...)
color.Output = output
mutex.Unlock()
default:
case themesDB[currThemeName].Info:
mutex.Lock()
c.Print(ProgramName() + ": ")
c.Print(a...)
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...)
color.Output = output
mutex.Unlock()
default:
case themesDB[currThemeName].Info:
mutex.Lock()
c.Print(ProgramName() + ": ")
c.Println(a...)
mutex.Unlock()
}
}
// printf - same as print, but takes a format specifier
printf = func(c *color.Color, f string, a ...interface{}) {
switch c {
case themesDB[currThemeName].Debug:
case themesDB[currThemeName].Dir:
mutex.Lock()
output := color.Output
color.Output = stderrColoredOutput
c.Print(ProgramName() + ": <DEBUG> ")
c.Printf(f, a...)
color.Output = output
// ugly but its needed
c.Printf("%s/\n", a...)
mutex.Unlock()
case themesDB[currThemeName].Fatal:
case themesDB[currThemeName].File:
mutex.Lock()
output := color.Output
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
c.Println(a...)
mutex.Unlock()
default:
mutex.Lock()
c.Print(ProgramName() + ": ")
c.Printf(f, a...)
c.Println(a...)
mutex.Unlock()
}
}
@@ -262,46 +344,6 @@ func SetTheme(themeName string) error {
}
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()

View File

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