mirror of
https://github.com/minio/mc.git
synced 2025-11-12 01:02:26 +03:00
517 lines
13 KiB
Go
517 lines
13 KiB
Go
/*
|
|
* Minio Client (C) 2015 Minio, Inc.
|
|
*
|
|
* 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 console
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
|
|
"path/filepath"
|
|
|
|
"github.com/fatih/color"
|
|
"github.com/fatih/structs"
|
|
"github.com/minio/minio/pkg/iodine"
|
|
"github.com/shiena/ansicolor"
|
|
)
|
|
|
|
// 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
|
|
|
|
// 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
|
|
Bar *color.Color
|
|
Print *color.Color
|
|
}
|
|
|
|
func readErrorFromdata(data interface{}) error {
|
|
st := structs.New(data)
|
|
if st.IsZero() {
|
|
return nil
|
|
}
|
|
msgErr := st.Field("Error")
|
|
return msgErr.Value().(error)
|
|
}
|
|
|
|
var (
|
|
mutex = &sync.RWMutex{}
|
|
|
|
stdoutColoredOutput = ansicolor.NewAnsiColorWriter(os.Stdout)
|
|
stderrColoredOutput = ansicolor.NewAnsiColorWriter(os.Stderr)
|
|
|
|
// themesDB contains supported list of Themes
|
|
themesDB = map[string]Theme{
|
|
"minimal": MiniTheme,
|
|
"nocolor": NoColorTheme,
|
|
"white": WhiteTheme,
|
|
}
|
|
|
|
// currTheme is current theme
|
|
currThemeName = func() string {
|
|
theme := GetDefaultThemeName()
|
|
// if not a TTY disable color
|
|
if !isatty(os.Stdout.Fd()) {
|
|
return "nocolor"
|
|
}
|
|
return theme
|
|
}()
|
|
|
|
// Bar print progress bar
|
|
Bar = themesDB[currThemeName].Info.Print
|
|
|
|
// Print prints a message
|
|
Print = func(data ...interface{}) {
|
|
if NoJSONPrint {
|
|
print(themesDB[currThemeName].Print, data...)
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
printBytes, _ := json.Marshal(&data[i])
|
|
print(themesDB[currThemeName].JSON, string(printBytes))
|
|
}
|
|
}
|
|
|
|
// Printf prints a formatted message
|
|
Printf = func(f string, data ...interface{}) {
|
|
if NoJSONPrint {
|
|
printf(themesDB[currThemeName].Print, f, data...)
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
printBytes, _ := json.Marshal(&data[i])
|
|
printf(themesDB[currThemeName].JSON, "", string(printBytes))
|
|
}
|
|
}
|
|
|
|
// Println prints a message with a newline
|
|
Println = func(data ...interface{}) {
|
|
if NoJSONPrint {
|
|
println(themesDB[currThemeName].Print, data...)
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
printBytes, _ := json.Marshal(&data[i])
|
|
println(themesDB[currThemeName].JSON, string(printBytes))
|
|
}
|
|
}
|
|
|
|
// Prints prints a structured message with a newline
|
|
Prints = func(data ...interface{}) {
|
|
for i := 0; i < len(data); i++ {
|
|
if NoJSONPrint {
|
|
println(themesDB[currThemeName].Info, data[i])
|
|
return
|
|
}
|
|
infoBytes, _ := json.Marshal(&data[i])
|
|
println(themesDB[currThemeName].JSON, string(infoBytes))
|
|
}
|
|
}
|
|
// Fatal print a error message and exit
|
|
Fatal = func(data ...interface{}) {
|
|
defer os.Exit(1)
|
|
if NoJSONPrint {
|
|
print(themesDB[currThemeName].Error, data...)
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
errorMessageBytes, _ := json.Marshal(&data[i])
|
|
print(themesDB[currThemeName].JSON, string(errorMessageBytes))
|
|
}
|
|
}
|
|
|
|
// Fatalf print a error message with a format specified and exit
|
|
Fatalf = func(f string, data ...interface{}) {
|
|
defer os.Exit(1)
|
|
if NoJSONPrint {
|
|
printf(themesDB[currThemeName].Error, f, data...)
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
errorMessageBytes, _ := json.Marshal(&data[i])
|
|
printf(themesDB[currThemeName].JSON, "", string(errorMessageBytes))
|
|
}
|
|
}
|
|
|
|
// Fatalln print a error message with a new line and exit
|
|
Fatalln = func(data ...interface{}) {
|
|
defer os.Exit(1)
|
|
if NoJSONPrint {
|
|
println(themesDB[currThemeName].Error, data...)
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
errorMessageBytes, _ := json.Marshal(&data[i])
|
|
println(themesDB[currThemeName].JSON, string(errorMessageBytes))
|
|
}
|
|
}
|
|
|
|
// Fatals print a error structure with a new line and exit
|
|
Fatals = func(data ...interface{}) {
|
|
defer os.Exit(1)
|
|
for i := 0; i < len(data); i++ {
|
|
err := readErrorFromdata(data[i])
|
|
if err != nil {
|
|
if NoJSONPrint {
|
|
println(themesDB[currThemeName].Error, data[i])
|
|
if !NoDebugPrint {
|
|
print(themesDB[currThemeName].Error, err)
|
|
}
|
|
return
|
|
}
|
|
errorMessageBytes, _ := json.Marshal(&data[i])
|
|
println(themesDB[currThemeName].JSON, string(errorMessageBytes))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Error prints a error message
|
|
Error = func(data ...interface{}) {
|
|
if NoJSONPrint {
|
|
print(themesDB[currThemeName].Error, data...)
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
errorMessageBytes, _ := json.Marshal(&data[i])
|
|
print(themesDB[currThemeName].JSON, string(errorMessageBytes))
|
|
}
|
|
}
|
|
|
|
// Errorf print a error message with a format specified
|
|
Errorf = func(f string, data ...interface{}) {
|
|
if NoJSONPrint {
|
|
printf(themesDB[currThemeName].Error, f, data...)
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
errorMessageBytes, _ := json.Marshal(&data[i])
|
|
printf(themesDB[currThemeName].JSON, "", string(errorMessageBytes))
|
|
}
|
|
}
|
|
|
|
// Errorln prints a error message with a new line
|
|
Errorln = func(data ...interface{}) {
|
|
if NoJSONPrint {
|
|
println(themesDB[currThemeName].Error, data...)
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
errorMessageBytes, _ := json.Marshal(&data[i])
|
|
println(themesDB[currThemeName].JSON, string(errorMessageBytes))
|
|
}
|
|
}
|
|
|
|
// Errors print a error structure with a new line
|
|
Errors = func(data ...interface{}) {
|
|
for i := 0; i < len(data); i++ {
|
|
err := readErrorFromdata(data[i])
|
|
if err != nil {
|
|
if NoJSONPrint {
|
|
println(themesDB[currThemeName].Error, data[i])
|
|
if !NoDebugPrint {
|
|
print(themesDB[currThemeName].Error, err)
|
|
}
|
|
return
|
|
}
|
|
errorMessageBytes, _ := json.Marshal(&data[i])
|
|
println(themesDB[currThemeName].JSON, string(errorMessageBytes))
|
|
}
|
|
}
|
|
}
|
|
|
|
// Info prints a informational message
|
|
Info = func(data ...interface{}) {
|
|
if NoJSONPrint {
|
|
print(themesDB[currThemeName].Info, data...)
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
infoBytes, _ := json.Marshal(&data[i])
|
|
print(themesDB[currThemeName].JSON, string(infoBytes))
|
|
}
|
|
}
|
|
|
|
// Infof prints a informational message in custom format
|
|
Infof = func(f string, data ...interface{}) {
|
|
if NoJSONPrint {
|
|
printf(themesDB[currThemeName].Info, f, data...)
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
infoBytes, _ := json.Marshal(&data[i])
|
|
printf(themesDB[currThemeName].JSON, "", string(infoBytes))
|
|
}
|
|
}
|
|
|
|
// Infoln prints a informational message with a new line
|
|
Infoln = func(data ...interface{}) {
|
|
if NoJSONPrint {
|
|
println(themesDB[currThemeName].Info, data...)
|
|
return
|
|
}
|
|
for i := 0; i < len(data); i++ {
|
|
infoBytes, _ := json.Marshal(&data[i])
|
|
println(themesDB[currThemeName].JSON, string(infoBytes))
|
|
}
|
|
}
|
|
|
|
// Infos print a informational structured message
|
|
Infos = func(data ...interface{}) {
|
|
for i := 0; i < len(data); i++ {
|
|
if NoJSONPrint {
|
|
println(themesDB[currThemeName].Info, data[i])
|
|
return
|
|
}
|
|
infoBytes, _ := json.Marshal(&data[i])
|
|
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...)
|
|
}
|
|
}
|
|
|
|
// Debugf prints a debug message with a new line
|
|
Debugf = func(f string, a ...interface{}) {
|
|
if !NoDebugPrint {
|
|
printf(themesDB[currThemeName].Debug, f, a...)
|
|
}
|
|
}
|
|
|
|
// Debugln prints a debug message with a new line
|
|
Debugln = func(a ...interface{}) {
|
|
if !NoDebugPrint {
|
|
println(themesDB[currThemeName].Debug, a...)
|
|
}
|
|
}
|
|
|
|
// Time helper to print Time theme
|
|
Time = themesDB[currThemeName].Time.SprintfFunc()
|
|
// Size helper to print Size theme
|
|
Size = themesDB[currThemeName].Size.SprintfFunc()
|
|
// File helper to print File theme
|
|
File = themesDB[currThemeName].File.SprintfFunc()
|
|
// Dir helper to print Dir theme
|
|
Dir = themesDB[currThemeName].Dir.SprintfFunc()
|
|
|
|
// Retry prints a retry message
|
|
Retry = func(a ...interface{}) {
|
|
println(themesDB[currThemeName].Retry, a...)
|
|
}
|
|
)
|
|
|
|
var (
|
|
// wrap around standard fmt functions
|
|
// print prints a message prefixed with message type and program name
|
|
print = func(c *color.Color, a ...interface{}) {
|
|
switch c {
|
|
case themesDB[currThemeName].Debug:
|
|
mutex.Lock()
|
|
output := color.Output
|
|
color.Output = stderrColoredOutput
|
|
c.Print(ProgramName() + ": <DEBUG> ")
|
|
c.Print(a...)
|
|
color.Output = output
|
|
mutex.Unlock()
|
|
case themesDB[currThemeName].Fatal:
|
|
mutex.Lock()
|
|
output := color.Output
|
|
color.Output = stderrColoredOutput
|
|
c.Print(ProgramName() + ": <FATAL> ")
|
|
c.Print(a...)
|
|
color.Output = output
|
|
mutex.Unlock()
|
|
case themesDB[currThemeName].Error:
|
|
mutex.Lock()
|
|
output := color.Output
|
|
color.Output = stderrColoredOutput
|
|
c.Print(ProgramName() + ": <ERROR> ")
|
|
c.Print(a...)
|
|
color.Output = output
|
|
mutex.Unlock()
|
|
case themesDB[currThemeName].Info:
|
|
mutex.Lock()
|
|
c.Print(ProgramName() + ": ")
|
|
c.Print(a...)
|
|
mutex.Unlock()
|
|
default:
|
|
mutex.Lock()
|
|
c.Print(a...)
|
|
mutex.Unlock()
|
|
}
|
|
}
|
|
|
|
// printf - same as print with a new line
|
|
printf = func(c *color.Color, f string, a ...interface{}) {
|
|
switch c {
|
|
case themesDB[currThemeName].Debug:
|
|
mutex.Lock()
|
|
output := color.Output
|
|
color.Output = stderrColoredOutput
|
|
c.Print(ProgramName() + ": <DEBUG> ")
|
|
c.Printf(f, a...)
|
|
color.Output = output
|
|
mutex.Unlock()
|
|
case themesDB[currThemeName].Fatal:
|
|
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
|
|
mutex.Unlock()
|
|
case themesDB[currThemeName].Info:
|
|
mutex.Lock()
|
|
c.Print(ProgramName() + ": ")
|
|
c.Printf(f, a...)
|
|
mutex.Unlock()
|
|
default:
|
|
mutex.Lock()
|
|
c.Printf(f, a...)
|
|
mutex.Unlock()
|
|
}
|
|
}
|
|
|
|
// println - same as print with a new line
|
|
println = func(c *color.Color, a ...interface{}) {
|
|
switch c {
|
|
case themesDB[currThemeName].Debug:
|
|
mutex.Lock()
|
|
output := color.Output
|
|
color.Output = stderrColoredOutput
|
|
c.Print(ProgramName() + ": <DEBUG> ")
|
|
c.Println(a...)
|
|
color.Output = output
|
|
mutex.Unlock()
|
|
case themesDB[currThemeName].Fatal:
|
|
mutex.Lock()
|
|
output := color.Output
|
|
color.Output = stderrColoredOutput
|
|
c.Print(ProgramName() + ": <FATAL> ")
|
|
c.Println(a...)
|
|
color.Output = output
|
|
mutex.Unlock()
|
|
case themesDB[currThemeName].Error:
|
|
mutex.Lock()
|
|
output := color.Output
|
|
color.Output = stderrColoredOutput
|
|
c.Print(ProgramName() + ": <ERROR> ")
|
|
c.Println(a...)
|
|
color.Output = output
|
|
mutex.Unlock()
|
|
case themesDB[currThemeName].Info:
|
|
mutex.Lock()
|
|
c.Print(ProgramName() + ": ")
|
|
c.Println(a...)
|
|
mutex.Unlock()
|
|
default:
|
|
mutex.Lock()
|
|
c.Println(a...)
|
|
mutex.Unlock()
|
|
}
|
|
}
|
|
)
|
|
|
|
// Lock console
|
|
func Lock() {
|
|
mutex.Lock()
|
|
}
|
|
|
|
// Unlock locked console
|
|
func Unlock() {
|
|
mutex.Unlock()
|
|
}
|
|
|
|
// SetTheme sets a color theme
|
|
func SetTheme(themeName string) error {
|
|
if !IsValidTheme(themeName) {
|
|
return iodine.New(fmt.Errorf("Unsupported theme name [%s]", themeName), nil)
|
|
}
|
|
|
|
mutex.Lock()
|
|
|
|
// Just another additional precaution to completely disable color.
|
|
// Color theme is also necessary, because it does other useful things like exit-on-fatal..
|
|
switch currThemeName {
|
|
case "nocolor":
|
|
color.NoColor = true
|
|
default:
|
|
color.NoColor = false
|
|
}
|
|
|
|
currThemeName = themeName
|
|
|
|
mutex.Unlock()
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetThemeName returns currently set theme name
|
|
func GetThemeName() string {
|
|
return currThemeName
|
|
}
|
|
|
|
// GetDefaultThemeName returns the default theme
|
|
func GetDefaultThemeName() string {
|
|
return "minimal"
|
|
}
|
|
|
|
// GetThemeNames returns currently supported list of themes
|
|
func GetThemeNames() (themeNames []string) {
|
|
for themeName := range themesDB {
|
|
themeNames = append(themeNames, themeName)
|
|
}
|
|
return themeNames
|
|
}
|
|
|
|
// IsValidTheme returns true if "themeName" is currently supported
|
|
func IsValidTheme(themeName string) bool {
|
|
_, ok := themesDB[themeName]
|
|
return ok
|
|
}
|
|
|
|
// ProgramName - return the name of the executable program
|
|
func ProgramName() string {
|
|
_, progName := filepath.Split(os.Args[0])
|
|
return progName
|
|
}
|