add github.com/pkg/errors to glide and vendor
This adds github.com/pkg/errors to glide.yaml followed by glide and glide-vc commands. The github.com/pkg/errors package is currently required mainly for the errors.Wrap() and errors.New() methods, since this lets us to annotate the errors while passing the error message up the call stack.
This commit is contained in:
-64
@@ -1,64 +0,0 @@
|
||||
package logrus
|
||||
|
||||
// The following code was sourced and modified from the
|
||||
// https://bitbucket.org/tebeka/atexit package governed by the following license:
|
||||
//
|
||||
// Copyright (c) 2012 Miki Tebeka <miki.tebeka@gmail.com>.
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
// this software and associated documentation files (the "Software"), to deal in
|
||||
// the Software without restriction, including without limitation the rights to
|
||||
// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
// the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
// subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
// FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
// IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
// CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
var handlers = []func(){}
|
||||
|
||||
func runHandler(handler func()) {
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
fmt.Fprintln(os.Stderr, "Error: Logrus exit handler error:", err)
|
||||
}
|
||||
}()
|
||||
|
||||
handler()
|
||||
}
|
||||
|
||||
func runHandlers() {
|
||||
for _, handler := range handlers {
|
||||
runHandler(handler)
|
||||
}
|
||||
}
|
||||
|
||||
// Exit runs all the Logrus atexit handlers and then terminates the program using os.Exit(code)
|
||||
func Exit(code int) {
|
||||
runHandlers()
|
||||
os.Exit(code)
|
||||
}
|
||||
|
||||
// RegisterExitHandler adds a Logrus Exit handler, call logrus.Exit to invoke
|
||||
// all handlers. The handlers will also be invoked when any Fatal log entry is
|
||||
// made.
|
||||
//
|
||||
// This method is useful when a caller wishes to use logrus to log a fatal
|
||||
// message but also needs to gracefully shutdown. An example usecase could be
|
||||
// closing database connections, or sending a alert that the application is
|
||||
// closing.
|
||||
func RegisterExitHandler(handler func()) {
|
||||
handlers = append(handlers, handler)
|
||||
}
|
||||
+23
-34
@@ -3,21 +3,11 @@ package logrus
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
var bufferPool *sync.Pool
|
||||
|
||||
func init() {
|
||||
bufferPool = &sync.Pool{
|
||||
New: func() interface{} {
|
||||
return new(bytes.Buffer)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Defines the key when adding errors using WithError.
|
||||
var ErrorKey = "error"
|
||||
|
||||
@@ -39,9 +29,6 @@ type Entry struct {
|
||||
|
||||
// Message passed to Debug, Info, Warn, Error, Fatal or Panic
|
||||
Message string
|
||||
|
||||
// When formatter is called in entry.log(), an Buffer may be set to entry
|
||||
Buffer *bytes.Buffer
|
||||
}
|
||||
|
||||
func NewEntry(logger *Logger) *Entry {
|
||||
@@ -52,15 +39,21 @@ func NewEntry(logger *Logger) *Entry {
|
||||
}
|
||||
}
|
||||
|
||||
// Returns a reader for the entry, which is a proxy to the formatter.
|
||||
func (entry *Entry) Reader() (*bytes.Buffer, error) {
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
return bytes.NewBuffer(serialized), err
|
||||
}
|
||||
|
||||
// Returns the string representation from the reader and ultimately the
|
||||
// formatter.
|
||||
func (entry *Entry) String() (string, error) {
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
reader, err := entry.Reader()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
str := string(serialized)
|
||||
return str, nil
|
||||
|
||||
return reader.String(), err
|
||||
}
|
||||
|
||||
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
||||
@@ -88,7 +81,6 @@ func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||
// This function is not declared with a pointer value because otherwise
|
||||
// race conditions will occur when using multiple goroutines
|
||||
func (entry Entry) log(level Level, msg string) {
|
||||
var buffer *bytes.Buffer
|
||||
entry.Time = time.Now()
|
||||
entry.Level = level
|
||||
entry.Message = msg
|
||||
@@ -98,23 +90,20 @@ func (entry Entry) log(level Level, msg string) {
|
||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
buffer = bufferPool.Get().(*bytes.Buffer)
|
||||
buffer.Reset()
|
||||
defer bufferPool.Put(buffer)
|
||||
entry.Buffer = buffer
|
||||
serialized, err := entry.Logger.Formatter.Format(&entry)
|
||||
entry.Buffer = nil
|
||||
|
||||
reader, err := entry.Reader()
|
||||
if err != nil {
|
||||
entry.Logger.mu.Lock()
|
||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
} else {
|
||||
entry.Logger.mu.Lock()
|
||||
_, err = entry.Logger.Out.Write(serialized)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
}
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
entry.Logger.mu.Lock()
|
||||
defer entry.Logger.mu.Unlock()
|
||||
|
||||
_, err = io.Copy(entry.Logger.Out, reader)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||
}
|
||||
|
||||
// To avoid Entry#log() returning a value that only would make sense for
|
||||
@@ -161,7 +150,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||
}
|
||||
Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panic(args ...interface{}) {
|
||||
@@ -209,7 +198,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(fmt.Sprintf(format, args...))
|
||||
}
|
||||
Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||
@@ -256,7 +245,7 @@ func (entry *Entry) Fatalln(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(entry.sprintlnn(args...))
|
||||
}
|
||||
Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicln(args ...interface{}) {
|
||||
|
||||
+9
-6
@@ -31,15 +31,18 @@ type Formatter interface {
|
||||
// It's not exported because it's still using Data in an opinionated way. It's to
|
||||
// avoid code duplication between the two default formatters.
|
||||
func prefixFieldClashes(data Fields) {
|
||||
if t, ok := data["time"]; ok {
|
||||
data["fields.time"] = t
|
||||
_, ok := data["time"]
|
||||
if ok {
|
||||
data["fields.time"] = data["time"]
|
||||
}
|
||||
|
||||
if m, ok := data["msg"]; ok {
|
||||
data["fields.msg"] = m
|
||||
_, ok = data["msg"]
|
||||
if ok {
|
||||
data["fields.msg"] = data["msg"]
|
||||
}
|
||||
|
||||
if l, ok := data["level"]; ok {
|
||||
data["fields.level"] = l
|
||||
_, ok = data["level"]
|
||||
if ok {
|
||||
data["fields.level"] = data["level"]
|
||||
}
|
||||
}
|
||||
|
||||
+3
-36
@@ -5,40 +5,9 @@ import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type fieldKey string
|
||||
type FieldMap map[fieldKey]string
|
||||
|
||||
const (
|
||||
FieldKeyMsg = "msg"
|
||||
FieldKeyLevel = "level"
|
||||
FieldKeyTime = "time"
|
||||
)
|
||||
|
||||
func (f FieldMap) resolve(key fieldKey) string {
|
||||
if k, ok := f[key]; ok {
|
||||
return k
|
||||
}
|
||||
|
||||
return string(key)
|
||||
}
|
||||
|
||||
type JSONFormatter struct {
|
||||
// TimestampFormat sets the format used for marshaling timestamps.
|
||||
TimestampFormat string
|
||||
|
||||
// DisableTimestamp allows disabling automatic timestamps in output
|
||||
DisableTimestamp bool
|
||||
|
||||
// FieldMap allows users to customize the names of keys for various fields.
|
||||
// As an example:
|
||||
// formatter := &JSONFormatter{
|
||||
// FieldMap: FieldMap{
|
||||
// FieldKeyTime: "@timestamp",
|
||||
// FieldKeyLevel: "@level",
|
||||
// FieldKeyLevel: "@message",
|
||||
// },
|
||||
// }
|
||||
FieldMap FieldMap
|
||||
}
|
||||
|
||||
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
@@ -60,11 +29,9 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
timestampFormat = DefaultTimestampFormat
|
||||
}
|
||||
|
||||
if !f.DisableTimestamp {
|
||||
data[f.FieldMap.resolve(FieldKeyTime)] = entry.Time.Format(timestampFormat)
|
||||
}
|
||||
data[f.FieldMap.resolve(FieldKeyMsg)] = entry.Message
|
||||
data[f.FieldMap.resolve(FieldKeyLevel)] = entry.Level.String()
|
||||
data["time"] = entry.Time.Format(timestampFormat)
|
||||
data["msg"] = entry.Message
|
||||
data["level"] = entry.Level.String()
|
||||
|
||||
serialized, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
|
||||
+33
-129
@@ -26,31 +26,8 @@ type Logger struct {
|
||||
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
||||
// logged. `logrus.Debug` is useful in
|
||||
Level Level
|
||||
// Used to sync writing to the log. Locking is enabled by Default
|
||||
mu MutexWrap
|
||||
// Reusable empty entry
|
||||
entryPool sync.Pool
|
||||
}
|
||||
|
||||
type MutexWrap struct {
|
||||
lock sync.Mutex
|
||||
disabled bool
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Lock() {
|
||||
if !mw.disabled {
|
||||
mw.lock.Lock()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Unlock() {
|
||||
if !mw.disabled {
|
||||
mw.lock.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
func (mw *MutexWrap) Disable() {
|
||||
mw.disabled = true
|
||||
// Used to sync writing to the log.
|
||||
mu sync.Mutex
|
||||
}
|
||||
|
||||
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
||||
@@ -74,235 +51,162 @@ func New() *Logger {
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) newEntry() *Entry {
|
||||
entry, ok := logger.entryPool.Get().(*Entry)
|
||||
if ok {
|
||||
return entry
|
||||
}
|
||||
return NewEntry(logger)
|
||||
}
|
||||
|
||||
func (logger *Logger) releaseEntry(entry *Entry) {
|
||||
logger.entryPool.Put(entry)
|
||||
}
|
||||
|
||||
// Adds a field to the log entry, note that it doesn't log until you call
|
||||
// Adds a field to the log entry, note that you it doesn't log until you call
|
||||
// Debug, Print, Info, Warn, Fatal or Panic. It only creates a log entry.
|
||||
// If you want multiple fields, use `WithFields`.
|
||||
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithField(key, value)
|
||||
return NewEntry(logger).WithField(key, value)
|
||||
}
|
||||
|
||||
// Adds a struct of fields to the log entry. All it does is call `WithField` for
|
||||
// each `Field`.
|
||||
func (logger *Logger) WithFields(fields Fields) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithFields(fields)
|
||||
return NewEntry(logger).WithFields(fields)
|
||||
}
|
||||
|
||||
// Add an error as single field to the log entry. All it does is call
|
||||
// `WithError` for the given `error`.
|
||||
func (logger *Logger) WithError(err error) *Entry {
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithError(err)
|
||||
return NewEntry(logger).WithError(err)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Debugf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Debugf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Infof(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Infof(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||
entry := logger.newEntry()
|
||||
entry.Printf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Printf(format, args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Errorf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Errorf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Fatalf(format, args...)
|
||||
}
|
||||
Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Panicf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Panicf(format, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Debug(args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Debug(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Debug(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Info(args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Info(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Print(args ...interface{}) {
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Info(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warn(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Warn(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warning(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Warn(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Error(args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Error(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Error(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatal(args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Fatal(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Fatal(args...)
|
||||
}
|
||||
Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panic(args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Panic(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Panic(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugln(args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Debugln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Debugln(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Infoln(args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Infoln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Infoln(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Println(args ...interface{}) {
|
||||
entry := logger.newEntry()
|
||||
entry.Println(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Println(args...)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnln(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Warnln(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningln(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Warnln(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorln(args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Errorln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Errorln(args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Fatalln(args...)
|
||||
}
|
||||
Exit(1)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicln(args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
entry := logger.newEntry()
|
||||
entry.Panicln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
NewEntry(logger).Panicln(args...)
|
||||
}
|
||||
}
|
||||
|
||||
//When file is opened with appending mode, it's safe to
|
||||
//write concurrently to a file (within 4k message on Linux).
|
||||
//In these cases user can choose to disable the lock.
|
||||
func (logger *Logger) SetNoLock() {
|
||||
logger.mu.Disable()
|
||||
}
|
||||
|
||||
-10
@@ -1,10 +0,0 @@
|
||||
// +build appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import "io"
|
||||
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal(f io.Writer) bool {
|
||||
return true
|
||||
}
|
||||
-1
@@ -1,5 +1,4 @@
|
||||
// +build darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
|
||||
-2
@@ -3,8 +3,6 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import "syscall"
|
||||
|
||||
+4
-11
@@ -4,25 +4,18 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal(f io.Writer) bool {
|
||||
func IsTerminal() bool {
|
||||
fd := syscall.Stderr
|
||||
var termios Termios
|
||||
switch v := f.(type) {
|
||||
case *os.File:
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(v.Fd()), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, uintptr(fd), ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
}
|
||||
|
||||
+4
-10
@@ -1,21 +1,15 @@
|
||||
// +build solaris,!appengine
|
||||
// +build solaris
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
func IsTerminal(f io.Writer) bool {
|
||||
switch v := f.(type) {
|
||||
case *os.File:
|
||||
_, err := unix.IoctlGetTermios(int(v.Fd()), unix.TCGETA)
|
||||
return err == nil
|
||||
default:
|
||||
return false
|
||||
}
|
||||
func IsTerminal() bool {
|
||||
_, err := unix.IoctlGetTermios(int(os.Stdout.Fd()), unix.TCGETA)
|
||||
return err == nil
|
||||
}
|
||||
|
||||
+6
-12
@@ -3,13 +3,11 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows,!appengine
|
||||
// +build windows
|
||||
|
||||
package logrus
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
@@ -21,13 +19,9 @@ var (
|
||||
)
|
||||
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal(f io.Writer) bool {
|
||||
switch v := f.(type) {
|
||||
case *os.File:
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(v.Fd()), uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
default:
|
||||
return false
|
||||
}
|
||||
func IsTerminal() bool {
|
||||
fd := syscall.Stderr
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, uintptr(fd), uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
}
|
||||
|
||||
+24
-52
@@ -3,9 +3,9 @@ package logrus
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -20,10 +20,16 @@ const (
|
||||
|
||||
var (
|
||||
baseTimestamp time.Time
|
||||
isTerminal bool
|
||||
)
|
||||
|
||||
func init() {
|
||||
baseTimestamp = time.Now()
|
||||
isTerminal = IsTerminal()
|
||||
}
|
||||
|
||||
func miniTS() int {
|
||||
return int(time.Since(baseTimestamp) / time.Second)
|
||||
}
|
||||
|
||||
type TextFormatter struct {
|
||||
@@ -48,32 +54,10 @@ type TextFormatter struct {
|
||||
// that log extremely frequently and don't use the JSON formatter this may not
|
||||
// be desired.
|
||||
DisableSorting bool
|
||||
|
||||
// QuoteEmptyFields will wrap empty fields in quotes if true
|
||||
QuoteEmptyFields bool
|
||||
|
||||
// QuoteCharacter can be set to the override the default quoting character "
|
||||
// with something else. For example: ', or `.
|
||||
QuoteCharacter string
|
||||
|
||||
// Whether the logger's out is to a terminal
|
||||
isTerminal bool
|
||||
|
||||
sync.Once
|
||||
}
|
||||
|
||||
func (f *TextFormatter) init(entry *Entry) {
|
||||
if len(f.QuoteCharacter) == 0 {
|
||||
f.QuoteCharacter = "\""
|
||||
}
|
||||
if entry.Logger != nil {
|
||||
f.isTerminal = IsTerminal(entry.Logger.Out)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
var b *bytes.Buffer
|
||||
keys := make([]string, 0, len(entry.Data))
|
||||
var keys []string = make([]string, 0, len(entry.Data))
|
||||
for k := range entry.Data {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
@@ -81,17 +65,13 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
if !f.DisableSorting {
|
||||
sort.Strings(keys)
|
||||
}
|
||||
if entry.Buffer != nil {
|
||||
b = entry.Buffer
|
||||
} else {
|
||||
b = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
|
||||
prefixFieldClashes(entry.Data)
|
||||
|
||||
f.Do(func() { f.init(entry) })
|
||||
|
||||
isColored := (f.ForceColors || f.isTerminal) && !f.DisableColors
|
||||
isColorTerminal := isTerminal && (runtime.GOOS != "windows")
|
||||
isColored := (f.ForceColors || isColorTerminal) && !f.DisableColors
|
||||
|
||||
timestampFormat := f.TimestampFormat
|
||||
if timestampFormat == "" {
|
||||
@@ -131,59 +111,51 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
||||
|
||||
levelText := strings.ToUpper(entry.Level.String())[0:4]
|
||||
|
||||
if f.DisableTimestamp {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m %-44s ", levelColor, levelText, entry.Message)
|
||||
} else if !f.FullTimestamp {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), entry.Message)
|
||||
if !f.FullTimestamp {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
|
||||
} else {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s] %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), entry.Message)
|
||||
}
|
||||
for _, k := range keys {
|
||||
v := entry.Data[k]
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
||||
f.appendValue(b, v)
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=%+v", levelColor, k, v)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *TextFormatter) needsQuoting(text string) bool {
|
||||
if f.QuoteEmptyFields && len(text) == 0 {
|
||||
return true
|
||||
}
|
||||
func needsQuoting(text string) bool {
|
||||
for _, ch := range text {
|
||||
if !((ch >= 'a' && ch <= 'z') ||
|
||||
(ch >= 'A' && ch <= 'Z') ||
|
||||
(ch >= '0' && ch <= '9') ||
|
||||
ch == '-' || ch == '.') {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
||||
|
||||
b.WriteString(key)
|
||||
b.WriteByte('=')
|
||||
f.appendValue(b, value)
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
|
||||
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
|
||||
switch value := value.(type) {
|
||||
case string:
|
||||
if !f.needsQuoting(value) {
|
||||
if needsQuoting(value) {
|
||||
b.WriteString(value)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, value, f.QuoteCharacter)
|
||||
fmt.Fprintf(b, "%q", value)
|
||||
}
|
||||
case error:
|
||||
errmsg := value.Error()
|
||||
if !f.needsQuoting(errmsg) {
|
||||
if needsQuoting(errmsg) {
|
||||
b.WriteString(errmsg)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%s%v%s", f.QuoteCharacter, errmsg, f.QuoteCharacter)
|
||||
fmt.Fprintf(b, "%q", value)
|
||||
}
|
||||
default:
|
||||
fmt.Fprint(b, value)
|
||||
}
|
||||
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
|
||||
+4
-35
@@ -7,52 +7,21 @@ import (
|
||||
)
|
||||
|
||||
func (logger *Logger) Writer() *io.PipeWriter {
|
||||
return logger.WriterLevel(InfoLevel)
|
||||
}
|
||||
|
||||
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
||||
return NewEntry(logger).WriterLevel(level)
|
||||
}
|
||||
|
||||
func (entry *Entry) Writer() *io.PipeWriter {
|
||||
return entry.WriterLevel(InfoLevel)
|
||||
}
|
||||
|
||||
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
||||
reader, writer := io.Pipe()
|
||||
|
||||
var printFunc func(args ...interface{})
|
||||
|
||||
switch level {
|
||||
case DebugLevel:
|
||||
printFunc = entry.Debug
|
||||
case InfoLevel:
|
||||
printFunc = entry.Info
|
||||
case WarnLevel:
|
||||
printFunc = entry.Warn
|
||||
case ErrorLevel:
|
||||
printFunc = entry.Error
|
||||
case FatalLevel:
|
||||
printFunc = entry.Fatal
|
||||
case PanicLevel:
|
||||
printFunc = entry.Panic
|
||||
default:
|
||||
printFunc = entry.Print
|
||||
}
|
||||
|
||||
go entry.writerScanner(reader, printFunc)
|
||||
go logger.writerScanner(reader)
|
||||
runtime.SetFinalizer(writer, writerFinalizer)
|
||||
|
||||
return writer
|
||||
}
|
||||
|
||||
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||
func (logger *Logger) writerScanner(reader *io.PipeReader) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
printFunc(scanner.Text())
|
||||
logger.Print(scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
entry.Errorf("Error while reading from Writer: %s", err)
|
||||
logger.Errorf("Error while reading from Writer: %s", err)
|
||||
}
|
||||
reader.Close()
|
||||
}
|
||||
|
||||
+7
-17
@@ -431,7 +431,7 @@ func strctVal(s interface{}) reflect.Value {
|
||||
v := reflect.ValueOf(s)
|
||||
|
||||
// if pointer get the underlying element≤
|
||||
for v.Kind() == reflect.Ptr {
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
@@ -530,22 +530,15 @@ func (s *Struct) nested(val reflect.Value) interface{} {
|
||||
finalVal = m
|
||||
}
|
||||
case reflect.Map:
|
||||
// get the element type of the map
|
||||
mapElem := val.Type()
|
||||
switch val.Type().Kind() {
|
||||
case reflect.Ptr, reflect.Array, reflect.Map,
|
||||
reflect.Slice, reflect.Chan:
|
||||
mapElem = val.Type().Elem()
|
||||
if mapElem.Kind() == reflect.Ptr {
|
||||
mapElem = mapElem.Elem()
|
||||
}
|
||||
v := val.Type().Elem()
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
// only iterate over struct types, ie: map[string]StructType,
|
||||
// map[string][]StructType,
|
||||
if mapElem.Kind() == reflect.Struct ||
|
||||
(mapElem.Kind() == reflect.Slice &&
|
||||
mapElem.Elem().Kind() == reflect.Struct) {
|
||||
if v.Kind() == reflect.Struct ||
|
||||
(v.Kind() == reflect.Slice && v.Elem().Kind() == reflect.Struct) {
|
||||
m := make(map[string]interface{}, val.Len())
|
||||
for _, k := range val.MapKeys() {
|
||||
m[k.String()] = s.nested(val.MapIndex(k))
|
||||
@@ -565,10 +558,7 @@ func (s *Struct) nested(val reflect.Value) interface{} {
|
||||
// TODO(arslan): should this be optional?
|
||||
// do not iterate of non struct types, just pass the value. Ie: []int,
|
||||
// []string, co... We only iterate further if it's a struct.
|
||||
// i.e []foo or []*foo
|
||||
if val.Type().Elem().Kind() != reflect.Struct &&
|
||||
!(val.Type().Elem().Kind() == reflect.Ptr &&
|
||||
val.Type().Elem().Elem().Kind() == reflect.Struct) {
|
||||
if val.Type().Elem().Kind() != reflect.Struct {
|
||||
finalVal = val.Interface()
|
||||
break
|
||||
}
|
||||
|
||||
+5
-3
@@ -28,10 +28,12 @@ func newTomlTree() *TomlTree {
|
||||
}
|
||||
|
||||
// TreeFromMap initializes a new TomlTree object using the given map.
|
||||
func TreeFromMap(m map[string]interface{}) *TomlTree {
|
||||
return &TomlTree{
|
||||
values: m,
|
||||
func TreeFromMap(m map[string]interface{}) (*TomlTree, error) {
|
||||
result, err := toTree(m)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return result.(*TomlTree), nil
|
||||
}
|
||||
|
||||
// Has returns a boolean indicating if the given key exists.
|
||||
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
package toml
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"time"
|
||||
)
|
||||
|
||||
// supported values:
|
||||
// string, bool, int64, uint64, float64, time.Time, int, int8, int16, int32, uint, uint8, uint16, uint32, float32
|
||||
|
||||
var kindToTypeMapping = map[reflect.Kind]reflect.Type{
|
||||
reflect.Bool: reflect.TypeOf(true),
|
||||
reflect.String: reflect.TypeOf(""),
|
||||
reflect.Float32: reflect.TypeOf(float64(1)),
|
||||
reflect.Float64: reflect.TypeOf(float64(1)),
|
||||
reflect.Int: reflect.TypeOf(int64(1)),
|
||||
reflect.Int8: reflect.TypeOf(int64(1)),
|
||||
reflect.Int16: reflect.TypeOf(int64(1)),
|
||||
reflect.Int32: reflect.TypeOf(int64(1)),
|
||||
reflect.Int64: reflect.TypeOf(int64(1)),
|
||||
reflect.Uint: reflect.TypeOf(uint64(1)),
|
||||
reflect.Uint8: reflect.TypeOf(uint64(1)),
|
||||
reflect.Uint16: reflect.TypeOf(uint64(1)),
|
||||
reflect.Uint32: reflect.TypeOf(uint64(1)),
|
||||
reflect.Uint64: reflect.TypeOf(uint64(1)),
|
||||
}
|
||||
|
||||
func simpleValueCoercion(object interface{}) (interface{}, error) {
|
||||
switch original := object.(type) {
|
||||
case string, bool, int64, uint64, float64, time.Time:
|
||||
return original, nil
|
||||
case int:
|
||||
return int64(original), nil
|
||||
case int8:
|
||||
return int64(original), nil
|
||||
case int16:
|
||||
return int64(original), nil
|
||||
case int32:
|
||||
return int64(original), nil
|
||||
case uint:
|
||||
return uint64(original), nil
|
||||
case uint8:
|
||||
return uint64(original), nil
|
||||
case uint16:
|
||||
return uint64(original), nil
|
||||
case uint32:
|
||||
return uint64(original), nil
|
||||
case float32:
|
||||
return float64(original), nil
|
||||
default:
|
||||
return nil, fmt.Errorf("cannot convert type %T to TomlTree", object)
|
||||
}
|
||||
}
|
||||
|
||||
func sliceToTree(object interface{}) (interface{}, error) {
|
||||
// arrays are a bit tricky, since they can represent either a
|
||||
// collection of simple values, which is represented by one
|
||||
// *tomlValue, or an array of tables, which is represented by an
|
||||
// array of *TomlTree.
|
||||
|
||||
// holding the assumption that this function is called from toTree only when value.Kind() is Array or Slice
|
||||
value := reflect.ValueOf(object)
|
||||
insideType := value.Type().Elem()
|
||||
length := value.Len()
|
||||
if insideType.Kind() == reflect.Map {
|
||||
// this is considered as an array of tables
|
||||
tablesArray := make([]*TomlTree, 0, length)
|
||||
for i := 0; i < length; i++ {
|
||||
table := value.Index(i)
|
||||
tree, err := toTree(table.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
tablesArray = append(tablesArray, tree.(*TomlTree))
|
||||
}
|
||||
return tablesArray, nil
|
||||
}
|
||||
|
||||
sliceType := kindToTypeMapping[insideType.Kind()]
|
||||
if sliceType == nil {
|
||||
sliceType = insideType
|
||||
}
|
||||
|
||||
arrayValue := reflect.MakeSlice(reflect.SliceOf(sliceType), 0, length)
|
||||
|
||||
for i := 0; i < length; i++ {
|
||||
val := value.Index(i).Interface()
|
||||
simpleValue, err := simpleValueCoercion(val)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arrayValue = reflect.Append(arrayValue, reflect.ValueOf(simpleValue))
|
||||
}
|
||||
return &tomlValue{arrayValue.Interface(), Position{}}, nil
|
||||
}
|
||||
|
||||
func toTree(object interface{}) (interface{}, error) {
|
||||
value := reflect.ValueOf(object)
|
||||
|
||||
if value.Kind() == reflect.Map {
|
||||
values := map[string]interface{}{}
|
||||
keys := value.MapKeys()
|
||||
for _, key := range keys {
|
||||
k, ok := key.Interface().(string)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("map key needs to be a string, not %T", key.Interface())
|
||||
}
|
||||
|
||||
v := value.MapIndex(key)
|
||||
newValue, err := toTree(v.Interface())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
values[k] = newValue
|
||||
}
|
||||
return &TomlTree{values, Position{}}, nil
|
||||
}
|
||||
|
||||
if value.Kind() == reflect.Array || value.Kind() == reflect.Slice {
|
||||
return sliceToTree(object)
|
||||
}
|
||||
|
||||
simpleValue, err := simpleValueCoercion(object)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &tomlValue{simpleValue, Position{}}, nil
|
||||
}
|
||||
+1
-4
@@ -95,7 +95,7 @@ func (t *TomlTree) writeTo(w io.Writer, indent, keyspace string, bytesCount int6
|
||||
for _, k := range simpleValuesKeys {
|
||||
v, ok := t.values[k].(*tomlValue)
|
||||
if !ok {
|
||||
return bytesCount, fmt.Errorf("invalid key type at %s: %T", k, t.values[k])
|
||||
return bytesCount, fmt.Errorf("invalid value type at %s: %T", k, t.values[k])
|
||||
}
|
||||
|
||||
repr, err := tomlValueStringRepresentation(v.value)
|
||||
@@ -201,9 +201,6 @@ func (t *TomlTree) ToMap() map[string]interface{} {
|
||||
result[k] = array
|
||||
case *TomlTree:
|
||||
result[k] = node.ToMap()
|
||||
case map[string]interface{}:
|
||||
sub := TreeFromMap(node)
|
||||
result[k] = sub.ToMap()
|
||||
case *tomlValue:
|
||||
result[k] = node.value
|
||||
}
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
+269
@@ -0,0 +1,269 @@
|
||||
// Package errors provides simple error handling primitives.
|
||||
//
|
||||
// The traditional error handling idiom in Go is roughly akin to
|
||||
//
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
//
|
||||
// which applied recursively up the call stack results in error reports
|
||||
// without context or debugging information. The errors package allows
|
||||
// programmers to add context to the failure path in their code in a way
|
||||
// that does not destroy the original value of the error.
|
||||
//
|
||||
// Adding context to an error
|
||||
//
|
||||
// The errors.Wrap function returns a new error that adds context to the
|
||||
// original error by recording a stack trace at the point Wrap is called,
|
||||
// and the supplied message. For example
|
||||
//
|
||||
// _, err := ioutil.ReadAll(r)
|
||||
// if err != nil {
|
||||
// return errors.Wrap(err, "read failed")
|
||||
// }
|
||||
//
|
||||
// If additional control is required the errors.WithStack and errors.WithMessage
|
||||
// functions destructure errors.Wrap into its component operations of annotating
|
||||
// an error with a stack trace and an a message, respectively.
|
||||
//
|
||||
// Retrieving the cause of an error
|
||||
//
|
||||
// Using errors.Wrap constructs a stack of errors, adding context to the
|
||||
// preceding error. Depending on the nature of the error it may be necessary
|
||||
// to reverse the operation of errors.Wrap to retrieve the original error
|
||||
// for inspection. Any error value which implements this interface
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// can be inspected by errors.Cause. errors.Cause will recursively retrieve
|
||||
// the topmost error which does not implement causer, which is assumed to be
|
||||
// the original cause. For example:
|
||||
//
|
||||
// switch err := errors.Cause(err).(type) {
|
||||
// case *MyError:
|
||||
// // handle specifically
|
||||
// default:
|
||||
// // unknown error
|
||||
// }
|
||||
//
|
||||
// causer interface is not exported by this package, but is considered a part
|
||||
// of stable public API.
|
||||
//
|
||||
// Formatted printing of errors
|
||||
//
|
||||
// All error values returned from this package implement fmt.Formatter and can
|
||||
// be formatted by the fmt package. The following verbs are supported
|
||||
//
|
||||
// %s print the error. If the error has a Cause it will be
|
||||
// printed recursively
|
||||
// %v see %s
|
||||
// %+v extended format. Each Frame of the error's StackTrace will
|
||||
// be printed in detail.
|
||||
//
|
||||
// Retrieving the stack trace of an error or wrapper
|
||||
//
|
||||
// New, Errorf, Wrap, and Wrapf record a stack trace at the point they are
|
||||
// invoked. This information can be retrieved with the following interface.
|
||||
//
|
||||
// type stackTracer interface {
|
||||
// StackTrace() errors.StackTrace
|
||||
// }
|
||||
//
|
||||
// Where errors.StackTrace is defined as
|
||||
//
|
||||
// type StackTrace []Frame
|
||||
//
|
||||
// The Frame type represents a call site in the stack trace. Frame supports
|
||||
// the fmt.Formatter interface that can be used for printing information about
|
||||
// the stack trace of this error. For example:
|
||||
//
|
||||
// if err, ok := err.(stackTracer); ok {
|
||||
// for _, f := range err.StackTrace() {
|
||||
// fmt.Printf("%+s:%d", f)
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// stackTracer interface is not exported by this package, but is considered a part
|
||||
// of stable public API.
|
||||
//
|
||||
// See the documentation for Frame.Format for more details.
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
// New returns an error with the supplied message.
|
||||
// New also records the stack trace at the point it was called.
|
||||
func New(message string) error {
|
||||
return &fundamental{
|
||||
msg: message,
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Errorf formats according to a format specifier and returns the string
|
||||
// as a value that satisfies error.
|
||||
// Errorf also records the stack trace at the point it was called.
|
||||
func Errorf(format string, args ...interface{}) error {
|
||||
return &fundamental{
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
stack: callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// fundamental is an error that has a message and a stack, but no caller.
|
||||
type fundamental struct {
|
||||
msg string
|
||||
*stack
|
||||
}
|
||||
|
||||
func (f *fundamental) Error() string { return f.msg }
|
||||
|
||||
func (f *fundamental) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
io.WriteString(s, f.msg)
|
||||
f.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, f.msg)
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", f.msg)
|
||||
}
|
||||
}
|
||||
|
||||
// WithStack annotates err with a stack trace at the point WithStack was called.
|
||||
// If err is nil, WithStack returns nil.
|
||||
func WithStack(err error) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
type withStack struct {
|
||||
error
|
||||
*stack
|
||||
}
|
||||
|
||||
func (w *withStack) Cause() error { return w.error }
|
||||
|
||||
func (w *withStack) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v", w.Cause())
|
||||
w.stack.Format(s, verb)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's':
|
||||
io.WriteString(s, w.Error())
|
||||
case 'q':
|
||||
fmt.Fprintf(s, "%q", w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap returns an error annotating err with a stack trace
|
||||
// at the point Wrap is called, and the supplied message.
|
||||
// If err is nil, Wrap returns nil.
|
||||
func Wrap(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// Wrapf returns an error annotating err with a stack trace
|
||||
// at the point Wrapf is call, and the format specifier.
|
||||
// If err is nil, Wrapf returns nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
err = &withMessage{
|
||||
cause: err,
|
||||
msg: fmt.Sprintf(format, args...),
|
||||
}
|
||||
return &withStack{
|
||||
err,
|
||||
callers(),
|
||||
}
|
||||
}
|
||||
|
||||
// WithMessage annotates err with a new message.
|
||||
// If err is nil, WithMessage returns nil.
|
||||
func WithMessage(err error, message string) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
return &withMessage{
|
||||
cause: err,
|
||||
msg: message,
|
||||
}
|
||||
}
|
||||
|
||||
type withMessage struct {
|
||||
cause error
|
||||
msg string
|
||||
}
|
||||
|
||||
func (w *withMessage) Error() string { return w.msg + ": " + w.cause.Error() }
|
||||
func (w *withMessage) Cause() error { return w.cause }
|
||||
|
||||
func (w *withMessage) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
if s.Flag('+') {
|
||||
fmt.Fprintf(s, "%+v\n", w.Cause())
|
||||
io.WriteString(s, w.msg)
|
||||
return
|
||||
}
|
||||
fallthrough
|
||||
case 's', 'q':
|
||||
io.WriteString(s, w.Error())
|
||||
}
|
||||
}
|
||||
|
||||
// Cause returns the underlying cause of the error, if possible.
|
||||
// An error value has a cause if it implements the following
|
||||
// interface:
|
||||
//
|
||||
// type causer interface {
|
||||
// Cause() error
|
||||
// }
|
||||
//
|
||||
// If the error does not implement Cause, the original error will
|
||||
// be returned. If the error is nil, nil will be returned without further
|
||||
// investigation.
|
||||
func Cause(err error) error {
|
||||
type causer interface {
|
||||
Cause() error
|
||||
}
|
||||
|
||||
for err != nil {
|
||||
cause, ok := err.(causer)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
err = cause.Cause()
|
||||
}
|
||||
return err
|
||||
}
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
package errors
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"path"
|
||||
"runtime"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Frame represents a program counter inside a stack frame.
|
||||
type Frame uintptr
|
||||
|
||||
// pc returns the program counter for this frame;
|
||||
// multiple frames may have the same PC value.
|
||||
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
|
||||
|
||||
// file returns the full path to the file that contains the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) file() string {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return "unknown"
|
||||
}
|
||||
file, _ := fn.FileLine(f.pc())
|
||||
return file
|
||||
}
|
||||
|
||||
// line returns the line number of source code of the
|
||||
// function for this Frame's pc.
|
||||
func (f Frame) line() int {
|
||||
fn := runtime.FuncForPC(f.pc())
|
||||
if fn == nil {
|
||||
return 0
|
||||
}
|
||||
_, line := fn.FileLine(f.pc())
|
||||
return line
|
||||
}
|
||||
|
||||
// Format formats the frame according to the fmt.Formatter interface.
|
||||
//
|
||||
// %s source file
|
||||
// %d source line
|
||||
// %n function name
|
||||
// %v equivalent to %s:%d
|
||||
//
|
||||
// Format accepts flags that alter the printing of some verbs, as follows:
|
||||
//
|
||||
// %+s path of source file relative to the compile time GOPATH
|
||||
// %+v equivalent to %+s:%d
|
||||
func (f Frame) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 's':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
pc := f.pc()
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn == nil {
|
||||
io.WriteString(s, "unknown")
|
||||
} else {
|
||||
file, _ := fn.FileLine(pc)
|
||||
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
|
||||
}
|
||||
default:
|
||||
io.WriteString(s, path.Base(f.file()))
|
||||
}
|
||||
case 'd':
|
||||
fmt.Fprintf(s, "%d", f.line())
|
||||
case 'n':
|
||||
name := runtime.FuncForPC(f.pc()).Name()
|
||||
io.WriteString(s, funcname(name))
|
||||
case 'v':
|
||||
f.Format(s, 's')
|
||||
io.WriteString(s, ":")
|
||||
f.Format(s, 'd')
|
||||
}
|
||||
}
|
||||
|
||||
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
|
||||
type StackTrace []Frame
|
||||
|
||||
func (st StackTrace) Format(s fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case s.Flag('+'):
|
||||
for _, f := range st {
|
||||
fmt.Fprintf(s, "\n%+v", f)
|
||||
}
|
||||
case s.Flag('#'):
|
||||
fmt.Fprintf(s, "%#v", []Frame(st))
|
||||
default:
|
||||
fmt.Fprintf(s, "%v", []Frame(st))
|
||||
}
|
||||
case 's':
|
||||
fmt.Fprintf(s, "%s", []Frame(st))
|
||||
}
|
||||
}
|
||||
|
||||
// stack represents a stack of program counters.
|
||||
type stack []uintptr
|
||||
|
||||
func (s *stack) Format(st fmt.State, verb rune) {
|
||||
switch verb {
|
||||
case 'v':
|
||||
switch {
|
||||
case st.Flag('+'):
|
||||
for _, pc := range *s {
|
||||
f := Frame(pc)
|
||||
fmt.Fprintf(st, "\n%+v", f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (s *stack) StackTrace() StackTrace {
|
||||
f := make([]Frame, len(*s))
|
||||
for i := 0; i < len(f); i++ {
|
||||
f[i] = Frame((*s)[i])
|
||||
}
|
||||
return f
|
||||
}
|
||||
|
||||
func callers() *stack {
|
||||
const depth = 32
|
||||
var pcs [depth]uintptr
|
||||
n := runtime.Callers(3, pcs[:])
|
||||
var st stack = pcs[0:n]
|
||||
return &st
|
||||
}
|
||||
|
||||
// funcname removes the path prefix component of a function's name reported by func.Name().
|
||||
func funcname(name string) string {
|
||||
i := strings.LastIndex(name, "/")
|
||||
name = name[i+1:]
|
||||
i = strings.Index(name, ".")
|
||||
return name[i+1:]
|
||||
}
|
||||
|
||||
func trimGOPATH(name, file string) string {
|
||||
// Here we want to get the source file path relative to the compile time
|
||||
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
|
||||
// GOPATH at runtime, but we can infer the number of path segments in the
|
||||
// GOPATH. We note that fn.Name() returns the function name qualified by
|
||||
// the import path, which does not include the GOPATH. Thus we can trim
|
||||
// segments from the beginning of the file path until the number of path
|
||||
// separators remaining is one more than the number of path separators in
|
||||
// the function name. For example, given:
|
||||
//
|
||||
// GOPATH /home/user
|
||||
// file /home/user/src/pkg/sub/file.go
|
||||
// fn.Name() pkg/sub.Type.Method
|
||||
//
|
||||
// We want to produce:
|
||||
//
|
||||
// pkg/sub/file.go
|
||||
//
|
||||
// From this we can easily see that fn.Name() has one less path separator
|
||||
// than our desired output. We count separators from the end of the file
|
||||
// path until it finds two more than in the function name and then move
|
||||
// one character forward to preserve the initial path segment without a
|
||||
// leading separator.
|
||||
const sep = "/"
|
||||
goal := strings.Count(name, sep) + 2
|
||||
i := len(file)
|
||||
for n := 0; n < goal; n++ {
|
||||
i = strings.LastIndex(file[:i], sep)
|
||||
if i == -1 {
|
||||
// not enough separators found, set i so that the slice expression
|
||||
// below leaves file unmodified
|
||||
i = -len(sep)
|
||||
break
|
||||
}
|
||||
}
|
||||
// get back to 0 or trim the leading separator
|
||||
file = file[i+len(sep):]
|
||||
return file
|
||||
}
|
||||
+7
-9
@@ -10,9 +10,8 @@ import (
|
||||
"github.com/spf13/pflag"
|
||||
)
|
||||
|
||||
// Annotations for Bash completion.
|
||||
const (
|
||||
BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions"
|
||||
BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extentions"
|
||||
BashCompCustom = "cobra_annotation_bash_completion_custom"
|
||||
BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag"
|
||||
BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir"
|
||||
@@ -23,7 +22,7 @@ func preamble(out io.Writer, name string) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
preamStr := `
|
||||
_, err = fmt.Fprint(out, `
|
||||
__debug()
|
||||
{
|
||||
if [[ -n ${BASH_COMP_DEBUG_FILE} ]]; then
|
||||
@@ -247,8 +246,7 @@ __handle_word()
|
||||
__handle_word
|
||||
}
|
||||
|
||||
`
|
||||
_, err = fmt.Fprint(out, preamStr)
|
||||
`)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -403,8 +401,10 @@ func writeLocalNonPersistentFlag(flag *pflag.Flag, w io.Writer) error {
|
||||
format += "="
|
||||
}
|
||||
format += "\")\n"
|
||||
_, err := fmt.Fprintf(w, format, name)
|
||||
return err
|
||||
if _, err := fmt.Fprintf(w, format, name); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func writeFlags(cmd *Command, w io.Writer) error {
|
||||
@@ -568,7 +568,6 @@ func gen(cmd *Command, w io.Writer) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// GenBashCompletion generates bash completion file and writes to the passed writer.
|
||||
func (cmd *Command) GenBashCompletion(w io.Writer) error {
|
||||
if err := preamble(w, cmd.Name()); err != nil {
|
||||
return err
|
||||
@@ -588,7 +587,6 @@ func nonCompletableFlag(flag *pflag.Flag) bool {
|
||||
return flag.Hidden || len(flag.Deprecated) > 0
|
||||
}
|
||||
|
||||
// GenBashCompletionFile generates bash completion file.
|
||||
func (cmd *Command) GenBashCompletionFile(filename string) error {
|
||||
outFile, err := os.Create(filename)
|
||||
if err != nil {
|
||||
|
||||
+19
-18
@@ -37,37 +37,38 @@ var templateFuncs = template.FuncMap{
|
||||
|
||||
var initializers []func()
|
||||
|
||||
// EnablePrefixMatching allows to set automatic prefix matching. Automatic prefix matching can be a dangerous thing
|
||||
// to automatically enable in CLI tools.
|
||||
// Set this to true to enable it.
|
||||
// automatic prefix matching can be a dangerous thing to automatically enable in CLI tools.
|
||||
// Set this to true to enable it
|
||||
var EnablePrefixMatching = false
|
||||
|
||||
// EnableCommandSorting controls sorting of the slice of commands, which is turned on by default.
|
||||
// To disable sorting, set it to false.
|
||||
//EnableCommandSorting controls sorting of the slice of commands, which is turned on by default.
|
||||
//To disable sorting, set it to false.
|
||||
var EnableCommandSorting = true
|
||||
|
||||
// AddTemplateFunc adds a template function that's available to Usage and Help
|
||||
// template generation.
|
||||
//AddTemplateFunc adds a template function that's available to Usage and Help
|
||||
//template generation.
|
||||
func AddTemplateFunc(name string, tmplFunc interface{}) {
|
||||
templateFuncs[name] = tmplFunc
|
||||
}
|
||||
|
||||
// AddTemplateFuncs adds multiple template functions availalble to Usage and
|
||||
// Help template generation.
|
||||
//AddTemplateFuncs adds multiple template functions availalble to Usage and
|
||||
//Help template generation.
|
||||
func AddTemplateFuncs(tmplFuncs template.FuncMap) {
|
||||
for k, v := range tmplFuncs {
|
||||
templateFuncs[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// OnInitialize takes a series of func() arguments and appends them to a slice of func().
|
||||
//OnInitialize takes a series of func() arguments and appends them to a slice of func().
|
||||
func OnInitialize(y ...func()) {
|
||||
initializers = append(initializers, y...)
|
||||
for _, x := range y {
|
||||
initializers = append(initializers, x)
|
||||
}
|
||||
}
|
||||
|
||||
// Gt takes two types and checks whether the first type is greater than the second. In case of types Arrays, Chans,
|
||||
// Maps and Slices, Gt will compare their lengths. Ints are compared directly while strings are first parsed as
|
||||
// ints and then compared.
|
||||
//Gt takes two types and checks whether the first type is greater than the second. In case of types Arrays, Chans,
|
||||
//Maps and Slices, Gt will compare their lengths. Ints are compared directly while strings are first parsed as
|
||||
//ints and then compared.
|
||||
func Gt(a interface{}, b interface{}) bool {
|
||||
var left, right int64
|
||||
av := reflect.ValueOf(a)
|
||||
@@ -95,7 +96,7 @@ func Gt(a interface{}, b interface{}) bool {
|
||||
return left > right
|
||||
}
|
||||
|
||||
// Eq takes two types and checks whether they are equal. Supported types are int and string. Unsupported types will panic.
|
||||
//Eq takes two types and checks whether they are equal. Supported types are int and string. Unsupported types will panic.
|
||||
func Eq(a interface{}, b interface{}) bool {
|
||||
av := reflect.ValueOf(a)
|
||||
bv := reflect.ValueOf(b)
|
||||
@@ -115,7 +116,7 @@ func trimRightSpace(s string) string {
|
||||
return strings.TrimRightFunc(s, unicode.IsSpace)
|
||||
}
|
||||
|
||||
// appendIfNotPresent will append stringToAppend to the end of s, but only if it's not yet present in s.
|
||||
// appendIfNotPresent will append stringToAppend to the end of s, but only if it's not yet present in s
|
||||
func appendIfNotPresent(s, stringToAppend string) string {
|
||||
if strings.Contains(s, stringToAppend) {
|
||||
return s
|
||||
@@ -123,7 +124,7 @@ func appendIfNotPresent(s, stringToAppend string) string {
|
||||
return s + " " + stringToAppend
|
||||
}
|
||||
|
||||
// rpad adds padding to the right of a string.
|
||||
//rpad adds padding to the right of a string
|
||||
func rpad(s string, padding int) string {
|
||||
template := fmt.Sprintf("%%-%ds", padding)
|
||||
return fmt.Sprintf(template, s)
|
||||
@@ -137,7 +138,7 @@ func tmpl(w io.Writer, text string, data interface{}) error {
|
||||
return t.Execute(w, data)
|
||||
}
|
||||
|
||||
// ld compares two strings and returns the levenshtein distance between them.
|
||||
// ld compares two strings and returns the levenshtein distance between them
|
||||
func ld(s, t string, ignoreCase bool) int {
|
||||
if ignoreCase {
|
||||
s = strings.ToLower(s)
|
||||
|
||||
+94
-143
@@ -57,9 +57,6 @@ type Command struct {
|
||||
Deprecated string
|
||||
// Is this command hidden and should NOT show up in the list of available commands?
|
||||
Hidden bool
|
||||
// Annotations are key/value pairs that can be used by applications to identify or
|
||||
// group commands
|
||||
Annotations map[string]string
|
||||
// Full set of flags
|
||||
flags *flag.FlagSet
|
||||
// Set of flags childrens of this command will inherit
|
||||
@@ -112,11 +109,10 @@ type Command struct {
|
||||
|
||||
flagErrorBuf *bytes.Buffer
|
||||
|
||||
args []string // actual args parsed from flags
|
||||
output *io.Writer // out writer if set in SetOutput(w)
|
||||
usageFunc func(*Command) error // Usage can be defined by application
|
||||
usageTemplate string // Can be defined by Application
|
||||
flagErrorFunc func(*Command, error) error
|
||||
args []string // actual args parsed from flags
|
||||
output *io.Writer // out writer if set in SetOutput(w)
|
||||
usageFunc func(*Command) error // Usage can be defined by application
|
||||
usageTemplate string // Can be defined by Application
|
||||
helpTemplate string // Can be defined by Application
|
||||
helpFunc func(*Command, []string) // Help can be defined by application
|
||||
helpCommand *Command // The help command
|
||||
@@ -132,7 +128,7 @@ type Command struct {
|
||||
DisableFlagParsing bool
|
||||
}
|
||||
|
||||
// SetArgs sets arguments for the command. It is set to os.Args[1:] by default, if desired, can be overridden
|
||||
// os.Args[1:] by default, if desired, can be overridden
|
||||
// particularly useful when testing.
|
||||
func (c *Command) SetArgs(a []string) {
|
||||
c.args = a
|
||||
@@ -144,33 +140,26 @@ func (c *Command) SetOutput(output io.Writer) {
|
||||
c.output = &output
|
||||
}
|
||||
|
||||
// SetUsageFunc sets usage function. Usage can be defined by application.
|
||||
// Usage can be defined by application
|
||||
func (c *Command) SetUsageFunc(f func(*Command) error) {
|
||||
c.usageFunc = f
|
||||
}
|
||||
|
||||
// SetUsageTemplate sets usage template. Can be defined by Application.
|
||||
// Can be defined by Application
|
||||
func (c *Command) SetUsageTemplate(s string) {
|
||||
c.usageTemplate = s
|
||||
}
|
||||
|
||||
// SetFlagErrorFunc sets a function to generate an error when flag parsing
|
||||
// fails.
|
||||
func (c *Command) SetFlagErrorFunc(f func(*Command, error) error) {
|
||||
c.flagErrorFunc = f
|
||||
}
|
||||
|
||||
// SetHelpFunc sets help function. Can be defined by Application.
|
||||
// Can be defined by Application
|
||||
func (c *Command) SetHelpFunc(f func(*Command, []string)) {
|
||||
c.helpFunc = f
|
||||
}
|
||||
|
||||
// SetHelpCommand sets help command.
|
||||
func (c *Command) SetHelpCommand(cmd *Command) {
|
||||
c.helpCommand = cmd
|
||||
}
|
||||
|
||||
// SetHelpTemplate sets help template to be used. Application can use it to set custom template.
|
||||
// Can be defined by Application
|
||||
func (c *Command) SetHelpTemplate(s string) {
|
||||
c.helpTemplate = s
|
||||
}
|
||||
@@ -187,12 +176,10 @@ func (c *Command) SetGlobalNormalizationFunc(n func(f *flag.FlagSet, name string
|
||||
}
|
||||
}
|
||||
|
||||
// OutOrStdout returns output to stdout.
|
||||
func (c *Command) OutOrStdout() io.Writer {
|
||||
return c.getOut(os.Stdout)
|
||||
}
|
||||
|
||||
// OutOrStderr returns output to stderr
|
||||
func (c *Command) OutOrStderr() io.Writer {
|
||||
return c.getOut(os.Stderr)
|
||||
}
|
||||
@@ -208,7 +195,7 @@ func (c *Command) getOut(def io.Writer) io.Writer {
|
||||
}
|
||||
|
||||
// UsageFunc returns either the function set by SetUsageFunc for this command
|
||||
// or a parent, or it returns a default usage function.
|
||||
// or a parent, or it returns a default usage function
|
||||
func (c *Command) UsageFunc() (f func(*Command) error) {
|
||||
if c.usageFunc != nil {
|
||||
return c.usageFunc
|
||||
@@ -227,18 +214,22 @@ func (c *Command) UsageFunc() (f func(*Command) error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Usage puts out the usage for the command.
|
||||
// Used when a user provides invalid input.
|
||||
// Can be defined by user by overriding UsageFunc.
|
||||
// Output the usage for the command
|
||||
// Used when a user provides invalid input
|
||||
// Can be defined by user by overriding UsageFunc
|
||||
func (c *Command) Usage() error {
|
||||
return c.UsageFunc()(c)
|
||||
}
|
||||
|
||||
// HelpFunc returns either the function set by SetHelpFunc for this command
|
||||
// or a parent, or it returns a function with default help behavior.
|
||||
// or a parent, or it returns a function with default help behavior
|
||||
func (c *Command) HelpFunc() func(*Command, []string) {
|
||||
if helpFunc := c.checkHelpFunc(); helpFunc != nil {
|
||||
return helpFunc
|
||||
cmd := c
|
||||
for cmd != nil {
|
||||
if cmd.helpFunc != nil {
|
||||
return cmd.helpFunc
|
||||
}
|
||||
cmd = cmd.parent
|
||||
}
|
||||
return func(*Command, []string) {
|
||||
c.mergePersistentFlags()
|
||||
@@ -249,29 +240,14 @@ func (c *Command) HelpFunc() func(*Command, []string) {
|
||||
}
|
||||
}
|
||||
|
||||
// checkHelpFunc checks if there is helpFunc in ancestors of c.
|
||||
func (c *Command) checkHelpFunc() func(*Command, []string) {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
if c.helpFunc != nil {
|
||||
return c.helpFunc
|
||||
}
|
||||
if c.HasParent() {
|
||||
return c.parent.checkHelpFunc()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Help puts out the help for the command.
|
||||
// Used when a user calls help [command].
|
||||
// Can be defined by user by overriding HelpFunc.
|
||||
// Output the help for the command
|
||||
// Used when a user calls help [command]
|
||||
// Can be defined by user by overriding HelpFunc
|
||||
func (c *Command) Help() error {
|
||||
c.HelpFunc()(c, []string{})
|
||||
return nil
|
||||
}
|
||||
|
||||
// UsageString return usage string.
|
||||
func (c *Command) UsageString() string {
|
||||
tmpOutput := c.output
|
||||
bb := new(bytes.Buffer)
|
||||
@@ -281,25 +257,8 @@ func (c *Command) UsageString() string {
|
||||
return bb.String()
|
||||
}
|
||||
|
||||
// FlagErrorFunc returns either the function set by SetFlagErrorFunc for this
|
||||
// command or a parent, or it returns a function which returns the original
|
||||
// error.
|
||||
func (c *Command) FlagErrorFunc() (f func(*Command, error) error) {
|
||||
if c.flagErrorFunc != nil {
|
||||
return c.flagErrorFunc
|
||||
}
|
||||
|
||||
if c.HasParent() {
|
||||
return c.parent.FlagErrorFunc()
|
||||
}
|
||||
return func(c *Command, err error) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var minUsagePadding = 25
|
||||
|
||||
// UsagePadding return padding for the usage.
|
||||
func (c *Command) UsagePadding() int {
|
||||
if c.parent == nil || minUsagePadding > c.parent.commandsMaxUseLen {
|
||||
return minUsagePadding
|
||||
@@ -309,7 +268,7 @@ func (c *Command) UsagePadding() int {
|
||||
|
||||
var minCommandPathPadding = 11
|
||||
|
||||
// CommandPathPadding return padding for the command path.
|
||||
//
|
||||
func (c *Command) CommandPathPadding() int {
|
||||
if c.parent == nil || minCommandPathPadding > c.parent.commandsMaxCommandPathLen {
|
||||
return minCommandPathPadding
|
||||
@@ -319,7 +278,6 @@ func (c *Command) CommandPathPadding() int {
|
||||
|
||||
var minNamePadding = 11
|
||||
|
||||
// NamePadding returns padding for the name.
|
||||
func (c *Command) NamePadding() int {
|
||||
if c.parent == nil || minNamePadding > c.parent.commandsMaxNameLen {
|
||||
return minNamePadding
|
||||
@@ -327,7 +285,6 @@ func (c *Command) NamePadding() int {
|
||||
return c.parent.commandsMaxNameLen
|
||||
}
|
||||
|
||||
// UsageTemplate returns usage template for the command.
|
||||
func (c *Command) UsageTemplate() string {
|
||||
if c.usageTemplate != "" {
|
||||
return c.usageTemplate
|
||||
@@ -345,25 +302,24 @@ Aliases:
|
||||
{{end}}{{if .HasExample}}
|
||||
|
||||
Examples:
|
||||
{{ .Example }}{{end}}{{if .HasAvailableSubCommands}}
|
||||
{{ .Example }}{{end}}{{ if .HasAvailableSubCommands}}
|
||||
|
||||
Available Commands:{{range .Commands}}{{if (or .IsAvailableCommand (eq .Name "help"))}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableLocalFlags}}
|
||||
Available Commands:{{range .Commands}}{{if .IsAvailableCommand}}
|
||||
{{rpad .Name .NamePadding }} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableLocalFlags}}
|
||||
|
||||
Flags:
|
||||
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasAvailableInheritedFlags}}
|
||||
{{.LocalFlags.FlagUsages | trimRightSpace}}{{end}}{{ if .HasAvailableInheritedFlags}}
|
||||
|
||||
Global Flags:
|
||||
{{.InheritedFlags.FlagUsages | trimRightSpace}}{{end}}{{if .HasHelpSubCommands}}
|
||||
|
||||
Additional help topics:{{range .Commands}}{{if .IsAdditionalHelpTopicCommand}}
|
||||
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{if .HasAvailableSubCommands}}
|
||||
Additional help topics:{{range .Commands}}{{if .IsHelpCommand}}
|
||||
{{rpad .CommandPath .CommandPathPadding}} {{.Short}}{{end}}{{end}}{{end}}{{ if .HasAvailableSubCommands }}
|
||||
|
||||
Use "{{.CommandPath}} [command] --help" for more information about a command.{{end}}
|
||||
`
|
||||
}
|
||||
|
||||
// HelpTemplate return help template for the command.
|
||||
func (c *Command) HelpTemplate() string {
|
||||
if c.helpTemplate != "" {
|
||||
return c.helpTemplate
|
||||
@@ -377,25 +333,27 @@ func (c *Command) HelpTemplate() string {
|
||||
{{end}}{{if or .Runnable .HasSubCommands}}{{.UsageString}}{{end}}`
|
||||
}
|
||||
|
||||
// Really only used when casting a command to a commander.
|
||||
// Really only used when casting a command to a commander
|
||||
func (c *Command) resetChildrensParents() {
|
||||
for _, x := range c.commands {
|
||||
x.parent = c
|
||||
}
|
||||
}
|
||||
|
||||
func hasNoOptDefVal(name string, f *flag.FlagSet) bool {
|
||||
// Test if the named flag is a boolean flag.
|
||||
func isBooleanFlag(name string, f *flag.FlagSet) bool {
|
||||
flag := f.Lookup(name)
|
||||
if flag == nil {
|
||||
return false
|
||||
}
|
||||
return len(flag.NoOptDefVal) > 0
|
||||
return flag.Value.Type() == "bool"
|
||||
}
|
||||
|
||||
func shortHasNoOptDefVal(name string, fs *flag.FlagSet) bool {
|
||||
// Test if the named flag is a boolean flag.
|
||||
func isBooleanShortFlag(name string, f *flag.FlagSet) bool {
|
||||
result := false
|
||||
fs.VisitAll(func(flag *flag.Flag) {
|
||||
if flag.Shorthand == name && len(flag.NoOptDefVal) > 0 {
|
||||
f.VisitAll(func(f *flag.Flag) {
|
||||
if f.Shorthand == name && f.Value.Type() == "bool" {
|
||||
result = true
|
||||
}
|
||||
})
|
||||
@@ -421,13 +379,13 @@ func stripFlags(args []string, c *Command) []string {
|
||||
inQuote = true
|
||||
case strings.HasPrefix(y, "--") && !strings.Contains(y, "="):
|
||||
// TODO: this isn't quite right, we should really check ahead for 'true' or 'false'
|
||||
inFlag = !hasNoOptDefVal(y[2:], c.Flags())
|
||||
case strings.HasPrefix(y, "-") && !strings.Contains(y, "=") && len(y) == 2 && !shortHasNoOptDefVal(y[1:], c.Flags()):
|
||||
inFlag = !isBooleanFlag(y[2:], c.Flags())
|
||||
case strings.HasPrefix(y, "-") && !strings.Contains(y, "=") && len(y) == 2 && !isBooleanShortFlag(y[1:], c.Flags()):
|
||||
inFlag = true
|
||||
case inFlag:
|
||||
inFlag = false
|
||||
case y == "":
|
||||
// strip empty commands, as the go tests expect this to be ok....
|
||||
// strip empty commands, as the go tests expect this to be ok....
|
||||
case !strings.HasPrefix(y, "-"):
|
||||
commands = append(commands, y)
|
||||
inFlag = false
|
||||
@@ -456,7 +414,7 @@ func argsMinusFirstX(args []string, x string) []string {
|
||||
return args
|
||||
}
|
||||
|
||||
// Find the target command given the args and command tree
|
||||
// find the target command given the args and command tree
|
||||
// Meant to be run on the highest node. Only searches down.
|
||||
func (c *Command) Find(args []string) (*Command, []string, error) {
|
||||
if c == nil {
|
||||
@@ -524,7 +482,6 @@ func (c *Command) Find(args []string) (*Command, []string, error) {
|
||||
return commandFound, a, nil
|
||||
}
|
||||
|
||||
// SuggestionsFor provides suggestions for the typedName.
|
||||
func (c *Command) SuggestionsFor(typedName string) []string {
|
||||
suggestions := []string{}
|
||||
for _, cmd := range c.commands {
|
||||
@@ -545,7 +502,6 @@ func (c *Command) SuggestionsFor(typedName string) []string {
|
||||
return suggestions
|
||||
}
|
||||
|
||||
// VisitParents visits all parents of the command and invokes fn on each parent.
|
||||
func (c *Command) VisitParents(fn func(*Command)) {
|
||||
var traverse func(*Command) *Command
|
||||
|
||||
@@ -561,7 +517,6 @@ func (c *Command) VisitParents(fn func(*Command)) {
|
||||
traverse(c)
|
||||
}
|
||||
|
||||
// Root finds root command.
|
||||
func (c *Command) Root() *Command {
|
||||
var findRoot func(*Command) *Command
|
||||
|
||||
@@ -598,7 +553,7 @@ func (c *Command) execute(a []string) (err error) {
|
||||
|
||||
err = c.ParseFlags(a)
|
||||
if err != nil {
|
||||
return c.FlagErrorFunc()(c, err)
|
||||
return err
|
||||
}
|
||||
// If help is called, regardless of other flags, return we want help
|
||||
// Also say we need help if the command isn't runnable.
|
||||
@@ -686,7 +641,7 @@ func (c *Command) errorMsgFromParse() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Execute Call execute to use the args (os.Args[1:] by default)
|
||||
// Call execute to use the args (os.Args[1:] by default)
|
||||
// and run through the command tree finding appropriate matches
|
||||
// for commands and then corresponding flags.
|
||||
func (c *Command) Execute() error {
|
||||
@@ -694,8 +649,8 @@ func (c *Command) Execute() error {
|
||||
return err
|
||||
}
|
||||
|
||||
// ExecuteC executes the command.
|
||||
func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||
|
||||
// Regardless of what command execute is called on, run on Root only
|
||||
if c.HasParent() {
|
||||
return c.Root().ExecuteC()
|
||||
@@ -757,7 +712,6 @@ func (c *Command) ExecuteC() (cmd *Command, err error) {
|
||||
}
|
||||
|
||||
func (c *Command) initHelpFlag() {
|
||||
c.mergePersistentFlags()
|
||||
if c.Flags().Lookup("help") == nil {
|
||||
c.Flags().BoolP("help", "h", false, "help for "+c.Name())
|
||||
}
|
||||
@@ -780,7 +734,7 @@ func (c *Command) initHelpCmd() {
|
||||
Run: func(c *Command, args []string) {
|
||||
cmd, _, e := c.Root().Find(args)
|
||||
if cmd == nil || e != nil {
|
||||
c.Printf("Unknown help topic %#q\n", args)
|
||||
c.Printf("Unknown help topic %#q.", args)
|
||||
c.Root().Usage()
|
||||
} else {
|
||||
cmd.Help()
|
||||
@@ -791,13 +745,13 @@ func (c *Command) initHelpCmd() {
|
||||
c.AddCommand(c.helpCommand)
|
||||
}
|
||||
|
||||
// ResetCommands used for testing.
|
||||
// Used for testing
|
||||
func (c *Command) ResetCommands() {
|
||||
c.commands = nil
|
||||
c.helpCommand = nil
|
||||
}
|
||||
|
||||
// Sorts commands by their names.
|
||||
// Sorts commands by their names
|
||||
type commandSorterByName []*Command
|
||||
|
||||
func (c commandSorterByName) Len() int { return len(c) }
|
||||
@@ -877,18 +831,18 @@ main:
|
||||
}
|
||||
}
|
||||
|
||||
// Print is a convenience method to Print to the defined output, fallback to Stderr if not set.
|
||||
// Print is a convenience method to Print to the defined output, fallback to Stderr if not set
|
||||
func (c *Command) Print(i ...interface{}) {
|
||||
fmt.Fprint(c.OutOrStderr(), i...)
|
||||
}
|
||||
|
||||
// Println is a convenience method to Println to the defined output, fallback to Stderr if not set.
|
||||
// Println is a convenience method to Println to the defined output, fallback to Stderr if not set
|
||||
func (c *Command) Println(i ...interface{}) {
|
||||
str := fmt.Sprintln(i...)
|
||||
c.Print(str)
|
||||
}
|
||||
|
||||
// Printf is a convenience method to Printf to the defined output, fallback to Stderr if not set.
|
||||
// Printf is a convenience method to Printf to the defined output, fallback to Stderr if not set
|
||||
func (c *Command) Printf(format string, i ...interface{}) {
|
||||
str := fmt.Sprintf(format, i...)
|
||||
c.Print(str)
|
||||
@@ -905,7 +859,7 @@ func (c *Command) CommandPath() string {
|
||||
return str
|
||||
}
|
||||
|
||||
// UseLine puts out the full usage for a given command (including parents).
|
||||
//The full usage for a given command (including parents)
|
||||
func (c *Command) UseLine() string {
|
||||
str := ""
|
||||
if c.HasParent() {
|
||||
@@ -914,8 +868,8 @@ func (c *Command) UseLine() string {
|
||||
return str + c.Use
|
||||
}
|
||||
|
||||
// DebugFlags used to determine which flags have been assigned to which commands
|
||||
// and which persist.
|
||||
// For use in determining which flags have been assigned to which commands
|
||||
// and which persist
|
||||
func (c *Command) DebugFlags() {
|
||||
c.Println("DebugFlags called on", c.Name())
|
||||
var debugflags func(*Command)
|
||||
@@ -969,8 +923,7 @@ func (c *Command) Name() string {
|
||||
if i >= 0 {
|
||||
name = name[:i]
|
||||
}
|
||||
c.name = name
|
||||
return c.name
|
||||
return name
|
||||
}
|
||||
|
||||
// HasAlias determines if a given string is an alias of the command.
|
||||
@@ -983,28 +936,26 @@ func (c *Command) HasAlias(s string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// NameAndAliases returns string containing name and all aliases
|
||||
func (c *Command) NameAndAliases() string {
|
||||
return strings.Join(append([]string{c.Name()}, c.Aliases...), ", ")
|
||||
}
|
||||
|
||||
// HasExample determines if the command has example.
|
||||
func (c *Command) HasExample() bool {
|
||||
return len(c.Example) > 0
|
||||
}
|
||||
|
||||
// Runnable determines if the command is itself runnable.
|
||||
// Runnable determines if the command is itself runnable
|
||||
func (c *Command) Runnable() bool {
|
||||
return c.Run != nil || c.RunE != nil
|
||||
}
|
||||
|
||||
// HasSubCommands determines if the command has children commands.
|
||||
// HasSubCommands determines if the command has children commands
|
||||
func (c *Command) HasSubCommands() bool {
|
||||
return len(c.commands) > 0
|
||||
}
|
||||
|
||||
// IsAvailableCommand determines if a command is available as a non-help command
|
||||
// (this includes all non deprecated/hidden commands).
|
||||
// (this includes all non deprecated/hidden commands)
|
||||
func (c *Command) IsAvailableCommand() bool {
|
||||
if len(c.Deprecated) != 0 || c.Hidden {
|
||||
return false
|
||||
@@ -1021,12 +972,11 @@ func (c *Command) IsAvailableCommand() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// IsAdditionalHelpTopicCommand determines if a command is an additional
|
||||
// help topic command; additional help topic command is determined by the
|
||||
// fact that it is NOT runnable/hidden/deprecated, and has no sub commands that
|
||||
// are runnable/hidden/deprecated.
|
||||
// Concrete example: https://github.com/spf13/cobra/issues/393#issuecomment-282741924.
|
||||
func (c *Command) IsAdditionalHelpTopicCommand() bool {
|
||||
// IsHelpCommand determines if a command is a 'help' command; a help command is
|
||||
// determined by the fact that it is NOT runnable/hidden/deprecated, and has no
|
||||
// sub commands that are runnable/hidden/deprecated
|
||||
func (c *Command) IsHelpCommand() bool {
|
||||
|
||||
// if a command is runnable, deprecated, or hidden it is not a 'help' command
|
||||
if c.Runnable() || len(c.Deprecated) != 0 || c.Hidden {
|
||||
return false
|
||||
@@ -1034,7 +984,7 @@ func (c *Command) IsAdditionalHelpTopicCommand() bool {
|
||||
|
||||
// if any non-help sub commands are found, the command is not a 'help' command
|
||||
for _, sub := range c.commands {
|
||||
if !sub.IsAdditionalHelpTopicCommand() {
|
||||
if !sub.IsHelpCommand() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -1043,13 +993,14 @@ func (c *Command) IsAdditionalHelpTopicCommand() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// HasHelpSubCommands determines if a command has any available 'help' sub commands
|
||||
// HasHelpSubCommands determines if a command has any avilable 'help' sub commands
|
||||
// that need to be shown in the usage/help default template under 'additional help
|
||||
// topics'.
|
||||
// topics'
|
||||
func (c *Command) HasHelpSubCommands() bool {
|
||||
|
||||
// return true on the first found available 'help' sub command
|
||||
for _, sub := range c.commands {
|
||||
if sub.IsAdditionalHelpTopicCommand() {
|
||||
if sub.IsHelpCommand() {
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1059,8 +1010,9 @@ func (c *Command) HasHelpSubCommands() bool {
|
||||
}
|
||||
|
||||
// HasAvailableSubCommands determines if a command has available sub commands that
|
||||
// need to be shown in the usage/help default template under 'available commands'.
|
||||
// need to be shown in the usage/help default template under 'available commands'
|
||||
func (c *Command) HasAvailableSubCommands() bool {
|
||||
|
||||
// return true on the first found available (non deprecated/help/hidden)
|
||||
// sub command
|
||||
for _, sub := range c.commands {
|
||||
@@ -1074,18 +1026,17 @@ func (c *Command) HasAvailableSubCommands() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// HasParent determines if the command is a child command.
|
||||
// Determine if the command is a child command
|
||||
func (c *Command) HasParent() bool {
|
||||
return c.parent != nil
|
||||
}
|
||||
|
||||
// GlobalNormalizationFunc returns the global normalization function or nil if doesn't exists.
|
||||
// GlobalNormalizationFunc returns the global normalization function or nil if doesn't exists
|
||||
func (c *Command) GlobalNormalizationFunc() func(f *flag.FlagSet, name string) flag.NormalizedName {
|
||||
return c.globNormFunc
|
||||
}
|
||||
|
||||
// Flags returns the complete FlagSet that applies
|
||||
// to this command (local and persistent declared here and by all parents).
|
||||
// Get the complete FlagSet that applies to this command (local and persistent declared here and by all parents)
|
||||
func (c *Command) Flags() *flag.FlagSet {
|
||||
if c.flags == nil {
|
||||
c.flags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
|
||||
@@ -1097,7 +1048,7 @@ func (c *Command) Flags() *flag.FlagSet {
|
||||
return c.flags
|
||||
}
|
||||
|
||||
// LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands.
|
||||
// LocalNonPersistentFlags are flags specific to this command which will NOT persist to subcommands
|
||||
func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
|
||||
persistentFlags := c.PersistentFlags()
|
||||
|
||||
@@ -1110,7 +1061,7 @@ func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
|
||||
return out
|
||||
}
|
||||
|
||||
// LocalFlags returns the local FlagSet specifically set in the current command.
|
||||
// Get the local FlagSet specifically set in the current command
|
||||
func (c *Command) LocalFlags() *flag.FlagSet {
|
||||
c.mergePersistentFlags()
|
||||
|
||||
@@ -1128,7 +1079,7 @@ func (c *Command) LocalFlags() *flag.FlagSet {
|
||||
return local
|
||||
}
|
||||
|
||||
// InheritedFlags returns all flags which were inherited from parents commands.
|
||||
// All Flags which were inherited from parents commands
|
||||
func (c *Command) InheritedFlags() *flag.FlagSet {
|
||||
c.mergePersistentFlags()
|
||||
|
||||
@@ -1157,12 +1108,12 @@ func (c *Command) InheritedFlags() *flag.FlagSet {
|
||||
return inherited
|
||||
}
|
||||
|
||||
// NonInheritedFlags returns all flags which were not inherited from parent commands.
|
||||
// All Flags which were not inherited from parent commands
|
||||
func (c *Command) NonInheritedFlags() *flag.FlagSet {
|
||||
return c.LocalFlags()
|
||||
}
|
||||
|
||||
// PersistentFlags returns the persistent FlagSet specifically set in the current command.
|
||||
// Get the Persistent FlagSet specifically set in the current command
|
||||
func (c *Command) PersistentFlags() *flag.FlagSet {
|
||||
if c.pflags == nil {
|
||||
c.pflags = flag.NewFlagSet(c.Name(), flag.ContinueOnError)
|
||||
@@ -1174,7 +1125,7 @@ func (c *Command) PersistentFlags() *flag.FlagSet {
|
||||
return c.pflags
|
||||
}
|
||||
|
||||
// ResetFlags is used in testing.
|
||||
// For use in testing
|
||||
func (c *Command) ResetFlags() {
|
||||
c.flagErrorBuf = new(bytes.Buffer)
|
||||
c.flagErrorBuf.Reset()
|
||||
@@ -1184,50 +1135,50 @@ func (c *Command) ResetFlags() {
|
||||
c.pflags.SetOutput(c.flagErrorBuf)
|
||||
}
|
||||
|
||||
// HasFlags checks if the command contains any flags (local plus persistent from the entire structure).
|
||||
// Does the command contain any flags (local plus persistent from the entire structure)
|
||||
func (c *Command) HasFlags() bool {
|
||||
return c.Flags().HasFlags()
|
||||
}
|
||||
|
||||
// HasPersistentFlags checks if the command contains persistent flags.
|
||||
// Does the command contain persistent flags
|
||||
func (c *Command) HasPersistentFlags() bool {
|
||||
return c.PersistentFlags().HasFlags()
|
||||
}
|
||||
|
||||
// HasLocalFlags checks if the command has flags specifically declared locally.
|
||||
// Does the command has flags specifically declared locally
|
||||
func (c *Command) HasLocalFlags() bool {
|
||||
return c.LocalFlags().HasFlags()
|
||||
}
|
||||
|
||||
// HasInheritedFlags checks if the command has flags inherited from its parent command.
|
||||
// Does the command have flags inherited from its parent command
|
||||
func (c *Command) HasInheritedFlags() bool {
|
||||
return c.InheritedFlags().HasFlags()
|
||||
}
|
||||
|
||||
// HasAvailableFlags checks if the command contains any flags (local plus persistent from the entire
|
||||
// structure) which are not hidden or deprecated.
|
||||
// Does the command contain any flags (local plus persistent from the entire
|
||||
// structure) which are not hidden or deprecated
|
||||
func (c *Command) HasAvailableFlags() bool {
|
||||
return c.Flags().HasAvailableFlags()
|
||||
}
|
||||
|
||||
// HasAvailablePersistentFlags checks if the command contains persistent flags which are not hidden or deprecated.
|
||||
// Does the command contain persistent flags which are not hidden or deprecated
|
||||
func (c *Command) HasAvailablePersistentFlags() bool {
|
||||
return c.PersistentFlags().HasAvailableFlags()
|
||||
}
|
||||
|
||||
// HasAvailableLocalFlags checks if the command has flags specifically declared locally which are not hidden
|
||||
// or deprecated.
|
||||
// Does the command has flags specifically declared locally which are not hidden
|
||||
// or deprecated
|
||||
func (c *Command) HasAvailableLocalFlags() bool {
|
||||
return c.LocalFlags().HasAvailableFlags()
|
||||
}
|
||||
|
||||
// HasAvailableInheritedFlags checks if the command has flags inherited from its parent command which are
|
||||
// not hidden or deprecated.
|
||||
// Does the command have flags inherited from its parent command which are
|
||||
// not hidden or deprecated
|
||||
func (c *Command) HasAvailableInheritedFlags() bool {
|
||||
return c.InheritedFlags().HasAvailableFlags()
|
||||
}
|
||||
|
||||
// Flag climbs up the command tree looking for matching flag.
|
||||
// Flag climbs up the command tree looking for matching flag
|
||||
func (c *Command) Flag(name string) (flag *flag.Flag) {
|
||||
flag = c.Flags().Lookup(name)
|
||||
|
||||
@@ -1238,7 +1189,7 @@ func (c *Command) Flag(name string) (flag *flag.Flag) {
|
||||
return
|
||||
}
|
||||
|
||||
// Recursively find matching persistent flag.
|
||||
// recursively find matching persistent flag
|
||||
func (c *Command) persistentFlag(name string) (flag *flag.Flag) {
|
||||
if c.HasPersistentFlags() {
|
||||
flag = c.PersistentFlags().Lookup(name)
|
||||
@@ -1250,7 +1201,7 @@ func (c *Command) persistentFlag(name string) (flag *flag.Flag) {
|
||||
return
|
||||
}
|
||||
|
||||
// ParseFlags parses persistent flag tree and local flags.
|
||||
// ParseFlags parses persistent flag tree & local flags
|
||||
func (c *Command) ParseFlags(args []string) (err error) {
|
||||
if c.DisableFlagParsing {
|
||||
return nil
|
||||
@@ -1260,7 +1211,7 @@ func (c *Command) ParseFlags(args []string) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
// Parent returns a commands parent command.
|
||||
// Parent returns a commands parent command
|
||||
func (c *Command) Parent() *Command {
|
||||
return c.parent
|
||||
}
|
||||
|
||||
-147
@@ -1,147 +0,0 @@
|
||||
package pflag
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// -- boolSlice Value
|
||||
type boolSliceValue struct {
|
||||
value *[]bool
|
||||
changed bool
|
||||
}
|
||||
|
||||
func newBoolSliceValue(val []bool, p *[]bool) *boolSliceValue {
|
||||
bsv := new(boolSliceValue)
|
||||
bsv.value = p
|
||||
*bsv.value = val
|
||||
return bsv
|
||||
}
|
||||
|
||||
// Set converts, and assigns, the comma-separated boolean argument string representation as the []bool value of this flag.
|
||||
// If Set is called on a flag that already has a []bool assigned, the newly converted values will be appended.
|
||||
func (s *boolSliceValue) Set(val string) error {
|
||||
|
||||
// remove all quote characters
|
||||
rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "")
|
||||
|
||||
// read flag arguments with CSV parser
|
||||
boolStrSlice, err := readAsCSV(rmQuote.Replace(val))
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse boolean values into slice
|
||||
out := make([]bool, 0, len(boolStrSlice))
|
||||
for _, boolStr := range boolStrSlice {
|
||||
b, err := strconv.ParseBool(strings.TrimSpace(boolStr))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out = append(out, b)
|
||||
}
|
||||
|
||||
if !s.changed {
|
||||
*s.value = out
|
||||
} else {
|
||||
*s.value = append(*s.value, out...)
|
||||
}
|
||||
|
||||
s.changed = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns a string that uniquely represents this flag's type.
|
||||
func (s *boolSliceValue) Type() string {
|
||||
return "boolSlice"
|
||||
}
|
||||
|
||||
// String defines a "native" format for this boolean slice flag value.
|
||||
func (s *boolSliceValue) String() string {
|
||||
|
||||
boolStrSlice := make([]string, len(*s.value))
|
||||
for i, b := range *s.value {
|
||||
boolStrSlice[i] = strconv.FormatBool(b)
|
||||
}
|
||||
|
||||
out, _ := writeAsCSV(boolStrSlice)
|
||||
|
||||
return "[" + out + "]"
|
||||
}
|
||||
|
||||
func boolSliceConv(val string) (interface{}, error) {
|
||||
val = strings.Trim(val, "[]")
|
||||
// Empty string would cause a slice with one (empty) entry
|
||||
if len(val) == 0 {
|
||||
return []bool{}, nil
|
||||
}
|
||||
ss := strings.Split(val, ",")
|
||||
out := make([]bool, len(ss))
|
||||
for i, t := range ss {
|
||||
var err error
|
||||
out[i], err = strconv.ParseBool(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetBoolSlice returns the []bool value of a flag with the given name.
|
||||
func (f *FlagSet) GetBoolSlice(name string) ([]bool, error) {
|
||||
val, err := f.getFlagType(name, "boolSlice", boolSliceConv)
|
||||
if err != nil {
|
||||
return []bool{}, err
|
||||
}
|
||||
return val.([]bool), nil
|
||||
}
|
||||
|
||||
// BoolSliceVar defines a boolSlice flag with specified name, default value, and usage string.
|
||||
// The argument p points to a []bool variable in which to store the value of the flag.
|
||||
func (f *FlagSet) BoolSliceVar(p *[]bool, name string, value []bool, usage string) {
|
||||
f.VarP(newBoolSliceValue(value, p), name, "", usage)
|
||||
}
|
||||
|
||||
// BoolSliceVarP is like BoolSliceVar, but accepts a shorthand letter that can be used after a single dash.
|
||||
func (f *FlagSet) BoolSliceVarP(p *[]bool, name, shorthand string, value []bool, usage string) {
|
||||
f.VarP(newBoolSliceValue(value, p), name, shorthand, usage)
|
||||
}
|
||||
|
||||
// BoolSliceVar defines a []bool flag with specified name, default value, and usage string.
|
||||
// The argument p points to a []bool variable in which to store the value of the flag.
|
||||
func BoolSliceVar(p *[]bool, name string, value []bool, usage string) {
|
||||
CommandLine.VarP(newBoolSliceValue(value, p), name, "", usage)
|
||||
}
|
||||
|
||||
// BoolSliceVarP is like BoolSliceVar, but accepts a shorthand letter that can be used after a single dash.
|
||||
func BoolSliceVarP(p *[]bool, name, shorthand string, value []bool, usage string) {
|
||||
CommandLine.VarP(newBoolSliceValue(value, p), name, shorthand, usage)
|
||||
}
|
||||
|
||||
// BoolSlice defines a []bool flag with specified name, default value, and usage string.
|
||||
// The return value is the address of a []bool variable that stores the value of the flag.
|
||||
func (f *FlagSet) BoolSlice(name string, value []bool, usage string) *[]bool {
|
||||
p := []bool{}
|
||||
f.BoolSliceVarP(&p, name, "", value, usage)
|
||||
return &p
|
||||
}
|
||||
|
||||
// BoolSliceP is like BoolSlice, but accepts a shorthand letter that can be used after a single dash.
|
||||
func (f *FlagSet) BoolSliceP(name, shorthand string, value []bool, usage string) *[]bool {
|
||||
p := []bool{}
|
||||
f.BoolSliceVarP(&p, name, shorthand, value, usage)
|
||||
return &p
|
||||
}
|
||||
|
||||
// BoolSlice defines a []bool flag with specified name, default value, and usage string.
|
||||
// The return value is the address of a []bool variable that stores the value of the flag.
|
||||
func BoolSlice(name string, value []bool, usage string) *[]bool {
|
||||
return CommandLine.BoolSliceP(name, "", value, usage)
|
||||
}
|
||||
|
||||
// BoolSliceP is like BoolSlice, but accepts a shorthand letter that can be used after a single dash.
|
||||
func BoolSliceP(name, shorthand string, value []bool, usage string) *[]bool {
|
||||
return CommandLine.BoolSliceP(name, shorthand, value, usage)
|
||||
}
|
||||
+15
-131
@@ -487,76 +487,9 @@ func UnquoteUsage(flag *Flag) (name string, usage string) {
|
||||
return
|
||||
}
|
||||
|
||||
// Splits the string `s` on whitespace into an initial substring up to
|
||||
// `i` runes in length and the remainder. Will go `slop` over `i` if
|
||||
// that encompasses the entire string (which allows the caller to
|
||||
// avoid short orphan words on the final line).
|
||||
func wrapN(i, slop int, s string) (string, string) {
|
||||
if i+slop > len(s) {
|
||||
return s, ""
|
||||
}
|
||||
|
||||
w := strings.LastIndexAny(s[:i], " \t")
|
||||
if w <= 0 {
|
||||
return s, ""
|
||||
}
|
||||
|
||||
return s[:w], s[w+1:]
|
||||
}
|
||||
|
||||
// Wraps the string `s` to a maximum width `w` with leading indent
|
||||
// `i`. The first line is not indented (this is assumed to be done by
|
||||
// caller). Pass `w` == 0 to do no wrapping
|
||||
func wrap(i, w int, s string) string {
|
||||
if w == 0 {
|
||||
return s
|
||||
}
|
||||
|
||||
// space between indent i and end of line width w into which
|
||||
// we should wrap the text.
|
||||
wrap := w - i
|
||||
|
||||
var r, l string
|
||||
|
||||
// Not enough space for sensible wrapping. Wrap as a block on
|
||||
// the next line instead.
|
||||
if wrap < 24 {
|
||||
i = 16
|
||||
wrap = w - i
|
||||
r += "\n" + strings.Repeat(" ", i)
|
||||
}
|
||||
// If still not enough space then don't even try to wrap.
|
||||
if wrap < 24 {
|
||||
return s
|
||||
}
|
||||
|
||||
// Try to avoid short orphan words on the final line, by
|
||||
// allowing wrapN to go a bit over if that would fit in the
|
||||
// remainder of the line.
|
||||
slop := 5
|
||||
wrap = wrap - slop
|
||||
|
||||
// Handle first line, which is indented by the caller (or the
|
||||
// special case above)
|
||||
l, s = wrapN(wrap, slop, s)
|
||||
r = r + l
|
||||
|
||||
// Now wrap the rest
|
||||
for s != "" {
|
||||
var t string
|
||||
|
||||
t, s = wrapN(wrap, slop, s)
|
||||
r = r + "\n" + strings.Repeat(" ", i) + t
|
||||
}
|
||||
|
||||
return r
|
||||
|
||||
}
|
||||
|
||||
// FlagUsagesWrapped returns a string containing the usage information
|
||||
// for all flags in the FlagSet. Wrapped to `cols` columns (0 for no
|
||||
// wrapping)
|
||||
func (f *FlagSet) FlagUsagesWrapped(cols int) string {
|
||||
// FlagUsages Returns a string containing the usage information for all flags in
|
||||
// the FlagSet
|
||||
func (f *FlagSet) FlagUsages() string {
|
||||
x := new(bytes.Buffer)
|
||||
|
||||
lines := make([]string, 0, len(f.formal))
|
||||
@@ -613,19 +546,12 @@ func (f *FlagSet) FlagUsagesWrapped(cols int) string {
|
||||
for _, line := range lines {
|
||||
sidx := strings.Index(line, "\x00")
|
||||
spacing := strings.Repeat(" ", maxlen-sidx)
|
||||
// maxlen + 2 comes from + 1 for the \x00 and + 1 for the (deliberate) off-by-one in maxlen-sidx
|
||||
fmt.Fprintln(x, line[:sidx], spacing, wrap(maxlen+2, cols, line[sidx+1:]))
|
||||
fmt.Fprintln(x, line[:sidx], spacing, line[sidx+1:])
|
||||
}
|
||||
|
||||
return x.String()
|
||||
}
|
||||
|
||||
// FlagUsages returns a string containing the usage information for all flags in
|
||||
// the FlagSet
|
||||
func (f *FlagSet) FlagUsages() string {
|
||||
return f.FlagUsagesWrapped(0)
|
||||
}
|
||||
|
||||
// PrintDefaults prints to standard error the default values of all defined command-line flags.
|
||||
func PrintDefaults() {
|
||||
CommandLine.PrintDefaults()
|
||||
@@ -709,7 +635,7 @@ func (f *FlagSet) VarPF(value Value, name, shorthand, usage string) *Flag {
|
||||
|
||||
// VarP is like Var, but accepts a shorthand letter that can be used after a single dash.
|
||||
func (f *FlagSet) VarP(value Value, name, shorthand, usage string) {
|
||||
f.VarPF(value, name, shorthand, usage)
|
||||
_ = f.VarPF(value, name, shorthand, usage)
|
||||
}
|
||||
|
||||
// AddFlag will add the flag to the FlagSet
|
||||
@@ -826,7 +752,7 @@ func containsShorthand(arg, shorthand string) bool {
|
||||
return strings.Contains(arg, shorthand)
|
||||
}
|
||||
|
||||
func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []string, err error) {
|
||||
func (f *FlagSet) parseLongArg(s string, args []string) (a []string, err error) {
|
||||
a = args
|
||||
name := s[2:]
|
||||
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
|
||||
@@ -860,11 +786,11 @@ func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []strin
|
||||
err = f.failf("flag needs an argument: %s", s)
|
||||
return
|
||||
}
|
||||
err = fn(flag, value, s)
|
||||
err = f.setFlag(flag, value, s)
|
||||
return
|
||||
}
|
||||
|
||||
func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parseFunc) (outShorts string, outArgs []string, err error) {
|
||||
func (f *FlagSet) parseSingleShortArg(shorthands string, args []string) (outShorts string, outArgs []string, err error) {
|
||||
if strings.HasPrefix(shorthands, "test.") {
|
||||
return
|
||||
}
|
||||
@@ -899,16 +825,16 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parse
|
||||
err = f.failf("flag needs an argument: %q in -%s", c, shorthands)
|
||||
return
|
||||
}
|
||||
err = fn(flag, value, shorthands)
|
||||
err = f.setFlag(flag, value, shorthands)
|
||||
return
|
||||
}
|
||||
|
||||
func (f *FlagSet) parseShortArg(s string, args []string, fn parseFunc) (a []string, err error) {
|
||||
func (f *FlagSet) parseShortArg(s string, args []string) (a []string, err error) {
|
||||
a = args
|
||||
shorthands := s[1:]
|
||||
|
||||
for len(shorthands) > 0 {
|
||||
shorthands, a, err = f.parseSingleShortArg(shorthands, args, fn)
|
||||
shorthands, a, err = f.parseSingleShortArg(shorthands, args)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -917,7 +843,7 @@ func (f *FlagSet) parseShortArg(s string, args []string, fn parseFunc) (a []stri
|
||||
return
|
||||
}
|
||||
|
||||
func (f *FlagSet) parseArgs(args []string, fn parseFunc) (err error) {
|
||||
func (f *FlagSet) parseArgs(args []string) (err error) {
|
||||
for len(args) > 0 {
|
||||
s := args[0]
|
||||
args = args[1:]
|
||||
@@ -937,9 +863,9 @@ func (f *FlagSet) parseArgs(args []string, fn parseFunc) (err error) {
|
||||
f.args = append(f.args, args...)
|
||||
break
|
||||
}
|
||||
args, err = f.parseLongArg(s, args, fn)
|
||||
args, err = f.parseLongArg(s, args)
|
||||
} else {
|
||||
args, err = f.parseShortArg(s, args, fn)
|
||||
args, err = f.parseShortArg(s, args)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
@@ -955,41 +881,7 @@ func (f *FlagSet) parseArgs(args []string, fn parseFunc) (err error) {
|
||||
func (f *FlagSet) Parse(arguments []string) error {
|
||||
f.parsed = true
|
||||
f.args = make([]string, 0, len(arguments))
|
||||
|
||||
assign := func(flag *Flag, value, origArg string) error {
|
||||
return f.setFlag(flag, value, origArg)
|
||||
}
|
||||
|
||||
err := f.parseArgs(arguments, assign)
|
||||
if err != nil {
|
||||
switch f.errorHandling {
|
||||
case ContinueOnError:
|
||||
return err
|
||||
case ExitOnError:
|
||||
os.Exit(2)
|
||||
case PanicOnError:
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type parseFunc func(flag *Flag, value, origArg string) error
|
||||
|
||||
// ParseAll parses flag definitions from the argument list, which should not
|
||||
// include the command name. The arguments for fn are flag and value. Must be
|
||||
// called after all flags in the FlagSet are defined and before flags are
|
||||
// accessed by the program. The return value will be ErrHelp if -help was set
|
||||
// but not defined.
|
||||
func (f *FlagSet) ParseAll(arguments []string, fn func(flag *Flag, value string) error) error {
|
||||
f.parsed = true
|
||||
f.args = make([]string, 0, len(arguments))
|
||||
|
||||
assign := func(flag *Flag, value, origArg string) error {
|
||||
return fn(flag, value)
|
||||
}
|
||||
|
||||
err := f.parseArgs(arguments, assign)
|
||||
err := f.parseArgs(arguments)
|
||||
if err != nil {
|
||||
switch f.errorHandling {
|
||||
case ContinueOnError:
|
||||
@@ -1015,14 +907,6 @@ func Parse() {
|
||||
CommandLine.Parse(os.Args[1:])
|
||||
}
|
||||
|
||||
// ParseAll parses the command-line flags from os.Args[1:] and called fn for each.
|
||||
// The arguments for fn are flag and value. Must be called after all flags are
|
||||
// defined and before flags are accessed by the program.
|
||||
func ParseAll(fn func(flag *Flag, value string) error) {
|
||||
// Ignore errors; CommandLine is set for ExitOnError.
|
||||
CommandLine.ParseAll(os.Args[1:], fn)
|
||||
}
|
||||
|
||||
// SetInterspersed sets whether to support interspersed option/non-option arguments.
|
||||
func SetInterspersed(interspersed bool) {
|
||||
CommandLine.SetInterspersed(interspersed)
|
||||
|
||||
+3
@@ -6,10 +6,13 @@ package pflag
|
||||
|
||||
import (
|
||||
goflag "flag"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = fmt.Print
|
||||
|
||||
// flagValueWrapper implements pflag.Value around a flag.Value. The main
|
||||
// difference here is the addition of the Type method that returns a string
|
||||
// name of the type. As this is generally unknown, we approximate that with
|
||||
|
||||
+2
@@ -6,6 +6,8 @@ import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = strings.TrimSpace
|
||||
|
||||
// -- net.IP value
|
||||
type ipValue net.IP
|
||||
|
||||
|
||||
-148
@@ -1,148 +0,0 @@
|
||||
package pflag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// -- ipSlice Value
|
||||
type ipSliceValue struct {
|
||||
value *[]net.IP
|
||||
changed bool
|
||||
}
|
||||
|
||||
func newIPSliceValue(val []net.IP, p *[]net.IP) *ipSliceValue {
|
||||
ipsv := new(ipSliceValue)
|
||||
ipsv.value = p
|
||||
*ipsv.value = val
|
||||
return ipsv
|
||||
}
|
||||
|
||||
// Set converts, and assigns, the comma-separated IP argument string representation as the []net.IP value of this flag.
|
||||
// If Set is called on a flag that already has a []net.IP assigned, the newly converted values will be appended.
|
||||
func (s *ipSliceValue) Set(val string) error {
|
||||
|
||||
// remove all quote characters
|
||||
rmQuote := strings.NewReplacer(`"`, "", `'`, "", "`", "")
|
||||
|
||||
// read flag arguments with CSV parser
|
||||
ipStrSlice, err := readAsCSV(rmQuote.Replace(val))
|
||||
if err != nil && err != io.EOF {
|
||||
return err
|
||||
}
|
||||
|
||||
// parse ip values into slice
|
||||
out := make([]net.IP, 0, len(ipStrSlice))
|
||||
for _, ipStr := range ipStrSlice {
|
||||
ip := net.ParseIP(strings.TrimSpace(ipStr))
|
||||
if ip == nil {
|
||||
return fmt.Errorf("invalid string being converted to IP address: %s", ipStr)
|
||||
}
|
||||
out = append(out, ip)
|
||||
}
|
||||
|
||||
if !s.changed {
|
||||
*s.value = out
|
||||
} else {
|
||||
*s.value = append(*s.value, out...)
|
||||
}
|
||||
|
||||
s.changed = true
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Type returns a string that uniquely represents this flag's type.
|
||||
func (s *ipSliceValue) Type() string {
|
||||
return "ipSlice"
|
||||
}
|
||||
|
||||
// String defines a "native" format for this net.IP slice flag value.
|
||||
func (s *ipSliceValue) String() string {
|
||||
|
||||
ipStrSlice := make([]string, len(*s.value))
|
||||
for i, ip := range *s.value {
|
||||
ipStrSlice[i] = ip.String()
|
||||
}
|
||||
|
||||
out, _ := writeAsCSV(ipStrSlice)
|
||||
|
||||
return "[" + out + "]"
|
||||
}
|
||||
|
||||
func ipSliceConv(val string) (interface{}, error) {
|
||||
val = strings.Trim(val, "[]")
|
||||
// Emtpy string would cause a slice with one (empty) entry
|
||||
if len(val) == 0 {
|
||||
return []net.IP{}, nil
|
||||
}
|
||||
ss := strings.Split(val, ",")
|
||||
out := make([]net.IP, len(ss))
|
||||
for i, sval := range ss {
|
||||
ip := net.ParseIP(strings.TrimSpace(sval))
|
||||
if ip == nil {
|
||||
return nil, fmt.Errorf("invalid string being converted to IP address: %s", sval)
|
||||
}
|
||||
out[i] = ip
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetIPSlice returns the []net.IP value of a flag with the given name
|
||||
func (f *FlagSet) GetIPSlice(name string) ([]net.IP, error) {
|
||||
val, err := f.getFlagType(name, "ipSlice", ipSliceConv)
|
||||
if err != nil {
|
||||
return []net.IP{}, err
|
||||
}
|
||||
return val.([]net.IP), nil
|
||||
}
|
||||
|
||||
// IPSliceVar defines a ipSlice flag with specified name, default value, and usage string.
|
||||
// The argument p points to a []net.IP variable in which to store the value of the flag.
|
||||
func (f *FlagSet) IPSliceVar(p *[]net.IP, name string, value []net.IP, usage string) {
|
||||
f.VarP(newIPSliceValue(value, p), name, "", usage)
|
||||
}
|
||||
|
||||
// IPSliceVarP is like IPSliceVar, but accepts a shorthand letter that can be used after a single dash.
|
||||
func (f *FlagSet) IPSliceVarP(p *[]net.IP, name, shorthand string, value []net.IP, usage string) {
|
||||
f.VarP(newIPSliceValue(value, p), name, shorthand, usage)
|
||||
}
|
||||
|
||||
// IPSliceVar defines a []net.IP flag with specified name, default value, and usage string.
|
||||
// The argument p points to a []net.IP variable in which to store the value of the flag.
|
||||
func IPSliceVar(p *[]net.IP, name string, value []net.IP, usage string) {
|
||||
CommandLine.VarP(newIPSliceValue(value, p), name, "", usage)
|
||||
}
|
||||
|
||||
// IPSliceVarP is like IPSliceVar, but accepts a shorthand letter that can be used after a single dash.
|
||||
func IPSliceVarP(p *[]net.IP, name, shorthand string, value []net.IP, usage string) {
|
||||
CommandLine.VarP(newIPSliceValue(value, p), name, shorthand, usage)
|
||||
}
|
||||
|
||||
// IPSlice defines a []net.IP flag with specified name, default value, and usage string.
|
||||
// The return value is the address of a []net.IP variable that stores the value of that flag.
|
||||
func (f *FlagSet) IPSlice(name string, value []net.IP, usage string) *[]net.IP {
|
||||
p := []net.IP{}
|
||||
f.IPSliceVarP(&p, name, "", value, usage)
|
||||
return &p
|
||||
}
|
||||
|
||||
// IPSliceP is like IPSlice, but accepts a shorthand letter that can be used after a single dash.
|
||||
func (f *FlagSet) IPSliceP(name, shorthand string, value []net.IP, usage string) *[]net.IP {
|
||||
p := []net.IP{}
|
||||
f.IPSliceVarP(&p, name, shorthand, value, usage)
|
||||
return &p
|
||||
}
|
||||
|
||||
// IPSlice defines a []net.IP flag with specified name, default value, and usage string.
|
||||
// The return value is the address of a []net.IP variable that stores the value of the flag.
|
||||
func IPSlice(name string, value []net.IP, usage string) *[]net.IP {
|
||||
return CommandLine.IPSliceP(name, "", value, usage)
|
||||
}
|
||||
|
||||
// IPSliceP is like IPSlice, but accepts a shorthand letter that can be used after a single dash.
|
||||
func IPSliceP(name, shorthand string, value []net.IP, usage string) *[]net.IP {
|
||||
return CommandLine.IPSliceP(name, shorthand, value, usage)
|
||||
}
|
||||
+2
@@ -27,6 +27,8 @@ func (*ipNetValue) Type() string {
|
||||
return "ipNet"
|
||||
}
|
||||
|
||||
var _ = strings.TrimSpace
|
||||
|
||||
func newIPNetValue(val net.IPNet, p *net.IPNet) *ipNetValue {
|
||||
*p = val
|
||||
return (*ipNetValue)(p)
|
||||
|
||||
+6
@@ -1,5 +1,11 @@
|
||||
package pflag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
var _ = fmt.Fprint
|
||||
|
||||
// -- stringArray Value
|
||||
type stringArrayValue struct {
|
||||
value *[]string
|
||||
|
||||
+4
-1
@@ -3,9 +3,12 @@ package pflag
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/csv"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var _ = fmt.Fprint
|
||||
|
||||
// -- stringSlice Value
|
||||
type stringSliceValue struct {
|
||||
value *[]string
|
||||
@@ -36,7 +39,7 @@ func writeAsCSV(vals []string) (string, error) {
|
||||
return "", err
|
||||
}
|
||||
w.Flush()
|
||||
return strings.TrimSuffix(b.String(), "\n"), nil
|
||||
return strings.TrimSuffix(b.String(), fmt.Sprintln()), nil
|
||||
}
|
||||
|
||||
func (s *stringSliceValue) Set(val string) error {
|
||||
|
||||
-126
@@ -1,126 +0,0 @@
|
||||
package pflag
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// -- uintSlice Value
|
||||
type uintSliceValue struct {
|
||||
value *[]uint
|
||||
changed bool
|
||||
}
|
||||
|
||||
func newUintSliceValue(val []uint, p *[]uint) *uintSliceValue {
|
||||
uisv := new(uintSliceValue)
|
||||
uisv.value = p
|
||||
*uisv.value = val
|
||||
return uisv
|
||||
}
|
||||
|
||||
func (s *uintSliceValue) Set(val string) error {
|
||||
ss := strings.Split(val, ",")
|
||||
out := make([]uint, len(ss))
|
||||
for i, d := range ss {
|
||||
u, err := strconv.ParseUint(d, 10, 0)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out[i] = uint(u)
|
||||
}
|
||||
if !s.changed {
|
||||
*s.value = out
|
||||
} else {
|
||||
*s.value = append(*s.value, out...)
|
||||
}
|
||||
s.changed = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *uintSliceValue) Type() string {
|
||||
return "uintSlice"
|
||||
}
|
||||
|
||||
func (s *uintSliceValue) String() string {
|
||||
out := make([]string, len(*s.value))
|
||||
for i, d := range *s.value {
|
||||
out[i] = fmt.Sprintf("%d", d)
|
||||
}
|
||||
return "[" + strings.Join(out, ",") + "]"
|
||||
}
|
||||
|
||||
func uintSliceConv(val string) (interface{}, error) {
|
||||
val = strings.Trim(val, "[]")
|
||||
// Empty string would cause a slice with one (empty) entry
|
||||
if len(val) == 0 {
|
||||
return []uint{}, nil
|
||||
}
|
||||
ss := strings.Split(val, ",")
|
||||
out := make([]uint, len(ss))
|
||||
for i, d := range ss {
|
||||
u, err := strconv.ParseUint(d, 10, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out[i] = uint(u)
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GetUintSlice returns the []uint value of a flag with the given name.
|
||||
func (f *FlagSet) GetUintSlice(name string) ([]uint, error) {
|
||||
val, err := f.getFlagType(name, "uintSlice", uintSliceConv)
|
||||
if err != nil {
|
||||
return []uint{}, err
|
||||
}
|
||||
return val.([]uint), nil
|
||||
}
|
||||
|
||||
// UintSliceVar defines a uintSlice flag with specified name, default value, and usage string.
|
||||
// The argument p points to a []uint variable in which to store the value of the flag.
|
||||
func (f *FlagSet) UintSliceVar(p *[]uint, name string, value []uint, usage string) {
|
||||
f.VarP(newUintSliceValue(value, p), name, "", usage)
|
||||
}
|
||||
|
||||
// UintSliceVarP is like UintSliceVar, but accepts a shorthand letter that can be used after a single dash.
|
||||
func (f *FlagSet) UintSliceVarP(p *[]uint, name, shorthand string, value []uint, usage string) {
|
||||
f.VarP(newUintSliceValue(value, p), name, shorthand, usage)
|
||||
}
|
||||
|
||||
// UintSliceVar defines a uint[] flag with specified name, default value, and usage string.
|
||||
// The argument p points to a uint[] variable in which to store the value of the flag.
|
||||
func UintSliceVar(p *[]uint, name string, value []uint, usage string) {
|
||||
CommandLine.VarP(newUintSliceValue(value, p), name, "", usage)
|
||||
}
|
||||
|
||||
// UintSliceVarP is like the UintSliceVar, but accepts a shorthand letter that can be used after a single dash.
|
||||
func UintSliceVarP(p *[]uint, name, shorthand string, value []uint, usage string) {
|
||||
CommandLine.VarP(newUintSliceValue(value, p), name, shorthand, usage)
|
||||
}
|
||||
|
||||
// UintSlice defines a []uint flag with specified name, default value, and usage string.
|
||||
// The return value is the address of a []uint variable that stores the value of the flag.
|
||||
func (f *FlagSet) UintSlice(name string, value []uint, usage string) *[]uint {
|
||||
p := []uint{}
|
||||
f.UintSliceVarP(&p, name, "", value, usage)
|
||||
return &p
|
||||
}
|
||||
|
||||
// UintSliceP is like UintSlice, but accepts a shorthand letter that can be used after a single dash.
|
||||
func (f *FlagSet) UintSliceP(name, shorthand string, value []uint, usage string) *[]uint {
|
||||
p := []uint{}
|
||||
f.UintSliceVarP(&p, name, shorthand, value, usage)
|
||||
return &p
|
||||
}
|
||||
|
||||
// UintSlice defines a []uint flag with specified name, default value, and usage string.
|
||||
// The return value is the address of a []uint variable that stores the value of the flag.
|
||||
func UintSlice(name string, value []uint, usage string) *[]uint {
|
||||
return CommandLine.UintSliceP(name, "", value, usage)
|
||||
}
|
||||
|
||||
// UintSliceP is like UintSlice, but accepts a shorthand letter that can be used after a single dash.
|
||||
func UintSliceP(name, shorthand string, value []uint, usage string) *[]uint {
|
||||
return CommandLine.UintSliceP(name, shorthand, value, usage)
|
||||
}
|
||||
+61
-34
@@ -52,24 +52,35 @@ type implStruct struct {
|
||||
outError error
|
||||
}
|
||||
|
||||
func NewJsonPointer(jsonPointerString string) (JsonPointer, error) {
|
||||
|
||||
var p JsonPointer
|
||||
err := p.parse(jsonPointerString)
|
||||
return p, err
|
||||
|
||||
}
|
||||
|
||||
type JsonPointer struct {
|
||||
referenceTokens []string
|
||||
}
|
||||
|
||||
// NewJsonPointer parses the given string JSON pointer and returns an object
|
||||
func NewJsonPointer(jsonPointerString string) (p JsonPointer, err error) {
|
||||
// "Constructor", parses the given string JSON pointer
|
||||
func (p *JsonPointer) parse(jsonPointerString string) error {
|
||||
|
||||
// Pointer to the root of the document
|
||||
if len(jsonPointerString) == 0 {
|
||||
// Keep referenceTokens nil
|
||||
return
|
||||
}
|
||||
if jsonPointerString[0] != '/' {
|
||||
return p, errors.New(const_invalid_start)
|
||||
var err error
|
||||
|
||||
if jsonPointerString != const_empty_pointer {
|
||||
if !strings.HasPrefix(jsonPointerString, const_pointer_separator) {
|
||||
err = errors.New(const_invalid_start)
|
||||
} else {
|
||||
referenceTokens := strings.Split(jsonPointerString, const_pointer_separator)
|
||||
for _, referenceToken := range referenceTokens[1:] {
|
||||
p.referenceTokens = append(p.referenceTokens, referenceToken)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
p.referenceTokens = strings.Split(jsonPointerString[1:], const_pointer_separator)
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
// Uses the pointer to retrieve a value from a JSON document
|
||||
@@ -108,55 +119,64 @@ func (p *JsonPointer) implementation(i *implStruct) {
|
||||
|
||||
for ti, token := range p.referenceTokens {
|
||||
|
||||
decodedToken := decodeReferenceToken(token)
|
||||
isLastToken := ti == len(p.referenceTokens)-1
|
||||
|
||||
switch v := node.(type) {
|
||||
rValue := reflect.ValueOf(node)
|
||||
kind = rValue.Kind()
|
||||
|
||||
case map[string]interface{}:
|
||||
decodedToken := decodeReferenceToken(token)
|
||||
if _, ok := v[decodedToken]; ok {
|
||||
node = v[decodedToken]
|
||||
switch kind {
|
||||
|
||||
case reflect.Map:
|
||||
m := node.(map[string]interface{})
|
||||
if _, ok := m[decodedToken]; ok {
|
||||
node = m[decodedToken]
|
||||
if isLastToken && i.mode == "SET" {
|
||||
v[decodedToken] = i.setInValue
|
||||
m[decodedToken] = i.setInValue
|
||||
}
|
||||
} else {
|
||||
i.outError = fmt.Errorf("Object has no key '%s'", decodedToken)
|
||||
i.getOutKind = reflect.Map
|
||||
i.outError = errors.New(fmt.Sprintf("Object has no key '%s'", token))
|
||||
i.getOutKind = kind
|
||||
i.getOutNode = nil
|
||||
return
|
||||
}
|
||||
|
||||
case []interface{}:
|
||||
case reflect.Slice:
|
||||
s := node.([]interface{})
|
||||
tokenIndex, err := strconv.Atoi(token)
|
||||
if err != nil {
|
||||
i.outError = fmt.Errorf("Invalid array index '%s'", token)
|
||||
i.getOutKind = reflect.Slice
|
||||
i.outError = errors.New(fmt.Sprintf("Invalid array index '%s'", token))
|
||||
i.getOutKind = kind
|
||||
i.getOutNode = nil
|
||||
return
|
||||
}
|
||||
if tokenIndex < 0 || tokenIndex >= len(v) {
|
||||
i.outError = fmt.Errorf("Out of bound array[0,%d] index '%d'", len(v), tokenIndex)
|
||||
i.getOutKind = reflect.Slice
|
||||
sLength := len(s)
|
||||
if tokenIndex < 0 || tokenIndex >= sLength {
|
||||
i.outError = errors.New(fmt.Sprintf("Out of bound array[0,%d] index '%d'", sLength, tokenIndex))
|
||||
i.getOutKind = kind
|
||||
i.getOutNode = nil
|
||||
return
|
||||
}
|
||||
|
||||
node = v[tokenIndex]
|
||||
node = s[tokenIndex]
|
||||
if isLastToken && i.mode == "SET" {
|
||||
v[tokenIndex] = i.setInValue
|
||||
s[tokenIndex] = i.setInValue
|
||||
}
|
||||
|
||||
default:
|
||||
i.outError = fmt.Errorf("Invalid token reference '%s'", token)
|
||||
i.getOutKind = reflect.ValueOf(node).Kind()
|
||||
i.outError = errors.New(fmt.Sprintf("Invalid token reference '%s'", token))
|
||||
i.getOutKind = kind
|
||||
i.getOutNode = nil
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
rValue := reflect.ValueOf(node)
|
||||
kind = rValue.Kind()
|
||||
|
||||
i.getOutNode = node
|
||||
i.getOutKind = reflect.ValueOf(node).Kind()
|
||||
i.getOutKind = kind
|
||||
i.outError = nil
|
||||
}
|
||||
|
||||
@@ -177,14 +197,21 @@ func (p *JsonPointer) String() string {
|
||||
// ~1 => /
|
||||
// ... and vice versa
|
||||
|
||||
const (
|
||||
const_encoded_reference_token_0 = `~0`
|
||||
const_encoded_reference_token_1 = `~1`
|
||||
const_decoded_reference_token_0 = `~`
|
||||
const_decoded_reference_token_1 = `/`
|
||||
)
|
||||
|
||||
func decodeReferenceToken(token string) string {
|
||||
step1 := strings.Replace(token, `~1`, `/`, -1)
|
||||
step2 := strings.Replace(step1, `~0`, `~`, -1)
|
||||
step1 := strings.Replace(token, const_encoded_reference_token_1, const_decoded_reference_token_1, -1)
|
||||
step2 := strings.Replace(step1, const_encoded_reference_token_0, const_decoded_reference_token_0, -1)
|
||||
return step2
|
||||
}
|
||||
|
||||
func encodeReferenceToken(token string) string {
|
||||
step1 := strings.Replace(token, `~`, `~0`, -1)
|
||||
step2 := strings.Replace(step1, `/`, `~1`, -1)
|
||||
step1 := strings.Replace(token, const_decoded_reference_token_1, const_encoded_reference_token_1, -1)
|
||||
step2 := strings.Replace(step1, const_decoded_reference_token_0, const_encoded_reference_token_0, -1)
|
||||
return step2
|
||||
}
|
||||
|
||||
+8
-40
@@ -1,20 +1,10 @@
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"sync"
|
||||
"text/template"
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var errorTemplates errorTemplate = errorTemplate{template.New("errors-new"), sync.RWMutex{}}
|
||||
|
||||
// template.Template is not thread-safe for writing, so some locking is done
|
||||
// sync.RWMutex is used for efficiently locking when new templates are created
|
||||
type errorTemplate struct {
|
||||
*template.Template
|
||||
sync.RWMutex
|
||||
}
|
||||
|
||||
type (
|
||||
// RequiredError. ErrorDetails: property string
|
||||
RequiredError struct {
|
||||
@@ -240,35 +230,13 @@ func newError(err ResultError, context *jsonContext, value interface{}, locale l
|
||||
err.SetDescription(formatErrorDescription(d, details))
|
||||
}
|
||||
|
||||
// formatErrorDescription takes a string in the default text/template
|
||||
// format and converts it to a string with replacements. The fields come
|
||||
// from the ErrorDetails struct and vary for each type of error.
|
||||
// formatErrorDescription takes a string in this format: %field% is required
|
||||
// and converts it to a string with replacements. The fields come from
|
||||
// the ErrorDetails struct and vary for each type of error.
|
||||
func formatErrorDescription(s string, details ErrorDetails) string {
|
||||
|
||||
var tpl *template.Template
|
||||
var descrAsBuffer bytes.Buffer
|
||||
var err error
|
||||
|
||||
errorTemplates.RLock()
|
||||
tpl = errorTemplates.Lookup(s)
|
||||
errorTemplates.RUnlock()
|
||||
|
||||
if tpl == nil {
|
||||
errorTemplates.Lock()
|
||||
tpl = errorTemplates.New(s)
|
||||
|
||||
tpl, err = tpl.Parse(s)
|
||||
errorTemplates.Unlock()
|
||||
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
for name, val := range details {
|
||||
s = strings.Replace(s, "%"+strings.ToLower(name)+"%", fmt.Sprintf("%v", val), -1)
|
||||
}
|
||||
|
||||
err = tpl.Execute(&descrAsBuffer, details)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
return descrAsBuffer.String()
|
||||
return s
|
||||
}
|
||||
|
||||
+4
-21
@@ -5,7 +5,6 @@ import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -60,9 +59,6 @@ type (
|
||||
|
||||
// UUIDFormatChecker validates a UUID is in the correct format
|
||||
UUIDFormatChecker struct{}
|
||||
|
||||
// RegexFormatChecker validates a regex is in the correct format
|
||||
RegexFormatChecker struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -77,7 +73,6 @@ var (
|
||||
"ipv6": IPV6FormatChecker{},
|
||||
"uri": URIFormatChecker{},
|
||||
"uuid": UUIDFormatChecker{},
|
||||
"regex": RegexFormatChecker{},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -85,7 +80,7 @@ var (
|
||||
rxEmail = regexp.MustCompile("^(((([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+(\\.([a-zA-Z]|\\d|[!#\\$%&'\\*\\+\\-\\/=\\?\\^_`{\\|}~]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])+)*)|((\\x22)((((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(([\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x7f]|\\x21|[\\x23-\\x5b]|[\\x5d-\\x7e]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(\\([\\x01-\\x09\\x0b\\x0c\\x0d-\\x7f]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}]))))*(((\\x20|\\x09)*(\\x0d\\x0a))?(\\x20|\\x09)+)?(\\x22)))@((([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|\\d|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.)+(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])|(([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])([a-zA-Z]|\\d|-|\\.|_|~|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])*([a-zA-Z]|[\\x{00A0}-\\x{D7FF}\\x{F900}-\\x{FDCF}\\x{FDF0}-\\x{FFEF}])))\\.?$")
|
||||
|
||||
// Regex credit: https://www.socketloop.com/tutorials/golang-validate-hostname
|
||||
rxHostname = regexp.MustCompile(`^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])(\.([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]{0,61}[a-zA-Z0-9]))*$`)
|
||||
rxHostname = regexp.MustCompile(`^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$`)
|
||||
|
||||
rxUUID = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
|
||||
)
|
||||
@@ -137,13 +132,13 @@ func (f EmailFormatChecker) IsFormat(input string) bool {
|
||||
// Credit: https://github.com/asaskevich/govalidator
|
||||
func (f IPV4FormatChecker) IsFormat(input string) bool {
|
||||
ip := net.ParseIP(input)
|
||||
return ip != nil && strings.Contains(input, ".")
|
||||
return ip != nil && ip.To4() != nil
|
||||
}
|
||||
|
||||
// Credit: https://github.com/asaskevich/govalidator
|
||||
func (f IPV6FormatChecker) IsFormat(input string) bool {
|
||||
ip := net.ParseIP(input)
|
||||
return ip != nil && strings.Contains(input, ":")
|
||||
return ip != nil && ip.To4() == nil
|
||||
}
|
||||
|
||||
func (f DateTimeFormatChecker) IsFormat(input string) bool {
|
||||
@@ -174,21 +169,9 @@ func (f URIFormatChecker) IsFormat(input string) bool {
|
||||
}
|
||||
|
||||
func (f HostnameFormatChecker) IsFormat(input string) bool {
|
||||
return rxHostname.MatchString(input) && len(input) < 256
|
||||
return rxHostname.MatchString(input)
|
||||
}
|
||||
|
||||
func (f UUIDFormatChecker) IsFormat(input string) bool {
|
||||
return rxUUID.MatchString(input)
|
||||
}
|
||||
|
||||
// IsFormat implements FormatChecker interface.
|
||||
func (f RegexFormatChecker) IsFormat(input string) bool {
|
||||
if input == "" {
|
||||
return true
|
||||
}
|
||||
_, err := regexp.Compile(input)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
+82
-136
@@ -33,7 +33,6 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -41,92 +40,34 @@ import (
|
||||
"github.com/xeipuuv/gojsonreference"
|
||||
)
|
||||
|
||||
var osFS = osFileSystem(os.Open)
|
||||
|
||||
// JSON loader interface
|
||||
|
||||
type JSONLoader interface {
|
||||
JsonSource() interface{}
|
||||
LoadJSON() (interface{}, error)
|
||||
JsonReference() (gojsonreference.JsonReference, error)
|
||||
LoaderFactory() JSONLoaderFactory
|
||||
}
|
||||
|
||||
type JSONLoaderFactory interface {
|
||||
New(source string) JSONLoader
|
||||
}
|
||||
|
||||
type DefaultJSONLoaderFactory struct {
|
||||
}
|
||||
|
||||
type FileSystemJSONLoaderFactory struct {
|
||||
fs http.FileSystem
|
||||
}
|
||||
|
||||
func (d DefaultJSONLoaderFactory) New(source string) JSONLoader {
|
||||
return &jsonReferenceLoader{
|
||||
fs: osFS,
|
||||
source: source,
|
||||
}
|
||||
}
|
||||
|
||||
func (f FileSystemJSONLoaderFactory) New(source string) JSONLoader {
|
||||
return &jsonReferenceLoader{
|
||||
fs: f.fs,
|
||||
source: source,
|
||||
}
|
||||
}
|
||||
|
||||
// osFileSystem is a functional wrapper for os.Open that implements http.FileSystem.
|
||||
type osFileSystem func(string) (*os.File, error)
|
||||
|
||||
func (o osFileSystem) Open(name string) (http.File, error) {
|
||||
return o(name)
|
||||
jsonSource() interface{}
|
||||
loadJSON() (interface{}, error)
|
||||
loadSchema() (*Schema, error)
|
||||
}
|
||||
|
||||
// JSON Reference loader
|
||||
// references are used to load JSONs from files and HTTP
|
||||
|
||||
type jsonReferenceLoader struct {
|
||||
fs http.FileSystem
|
||||
source string
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) JsonSource() interface{} {
|
||||
func (l *jsonReferenceLoader) jsonSource() interface{} {
|
||||
return l.source
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
||||
return gojsonreference.NewJsonReference(l.JsonSource().(string))
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) LoaderFactory() JSONLoaderFactory {
|
||||
return &FileSystemJSONLoaderFactory{
|
||||
fs: l.fs,
|
||||
}
|
||||
}
|
||||
|
||||
// NewReferenceLoader returns a JSON reference loader using the given source and the local OS file system.
|
||||
func NewReferenceLoader(source string) *jsonReferenceLoader {
|
||||
return &jsonReferenceLoader{
|
||||
fs: osFS,
|
||||
source: source,
|
||||
}
|
||||
return &jsonReferenceLoader{source: source}
|
||||
}
|
||||
|
||||
// NewReferenceLoaderFileSystem returns a JSON reference loader using the given source and file system.
|
||||
func NewReferenceLoaderFileSystem(source string, fs http.FileSystem) *jsonReferenceLoader {
|
||||
return &jsonReferenceLoader{
|
||||
fs: fs,
|
||||
source: source,
|
||||
}
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) {
|
||||
func (l *jsonReferenceLoader) loadJSON() (interface{}, error) {
|
||||
|
||||
var err error
|
||||
|
||||
reference, err := gojsonreference.NewJsonReference(l.JsonSource().(string))
|
||||
reference, err := gojsonreference.NewJsonReference(l.jsonSource().(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -138,7 +79,7 @@ func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) {
|
||||
|
||||
if reference.HasFileScheme {
|
||||
|
||||
filename := strings.Replace(refToUrl.GetUrl().Path, "file://", "", -1)
|
||||
filename := strings.Replace(refToUrl.String(), "file://", "", -1)
|
||||
if runtime.GOOS == "windows" {
|
||||
// on Windows, a file URL may have an extra leading slash, use slashes
|
||||
// instead of backslashes, and have spaces escaped
|
||||
@@ -146,6 +87,7 @@ func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) {
|
||||
filename = filename[1:]
|
||||
}
|
||||
filename = filepath.FromSlash(filename)
|
||||
filename = strings.Replace(filename, "%20", " ", -1)
|
||||
}
|
||||
|
||||
document, err = l.loadFromFile(filename)
|
||||
@@ -166,6 +108,33 @@ func (l *jsonReferenceLoader) LoadJSON() (interface{}, error) {
|
||||
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) loadSchema() (*Schema, error) {
|
||||
|
||||
var err error
|
||||
|
||||
d := Schema{}
|
||||
d.pool = newSchemaPool()
|
||||
d.referencePool = newSchemaReferencePool()
|
||||
|
||||
d.documentReference, err = gojsonreference.NewJsonReference(l.jsonSource().(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
spd, err := d.pool.GetDocument(d.documentReference)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = d.parse(spd.Document)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error) {
|
||||
|
||||
resp, err := http.Get(address)
|
||||
@@ -175,7 +144,7 @@ func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error)
|
||||
|
||||
// must return HTTP Status 200 OK
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, errors.New(formatErrorDescription(Locale.HttpBadStatus(), ErrorDetails{"status": resp.Status}))
|
||||
return nil, errors.New(formatErrorDescription(Locale.httpBadStatus(), ErrorDetails{"status": resp.Status}))
|
||||
}
|
||||
|
||||
bodyBuff, err := ioutil.ReadAll(resp.Body)
|
||||
@@ -188,13 +157,8 @@ func (l *jsonReferenceLoader) loadFromHTTP(address string) (interface{}, error)
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) loadFromFile(path string) (interface{}, error) {
|
||||
f, err := l.fs.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
bodyBuff, err := ioutil.ReadAll(f)
|
||||
bodyBuff, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -209,52 +173,45 @@ type jsonStringLoader struct {
|
||||
source string
|
||||
}
|
||||
|
||||
func (l *jsonStringLoader) JsonSource() interface{} {
|
||||
func (l *jsonStringLoader) jsonSource() interface{} {
|
||||
return l.source
|
||||
}
|
||||
|
||||
func (l *jsonStringLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
||||
return gojsonreference.NewJsonReference("#")
|
||||
}
|
||||
|
||||
func (l *jsonStringLoader) LoaderFactory() JSONLoaderFactory {
|
||||
return &DefaultJSONLoaderFactory{}
|
||||
}
|
||||
|
||||
func NewStringLoader(source string) *jsonStringLoader {
|
||||
return &jsonStringLoader{source: source}
|
||||
}
|
||||
|
||||
func (l *jsonStringLoader) LoadJSON() (interface{}, error) {
|
||||
func (l *jsonStringLoader) loadJSON() (interface{}, error) {
|
||||
|
||||
return decodeJsonUsingNumber(strings.NewReader(l.JsonSource().(string)))
|
||||
return decodeJsonUsingNumber(strings.NewReader(l.jsonSource().(string)))
|
||||
|
||||
}
|
||||
|
||||
// JSON bytes loader
|
||||
func (l *jsonStringLoader) loadSchema() (*Schema, error) {
|
||||
|
||||
type jsonBytesLoader struct {
|
||||
source []byte
|
||||
}
|
||||
var err error
|
||||
|
||||
func (l *jsonBytesLoader) JsonSource() interface{} {
|
||||
return l.source
|
||||
}
|
||||
document, err := l.loadJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (l *jsonBytesLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
||||
return gojsonreference.NewJsonReference("#")
|
||||
}
|
||||
d := Schema{}
|
||||
d.pool = newSchemaPool()
|
||||
d.referencePool = newSchemaReferencePool()
|
||||
d.documentReference, err = gojsonreference.NewJsonReference("#")
|
||||
d.pool.SetStandaloneDocument(document)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (l *jsonBytesLoader) LoaderFactory() JSONLoaderFactory {
|
||||
return &DefaultJSONLoaderFactory{}
|
||||
}
|
||||
err = d.parse(document)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func NewBytesLoader(source []byte) *jsonBytesLoader {
|
||||
return &jsonBytesLoader{source: source}
|
||||
}
|
||||
return &d, nil
|
||||
|
||||
func (l *jsonBytesLoader) LoadJSON() (interface{}, error) {
|
||||
return decodeJsonUsingNumber(bytes.NewReader(l.JsonSource().([]byte)))
|
||||
}
|
||||
|
||||
// JSON Go (types) loader
|
||||
@@ -264,27 +221,19 @@ type jsonGoLoader struct {
|
||||
source interface{}
|
||||
}
|
||||
|
||||
func (l *jsonGoLoader) JsonSource() interface{} {
|
||||
func (l *jsonGoLoader) jsonSource() interface{} {
|
||||
return l.source
|
||||
}
|
||||
|
||||
func (l *jsonGoLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
||||
return gojsonreference.NewJsonReference("#")
|
||||
}
|
||||
|
||||
func (l *jsonGoLoader) LoaderFactory() JSONLoaderFactory {
|
||||
return &DefaultJSONLoaderFactory{}
|
||||
}
|
||||
|
||||
func NewGoLoader(source interface{}) *jsonGoLoader {
|
||||
return &jsonGoLoader{source: source}
|
||||
}
|
||||
|
||||
func (l *jsonGoLoader) LoadJSON() (interface{}, error) {
|
||||
func (l *jsonGoLoader) loadJSON() (interface{}, error) {
|
||||
|
||||
// convert it to a compliant JSON first to avoid types "mismatches"
|
||||
|
||||
jsonBytes, err := json.Marshal(l.JsonSource())
|
||||
jsonBytes, err := json.Marshal(l.jsonSource())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -293,34 +242,31 @@ func (l *jsonGoLoader) LoadJSON() (interface{}, error) {
|
||||
|
||||
}
|
||||
|
||||
type jsonIOLoader struct {
|
||||
buf *bytes.Buffer
|
||||
}
|
||||
func (l *jsonGoLoader) loadSchema() (*Schema, error) {
|
||||
|
||||
func NewReaderLoader(source io.Reader) (*jsonIOLoader, io.Reader) {
|
||||
buf := &bytes.Buffer{}
|
||||
return &jsonIOLoader{buf: buf}, io.TeeReader(source, buf)
|
||||
}
|
||||
var err error
|
||||
|
||||
func NewWriterLoader(source io.Writer) (*jsonIOLoader, io.Writer) {
|
||||
buf := &bytes.Buffer{}
|
||||
return &jsonIOLoader{buf: buf}, io.MultiWriter(source, buf)
|
||||
}
|
||||
document, err := l.loadJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (l *jsonIOLoader) JsonSource() interface{} {
|
||||
return l.buf.String()
|
||||
}
|
||||
d := Schema{}
|
||||
d.pool = newSchemaPool()
|
||||
d.referencePool = newSchemaReferencePool()
|
||||
d.documentReference, err = gojsonreference.NewJsonReference("#")
|
||||
d.pool.SetStandaloneDocument(document)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (l *jsonIOLoader) LoadJSON() (interface{}, error) {
|
||||
return decodeJsonUsingNumber(l.buf)
|
||||
}
|
||||
err = d.parse(document)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
func (l *jsonIOLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
||||
return gojsonreference.NewJsonReference("#")
|
||||
}
|
||||
return &d, nil
|
||||
|
||||
func (l *jsonIOLoader) LoaderFactory() JSONLoaderFactory {
|
||||
return &DefaultJSONLoaderFactory{}
|
||||
}
|
||||
|
||||
func decodeJsonUsingNumber(r io.Reader) (interface{}, error) {
|
||||
|
||||
+42
-47
@@ -26,7 +26,7 @@
|
||||
package gojsonschema
|
||||
|
||||
type (
|
||||
// locale is an interface for defining custom error strings
|
||||
// locale is an interface for definining custom error strings
|
||||
locale interface {
|
||||
Required() string
|
||||
InvalidType() string
|
||||
@@ -37,7 +37,6 @@ type (
|
||||
MissingDependency() string
|
||||
Internal() string
|
||||
Enum() string
|
||||
ArrayNotEnoughItems() string
|
||||
ArrayNoAdditionalItems() string
|
||||
ArrayMinItems() string
|
||||
ArrayMaxItems() string
|
||||
@@ -73,7 +72,7 @@ type (
|
||||
ReferenceMustBeCanonical() string
|
||||
NotAValidType() string
|
||||
Duplicated() string
|
||||
HttpBadStatus() string
|
||||
httpBadStatus() string
|
||||
|
||||
// ErrorFormat
|
||||
ErrorFormat() string
|
||||
@@ -84,11 +83,11 @@ type (
|
||||
)
|
||||
|
||||
func (l DefaultLocale) Required() string {
|
||||
return `{{.property}} is required`
|
||||
return `%property% is required`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) InvalidType() string {
|
||||
return `Invalid type. Expected: {{.expected}}, given: {{.given}}`
|
||||
return `Invalid type. Expected: %expected%, given: %given%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberAnyOf() string {
|
||||
@@ -108,161 +107,157 @@ func (l DefaultLocale) NumberNot() string {
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MissingDependency() string {
|
||||
return `Has a dependency on {{.dependency}}`
|
||||
return `Has a dependency on %dependency%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) Internal() string {
|
||||
return `Internal Error {{.error}}`
|
||||
return `Internal Error %error%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) Enum() string {
|
||||
return `{{.field}} must be one of the following: {{.allowed}}`
|
||||
return `%field% must be one of the following: %allowed%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ArrayNoAdditionalItems() string {
|
||||
return `No additional items allowed on array`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ArrayNotEnoughItems() string {
|
||||
return `Not enough items on array to match positional list of schema`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ArrayMinItems() string {
|
||||
return `Array must have at least {{.min}} items`
|
||||
return `Array must have at least %min% items`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ArrayMaxItems() string {
|
||||
return `Array must have at most {{.max}} items`
|
||||
return `Array must have at most %max% items`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) Unique() string {
|
||||
return `{{.type}} items must be unique`
|
||||
return `%type% items must be unique`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ArrayMinProperties() string {
|
||||
return `Must have at least {{.min}} properties`
|
||||
return `Must have at least %min% properties`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ArrayMaxProperties() string {
|
||||
return `Must have at most {{.max}} properties`
|
||||
return `Must have at most %max% properties`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) AdditionalPropertyNotAllowed() string {
|
||||
return `Additional property {{.property}} is not allowed`
|
||||
return `Additional property %property% is not allowed`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) InvalidPropertyPattern() string {
|
||||
return `Property "{{.property}}" does not match pattern {{.pattern}}`
|
||||
return `Property "%property%" does not match pattern %pattern%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) StringGTE() string {
|
||||
return `String length must be greater than or equal to {{.min}}`
|
||||
return `String length must be greater than or equal to %min%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) StringLTE() string {
|
||||
return `String length must be less than or equal to {{.max}}`
|
||||
return `String length must be less than or equal to %max%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) DoesNotMatchPattern() string {
|
||||
return `Does not match pattern '{{.pattern}}'`
|
||||
return `Does not match pattern '%pattern%'`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) DoesNotMatchFormat() string {
|
||||
return `Does not match format '{{.format}}'`
|
||||
return `Does not match format '%format%'`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MultipleOf() string {
|
||||
return `Must be a multiple of {{.multiple}}`
|
||||
return `Must be a multiple of %multiple%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberGTE() string {
|
||||
return `Must be greater than or equal to {{.min}}`
|
||||
return `Must be greater than or equal to %min%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberGT() string {
|
||||
return `Must be greater than {{.min}}`
|
||||
return `Must be greater than %min%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberLTE() string {
|
||||
return `Must be less than or equal to {{.max}}`
|
||||
return `Must be less than or equal to %max%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NumberLT() string {
|
||||
return `Must be less than {{.max}}`
|
||||
return `Must be less than %max%`
|
||||
}
|
||||
|
||||
// Schema validators
|
||||
func (l DefaultLocale) RegexPattern() string {
|
||||
return `Invalid regex pattern '{{.pattern}}'`
|
||||
return `Invalid regex pattern '%pattern%'`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) GreaterThanZero() string {
|
||||
return `{{.number}} must be strictly greater than 0`
|
||||
return `%number% must be strictly greater than 0`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MustBeOfA() string {
|
||||
return `{{.x}} must be of a {{.y}}`
|
||||
return `%x% must be of a %y%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MustBeOfAn() string {
|
||||
return `{{.x}} must be of an {{.y}}`
|
||||
return `%x% must be of an %y%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) CannotBeUsedWithout() string {
|
||||
return `{{.x}} cannot be used without {{.y}}`
|
||||
return `%x% cannot be used without %y%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) CannotBeGT() string {
|
||||
return `{{.x}} cannot be greater than {{.y}}`
|
||||
return `%x% cannot be greater than %y%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MustBeOfType() string {
|
||||
return `{{.key}} must be of type {{.type}}`
|
||||
return `%key% must be of type %type%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MustBeValidRegex() string {
|
||||
return `{{.key}} must be a valid regex`
|
||||
return `%key% must be a valid regex`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MustBeValidFormat() string {
|
||||
return `{{.key}} must be a valid format {{.given}}`
|
||||
return `%key% must be a valid format %given%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) MustBeGTEZero() string {
|
||||
return `{{.key}} must be greater than or equal to 0`
|
||||
return `%key% must be greater than or equal to 0`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) KeyCannotBeGreaterThan() string {
|
||||
return `{{.key}} cannot be greater than {{.y}}`
|
||||
return `%key% cannot be greater than %y%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) KeyItemsMustBeOfType() string {
|
||||
return `{{.key}} items must be {{.type}}`
|
||||
return `%key% items must be %type%`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) KeyItemsMustBeUnique() string {
|
||||
return `{{.key}} items must be unique`
|
||||
return `%key% items must be unique`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) ReferenceMustBeCanonical() string {
|
||||
return `Reference {{.reference}} must be canonical`
|
||||
return `Reference %reference% must be canonical`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) NotAValidType() string {
|
||||
return `{{.type}} is not a valid type -- `
|
||||
return `%type% is not a valid type -- `
|
||||
}
|
||||
|
||||
func (l DefaultLocale) Duplicated() string {
|
||||
return `{{.type}} type is duplicated`
|
||||
return `%type% type is duplicated`
|
||||
}
|
||||
|
||||
func (l DefaultLocale) HttpBadStatus() string {
|
||||
return `Could not read schema from HTTP, response status is {{.status}}`
|
||||
func (l DefaultLocale) httpBadStatus() string {
|
||||
return `Could not read schema from HTTP, response status is %status%`
|
||||
}
|
||||
|
||||
// Replacement options: field, description, context, value
|
||||
func (l DefaultLocale) ErrorFormat() string {
|
||||
return `{{.field}}: {{.description}}`
|
||||
return `%field%: %description%`
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
+1
-2
@@ -48,7 +48,6 @@ type (
|
||||
Value() interface{}
|
||||
SetDetails(ErrorDetails)
|
||||
Details() ErrorDetails
|
||||
String() string
|
||||
}
|
||||
|
||||
// ResultErrorFields holds the fields for each ResultError implementation.
|
||||
@@ -127,7 +126,7 @@ func (v ResultErrorFields) String() string {
|
||||
valueString := fmt.Sprintf("%v", v.value)
|
||||
|
||||
// marshal the go value value to json
|
||||
if v.value == nil {
|
||||
if v.Value == nil {
|
||||
valueString = TYPE_NULL
|
||||
} else {
|
||||
if vs, err := marshalToJsonString(v.value); err == nil {
|
||||
|
||||
+32
-54
@@ -42,39 +42,7 @@ var (
|
||||
)
|
||||
|
||||
func NewSchema(l JSONLoader) (*Schema, error) {
|
||||
ref, err := l.JsonReference()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := Schema{}
|
||||
d.pool = newSchemaPool(l.LoaderFactory())
|
||||
d.documentReference = ref
|
||||
d.referencePool = newSchemaReferencePool()
|
||||
|
||||
var doc interface{}
|
||||
if ref.String() != "" {
|
||||
// Get document from schema pool
|
||||
spd, err := d.pool.GetDocument(d.documentReference)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
doc = spd.Document
|
||||
} else {
|
||||
// Load JSON directly
|
||||
doc, err = l.LoadJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
d.pool.SetStandaloneDocument(doc)
|
||||
}
|
||||
|
||||
err = d.parse(doc)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
return l.loadSchema()
|
||||
}
|
||||
|
||||
type Schema struct {
|
||||
@@ -148,27 +116,14 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
|
||||
}
|
||||
if k, ok := m[KEY_REF].(string); ok {
|
||||
|
||||
jsonReference, err := gojsonreference.NewJsonReference(k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if jsonReference.HasFullUrl {
|
||||
currentSchema.ref = &jsonReference
|
||||
} else {
|
||||
inheritedReference, err := currentSchema.ref.Inherits(jsonReference)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
currentSchema.ref = inheritedReference
|
||||
}
|
||||
|
||||
if sch, ok := d.referencePool.Get(currentSchema.ref.String() + k); ok {
|
||||
|
||||
currentSchema.refSchema = sch
|
||||
|
||||
} else {
|
||||
err := d.parseReference(documentNode, currentSchema, k)
|
||||
|
||||
var err error
|
||||
err = d.parseReference(documentNode, currentSchema, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -800,11 +755,31 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema, reference string) error {
|
||||
var refdDocumentNode interface{}
|
||||
jsonPointer := currentSchema.ref.GetPointer()
|
||||
func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema, reference string) (e error) {
|
||||
|
||||
var err error
|
||||
|
||||
jsonReference, err := gojsonreference.NewJsonReference(reference)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
standaloneDocument := d.pool.GetStandaloneDocument()
|
||||
|
||||
if jsonReference.HasFullUrl {
|
||||
currentSchema.ref = &jsonReference
|
||||
} else {
|
||||
inheritedReference, err := currentSchema.ref.Inherits(jsonReference)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
currentSchema.ref = inheritedReference
|
||||
}
|
||||
|
||||
jsonPointer := currentSchema.ref.GetPointer()
|
||||
|
||||
var refdDocumentNode interface{}
|
||||
|
||||
if standaloneDocument != nil {
|
||||
|
||||
var err error
|
||||
@@ -814,6 +789,8 @@ func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSche
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
var err error
|
||||
dsp, err := d.pool.GetDocument(*currentSchema.ref)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -835,10 +812,11 @@ func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSche
|
||||
|
||||
// returns the loaded referenced subSchema for the caller to update its current subSchema
|
||||
newSchemaDocument := refdDocumentNode.(map[string]interface{})
|
||||
|
||||
newSchema := &subSchema{property: KEY_REF, parent: currentSchema, ref: currentSchema.ref}
|
||||
d.referencePool.Add(currentSchema.ref.String()+reference, newSchema)
|
||||
|
||||
err := d.parseSchema(newSchemaDocument, newSchema)
|
||||
err = d.parseSchema(newSchemaDocument, newSchema)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
+3
-5
@@ -39,15 +39,13 @@ type schemaPoolDocument struct {
|
||||
type schemaPool struct {
|
||||
schemaPoolDocuments map[string]*schemaPoolDocument
|
||||
standaloneDocument interface{}
|
||||
jsonLoaderFactory JSONLoaderFactory
|
||||
}
|
||||
|
||||
func newSchemaPool(f JSONLoaderFactory) *schemaPool {
|
||||
func newSchemaPool() *schemaPool {
|
||||
|
||||
p := &schemaPool{}
|
||||
p.schemaPoolDocuments = make(map[string]*schemaPoolDocument)
|
||||
p.standaloneDocument = nil
|
||||
p.jsonLoaderFactory = f
|
||||
|
||||
return p
|
||||
}
|
||||
@@ -95,8 +93,8 @@ func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*sche
|
||||
return spd, nil
|
||||
}
|
||||
|
||||
jsonReferenceLoader := p.jsonLoaderFactory.New(reference.String())
|
||||
document, err := jsonReferenceLoader.LoadJSON()
|
||||
jsonReferenceLoader := NewReferenceLoader(reference.String())
|
||||
document, err := jsonReferenceLoader.loadJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
+1
-1
@@ -214,7 +214,7 @@ func (s *subSchema) PatternPropertiesString() string {
|
||||
}
|
||||
|
||||
patternPropertiesKeySlice := []string{}
|
||||
for pk := range s.patternProperties {
|
||||
for pk, _ := range s.patternProperties {
|
||||
patternPropertiesKeySlice = append(patternPropertiesKeySlice, `"`+pk+`"`)
|
||||
}
|
||||
|
||||
|
||||
+4
-10
@@ -34,12 +34,7 @@ import (
|
||||
)
|
||||
|
||||
func isKind(what interface{}, kind reflect.Kind) bool {
|
||||
target := what
|
||||
if isJsonNumber(what) {
|
||||
// JSON Numbers are strings!
|
||||
target = *mustBeNumber(what)
|
||||
}
|
||||
return reflect.ValueOf(target).Kind() == kind
|
||||
return reflect.ValueOf(what).Kind() == kind
|
||||
}
|
||||
|
||||
func existsMapKey(m map[string]interface{}, k string) bool {
|
||||
@@ -82,14 +77,13 @@ func checkJsonNumber(what interface{}) (isValidFloat64 bool, isValidInt64 bool,
|
||||
|
||||
jsonNumber := what.(json.Number)
|
||||
|
||||
f64, errFloat64 := jsonNumber.Float64()
|
||||
s64 := strconv.FormatFloat(f64, 'f', -1, 64)
|
||||
_, errInt64 := strconv.ParseInt(s64, 10, 64)
|
||||
_, errFloat64 := jsonNumber.Float64()
|
||||
_, errInt64 := jsonNumber.Int64()
|
||||
|
||||
isValidFloat64 = errFloat64 == nil
|
||||
isValidInt64 = errInt64 == nil
|
||||
|
||||
_, errInt32 := strconv.ParseInt(s64, 10, 32)
|
||||
_, errInt32 := strconv.ParseInt(jsonNumber.String(), 10, 32)
|
||||
isValidInt32 = isValidInt64 && errInt32 == nil
|
||||
|
||||
return
|
||||
|
||||
+20
-23
@@ -55,7 +55,7 @@ func (v *Schema) Validate(l JSONLoader) (*Result, error) {
|
||||
|
||||
// load document
|
||||
|
||||
root, err := l.LoadJSON()
|
||||
root, err := l.loadJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -412,7 +412,7 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface
|
||||
internalLog(" %v", value)
|
||||
}
|
||||
|
||||
nbValues := len(value)
|
||||
nbItems := len(value)
|
||||
|
||||
// TODO explain
|
||||
if currentSubSchema.itemsChildrenIsSingleSchema {
|
||||
@@ -425,18 +425,15 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface
|
||||
if currentSubSchema.itemsChildren != nil && len(currentSubSchema.itemsChildren) > 0 {
|
||||
|
||||
nbItems := len(currentSubSchema.itemsChildren)
|
||||
nbValues := len(value)
|
||||
|
||||
// while we have both schemas and values, check them against each other
|
||||
for i := 0; i != nbItems && i != nbValues; i++ {
|
||||
subContext := newJsonContext(strconv.Itoa(i), context)
|
||||
validationResult := currentSubSchema.itemsChildren[i].subValidateWithContext(value[i], subContext)
|
||||
result.mergeErrors(validationResult)
|
||||
}
|
||||
|
||||
if nbItems < nbValues {
|
||||
// we have less schemas than elements in the instance array,
|
||||
// but that might be ok if "additionalItems" is specified.
|
||||
|
||||
if nbItems == nbValues {
|
||||
for i := 0; i != nbItems; i++ {
|
||||
subContext := newJsonContext(strconv.Itoa(i), context)
|
||||
validationResult := currentSubSchema.itemsChildren[i].subValidateWithContext(value[i], subContext)
|
||||
result.mergeErrors(validationResult)
|
||||
}
|
||||
} else if nbItems < nbValues {
|
||||
switch currentSubSchema.additionalItems.(type) {
|
||||
case bool:
|
||||
if !currentSubSchema.additionalItems.(bool) {
|
||||
@@ -456,7 +453,7 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface
|
||||
|
||||
// minItems & maxItems
|
||||
if currentSubSchema.minItems != nil {
|
||||
if nbValues < int(*currentSubSchema.minItems) {
|
||||
if nbItems < int(*currentSubSchema.minItems) {
|
||||
result.addError(
|
||||
new(ArrayMinItemsError),
|
||||
context,
|
||||
@@ -466,7 +463,7 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface
|
||||
}
|
||||
}
|
||||
if currentSubSchema.maxItems != nil {
|
||||
if nbValues > int(*currentSubSchema.maxItems) {
|
||||
if nbItems > int(*currentSubSchema.maxItems) {
|
||||
result.addError(
|
||||
new(ArrayMaxItemsError),
|
||||
context,
|
||||
@@ -779,22 +776,22 @@ func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{
|
||||
if currentSubSchema.exclusiveMaximum {
|
||||
if float64Value >= *currentSubSchema.maximum {
|
||||
result.addError(
|
||||
new(NumberLTError),
|
||||
new(NumberLTEError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{
|
||||
"max": resultErrorFormatNumber(*currentSubSchema.maximum),
|
||||
"min": resultErrorFormatNumber(*currentSubSchema.maximum),
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if float64Value > *currentSubSchema.maximum {
|
||||
result.addError(
|
||||
new(NumberLTEError),
|
||||
new(NumberLTError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{
|
||||
"max": resultErrorFormatNumber(*currentSubSchema.maximum),
|
||||
"min": resultErrorFormatNumber(*currentSubSchema.maximum),
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -806,22 +803,22 @@ func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{
|
||||
if currentSubSchema.exclusiveMinimum {
|
||||
if float64Value <= *currentSubSchema.minimum {
|
||||
result.addError(
|
||||
new(NumberGTError),
|
||||
new(NumberGTEError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{
|
||||
"min": resultErrorFormatNumber(*currentSubSchema.minimum),
|
||||
"max": resultErrorFormatNumber(*currentSubSchema.minimum),
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if float64Value < *currentSubSchema.minimum {
|
||||
result.addError(
|
||||
new(NumberGTEError),
|
||||
new(NumberGTError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{
|
||||
"min": resultErrorFormatNumber(*currentSubSchema.minimum),
|
||||
"max": resultErrorFormatNumber(*currentSubSchema.minimum),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user