forked from LaconicNetwork/kompose
Update logging for logrus
This commit is contained in:
+64
@@ -0,0 +1,64 @@
|
||||
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)
|
||||
}
|
||||
+34
-23
@@ -3,11 +3,21 @@ 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"
|
||||
|
||||
@@ -29,6 +39,9 @@ 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 {
|
||||
@@ -39,21 +52,15 @@ 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) {
|
||||
reader, err := entry.Reader()
|
||||
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return reader.String(), err
|
||||
str := string(serialized)
|
||||
return str, nil
|
||||
}
|
||||
|
||||
// Add an error as single field (using the key defined in ErrorKey) to the Entry.
|
||||
@@ -81,6 +88,7 @@ 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
|
||||
@@ -90,20 +98,23 @@ func (entry Entry) log(level Level, msg string) {
|
||||
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||
entry.Logger.mu.Unlock()
|
||||
}
|
||||
|
||||
reader, err := entry.Reader()
|
||||
buffer = bufferPool.Get().(*bytes.Buffer)
|
||||
buffer.Reset()
|
||||
defer bufferPool.Put(buffer)
|
||||
entry.Buffer = buffer
|
||||
serialized, err := entry.Logger.Formatter.Format(&entry)
|
||||
entry.Buffer = nil
|
||||
if err != nil {
|
||||
entry.Logger.mu.Lock()
|
||||
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %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)
|
||||
} 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()
|
||||
}
|
||||
|
||||
// To avoid Entry#log() returning a value that only would make sense for
|
||||
@@ -150,7 +161,7 @@ func (entry *Entry) Fatal(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.log(FatalLevel, fmt.Sprint(args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panic(args ...interface{}) {
|
||||
@@ -198,7 +209,7 @@ func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(fmt.Sprintf(format, args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||
@@ -245,7 +256,7 @@ func (entry *Entry) Fatalln(args ...interface{}) {
|
||||
if entry.Logger.Level >= FatalLevel {
|
||||
entry.Fatal(entry.sprintlnn(args...))
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (entry *Entry) Panicln(args ...interface{}) {
|
||||
|
||||
+6
-9
@@ -31,18 +31,15 @@ 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) {
|
||||
_, ok := data["time"]
|
||||
if ok {
|
||||
data["fields.time"] = data["time"]
|
||||
if t, ok := data["time"]; ok {
|
||||
data["fields.time"] = t
|
||||
}
|
||||
|
||||
_, ok = data["msg"]
|
||||
if ok {
|
||||
data["fields.msg"] = data["msg"]
|
||||
if m, ok := data["msg"]; ok {
|
||||
data["fields.msg"] = m
|
||||
}
|
||||
|
||||
_, ok = data["level"]
|
||||
if ok {
|
||||
data["fields.level"] = data["level"]
|
||||
if l, ok := data["level"]; ok {
|
||||
data["fields.level"] = l
|
||||
}
|
||||
}
|
||||
|
||||
+36
-3
@@ -5,9 +5,40 @@ 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) {
|
||||
@@ -29,9 +60,11 @@ func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
timestampFormat = DefaultTimestampFormat
|
||||
}
|
||||
|
||||
data["time"] = entry.Time.Format(timestampFormat)
|
||||
data["msg"] = entry.Message
|
||||
data["level"] = entry.Level.String()
|
||||
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()
|
||||
|
||||
serialized, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
|
||||
+129
-33
@@ -26,8 +26,31 @@ 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.
|
||||
mu sync.Mutex
|
||||
// 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
|
||||
}
|
||||
|
||||
// Creates a new logger. Configuration should be set by changing `Formatter`,
|
||||
@@ -51,162 +74,235 @@ func New() *Logger {
|
||||
}
|
||||
}
|
||||
|
||||
// Adds a field to the log entry, note that you it doesn't log until you call
|
||||
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
|
||||
// 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 {
|
||||
return NewEntry(logger).WithField(key, value)
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.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 {
|
||||
return NewEntry(logger).WithFields(fields)
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.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 {
|
||||
return NewEntry(logger).WithError(err)
|
||||
entry := logger.newEntry()
|
||||
defer logger.releaseEntry(entry)
|
||||
return entry.WithError(err)
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
NewEntry(logger).Debugf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Debugf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
NewEntry(logger).Infof(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Infof(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||
NewEntry(logger).Printf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Printf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
NewEntry(logger).Errorf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Errorf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
NewEntry(logger).Fatalf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
NewEntry(logger).Panicf(format, args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Panicf(format, args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Debug(args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
NewEntry(logger).Debug(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Debug(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Info(args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
NewEntry(logger).Info(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Print(args ...interface{}) {
|
||||
NewEntry(logger).Info(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Info(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warn(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warn(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warning(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warn(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warn(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Error(args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
NewEntry(logger).Error(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Error(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatal(args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
NewEntry(logger).Fatal(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Fatal(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panic(args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
NewEntry(logger).Panic(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Panic(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Debugln(args ...interface{}) {
|
||||
if logger.Level >= DebugLevel {
|
||||
NewEntry(logger).Debugln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Debugln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Infoln(args ...interface{}) {
|
||||
if logger.Level >= InfoLevel {
|
||||
NewEntry(logger).Infoln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Infoln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Println(args ...interface{}) {
|
||||
NewEntry(logger).Println(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Println(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
|
||||
func (logger *Logger) Warnln(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Warningln(args ...interface{}) {
|
||||
if logger.Level >= WarnLevel {
|
||||
NewEntry(logger).Warnln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Warnln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Errorln(args ...interface{}) {
|
||||
if logger.Level >= ErrorLevel {
|
||||
NewEntry(logger).Errorln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Errorln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||
if logger.Level >= FatalLevel {
|
||||
NewEntry(logger).Fatalln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Fatalln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
os.Exit(1)
|
||||
Exit(1)
|
||||
}
|
||||
|
||||
func (logger *Logger) Panicln(args ...interface{}) {
|
||||
if logger.Level >= PanicLevel {
|
||||
NewEntry(logger).Panicln(args...)
|
||||
entry := logger.newEntry()
|
||||
entry.Panicln(args...)
|
||||
logger.releaseEntry(entry)
|
||||
}
|
||||
}
|
||||
|
||||
//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()
|
||||
}
|
||||
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
// +build appengine
|
||||
|
||||
package logrus
|
||||
|
||||
// IsTerminal returns true if stderr's file descriptor is a terminal.
|
||||
func IsTerminal() bool {
|
||||
return true
|
||||
}
|
||||
+1
@@ -1,4 +1,5 @@
|
||||
// +build darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
|
||||
+2
@@ -3,6 +3,8 @@
|
||||
// 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"
|
||||
|
||||
+1
@@ -4,6 +4,7 @@
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build linux darwin freebsd openbsd netbsd dragonfly
|
||||
// +build !appengine
|
||||
|
||||
package logrus
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// +build solaris
|
||||
// +build solaris,!appengine
|
||||
|
||||
package logrus
|
||||
|
||||
|
||||
+1
-1
@@ -3,7 +3,7 @@
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
// +build windows
|
||||
// +build windows,!appengine
|
||||
|
||||
package logrus
|
||||
|
||||
|
||||
+22
-17
@@ -28,10 +28,6 @@ func init() {
|
||||
isTerminal = IsTerminal()
|
||||
}
|
||||
|
||||
func miniTS() int {
|
||||
return int(time.Since(baseTimestamp) / time.Second)
|
||||
}
|
||||
|
||||
type TextFormatter struct {
|
||||
// Set to true to bypass checking for a TTY before outputting colors.
|
||||
ForceColors bool
|
||||
@@ -57,7 +53,8 @@ type TextFormatter struct {
|
||||
}
|
||||
|
||||
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
var keys []string = make([]string, 0, len(entry.Data))
|
||||
var b *bytes.Buffer
|
||||
keys := make([]string, 0, len(entry.Data))
|
||||
for k := range entry.Data {
|
||||
keys = append(keys, k)
|
||||
}
|
||||
@@ -65,8 +62,11 @@ func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||
if !f.DisableSorting {
|
||||
sort.Strings(keys)
|
||||
}
|
||||
|
||||
b := &bytes.Buffer{}
|
||||
if entry.Buffer != nil {
|
||||
b = entry.Buffer
|
||||
} else {
|
||||
b = &bytes.Buffer{}
|
||||
}
|
||||
|
||||
prefixFieldClashes(entry.Data)
|
||||
|
||||
@@ -111,14 +111,17 @@ func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []strin
|
||||
|
||||
levelText := strings.ToUpper(entry.Level.String())[0:4]
|
||||
|
||||
if !f.FullTimestamp {
|
||||
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d] %-44s ", levelColor, levelText, miniTS(), entry.Message)
|
||||
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)
|
||||
} 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=%+v", levelColor, k, v)
|
||||
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
||||
f.appendValue(b, v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -128,34 +131,36 @@ func needsQuoting(text string) bool {
|
||||
(ch >= 'A' && ch <= 'Z') ||
|
||||
(ch >= '0' && ch <= '9') ||
|
||||
ch == '-' || ch == '.') {
|
||||
return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
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 needsQuoting(value) {
|
||||
if !needsQuoting(value) {
|
||||
b.WriteString(value)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%q", value)
|
||||
}
|
||||
case error:
|
||||
errmsg := value.Error()
|
||||
if needsQuoting(errmsg) {
|
||||
if !needsQuoting(errmsg) {
|
||||
b.WriteString(errmsg)
|
||||
} else {
|
||||
fmt.Fprintf(b, "%q", value)
|
||||
fmt.Fprintf(b, "%q", errmsg)
|
||||
}
|
||||
default:
|
||||
fmt.Fprint(b, value)
|
||||
}
|
||||
|
||||
b.WriteByte(' ')
|
||||
}
|
||||
|
||||
+25
-3
@@ -7,18 +7,40 @@ import (
|
||||
)
|
||||
|
||||
func (logger *Logger) Writer() *io.PipeWriter {
|
||||
return logger.WriterLevel(InfoLevel)
|
||||
}
|
||||
|
||||
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
||||
reader, writer := io.Pipe()
|
||||
|
||||
go logger.writerScanner(reader)
|
||||
var printFunc func(args ...interface{})
|
||||
switch level {
|
||||
case DebugLevel:
|
||||
printFunc = logger.Debug
|
||||
case InfoLevel:
|
||||
printFunc = logger.Info
|
||||
case WarnLevel:
|
||||
printFunc = logger.Warn
|
||||
case ErrorLevel:
|
||||
printFunc = logger.Error
|
||||
case FatalLevel:
|
||||
printFunc = logger.Fatal
|
||||
case PanicLevel:
|
||||
printFunc = logger.Panic
|
||||
default:
|
||||
printFunc = logger.Print
|
||||
}
|
||||
|
||||
go logger.writerScanner(reader, printFunc)
|
||||
runtime.SetFinalizer(writer, writerFinalizer)
|
||||
|
||||
return writer
|
||||
}
|
||||
|
||||
func (logger *Logger) writerScanner(reader *io.PipeReader) {
|
||||
func (logger *Logger) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||
scanner := bufio.NewScanner(reader)
|
||||
for scanner.Scan() {
|
||||
logger.Print(scanner.Text())
|
||||
printFunc(scanner.Text())
|
||||
}
|
||||
if err := scanner.Err(); err != nil {
|
||||
logger.Errorf("Error while reading from Writer: %s", err)
|
||||
|
||||
+17
-7
@@ -431,7 +431,7 @@ func strctVal(s interface{}) reflect.Value {
|
||||
v := reflect.ValueOf(s)
|
||||
|
||||
// if pointer get the underlying element≤
|
||||
if v.Kind() == reflect.Ptr {
|
||||
for v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
}
|
||||
|
||||
@@ -530,15 +530,22 @@ func (s *Struct) nested(val reflect.Value) interface{} {
|
||||
finalVal = m
|
||||
}
|
||||
case reflect.Map:
|
||||
v := val.Type().Elem()
|
||||
if v.Kind() == reflect.Ptr {
|
||||
v = v.Elem()
|
||||
// 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()
|
||||
}
|
||||
}
|
||||
|
||||
// only iterate over struct types, ie: map[string]StructType,
|
||||
// map[string][]StructType,
|
||||
if v.Kind() == reflect.Struct ||
|
||||
(v.Kind() == reflect.Slice && v.Elem().Kind() == reflect.Struct) {
|
||||
if mapElem.Kind() == reflect.Struct ||
|
||||
(mapElem.Kind() == reflect.Slice &&
|
||||
mapElem.Elem().Kind() == reflect.Struct) {
|
||||
m := make(map[string]interface{}, val.Len())
|
||||
for _, k := range val.MapKeys() {
|
||||
m[k.String()] = s.nested(val.MapIndex(k))
|
||||
@@ -558,7 +565,10 @@ 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.
|
||||
if val.Type().Elem().Kind() != reflect.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) {
|
||||
finalVal = val.Interface()
|
||||
break
|
||||
}
|
||||
|
||||
+9
-10
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 Frank Schroeder. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@@ -158,16 +158,16 @@ func dec(p *Properties, key string, def *string, opts map[string]string, v refle
|
||||
// keydef returns the property key and the default value based on the
|
||||
// name of the struct field and the options in the tag.
|
||||
keydef := func(f reflect.StructField) (string, *string, map[string]string) {
|
||||
key, opts := parseTag(f.Tag.Get("properties"))
|
||||
_key, _opts := parseTag(f.Tag.Get("properties"))
|
||||
|
||||
var def *string
|
||||
if d, ok := opts["default"]; ok {
|
||||
def = &d
|
||||
var _def *string
|
||||
if d, ok := _opts["default"]; ok {
|
||||
_def = &d
|
||||
}
|
||||
if key != "" {
|
||||
return key, def, opts
|
||||
if _key != "" {
|
||||
return _key, _def, _opts
|
||||
}
|
||||
return f.Name, def, opts
|
||||
return f.Name, _def, _opts
|
||||
}
|
||||
|
||||
switch {
|
||||
@@ -223,7 +223,7 @@ func dec(p *Properties, key string, def *string, opts map[string]string, v refle
|
||||
case isMap(t):
|
||||
valT := t.Elem()
|
||||
m := reflect.MakeMap(t)
|
||||
for postfix, _ := range p.FilterStripPrefix(key + ".").m {
|
||||
for postfix := range p.FilterStripPrefix(key + ".").m {
|
||||
pp := strings.SplitN(postfix, ".", 2)
|
||||
mk, mv := pp[0], reflect.New(valT)
|
||||
if err := dec(p, key+"."+mk, nil, nil, mv); err != nil {
|
||||
@@ -274,7 +274,6 @@ func isArray(t reflect.Type) bool { return t.Kind() == reflect.Array || t.Kin
|
||||
func isBool(t reflect.Type) bool { return t.Kind() == reflect.Bool }
|
||||
func isDuration(t reflect.Type) bool { return t == reflect.TypeOf(time.Second) }
|
||||
func isMap(t reflect.Type) bool { return t.Kind() == reflect.Map }
|
||||
func isNumeric(t reflect.Type) bool { return isInt(t) || isUint(t) || isFloat(t) }
|
||||
func isPtr(t reflect.Type) bool { return t.Kind() == reflect.Ptr }
|
||||
func isString(t reflect.Type) bool { return t.Kind() == reflect.String }
|
||||
func isStruct(t reflect.Type) bool { return t.Kind() == reflect.Struct }
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 Frank Schroeder. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 Frank Schroeder. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
|
||||
+10
-11
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 Frank Schroeder. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
//
|
||||
@@ -72,7 +72,7 @@ type lexer struct {
|
||||
|
||||
// next returns the next rune in the input.
|
||||
func (l *lexer) next() rune {
|
||||
if int(l.pos) >= len(l.input) {
|
||||
if l.pos >= len(l.input) {
|
||||
l.width = 0
|
||||
return eof
|
||||
}
|
||||
@@ -96,8 +96,8 @@ func (l *lexer) backup() {
|
||||
|
||||
// emit passes an item back to the client.
|
||||
func (l *lexer) emit(t itemType) {
|
||||
item := item{t, l.start, string(l.runes)}
|
||||
l.items <- item
|
||||
i := item{t, l.start, string(l.runes)}
|
||||
l.items <- i
|
||||
l.start = l.pos
|
||||
l.runes = l.runes[:0]
|
||||
}
|
||||
@@ -114,7 +114,7 @@ func (l *lexer) appendRune(r rune) {
|
||||
|
||||
// accept consumes the next rune if it's from the valid set.
|
||||
func (l *lexer) accept(valid string) bool {
|
||||
if strings.IndexRune(valid, l.next()) >= 0 {
|
||||
if strings.ContainsRune(valid, l.next()) {
|
||||
return true
|
||||
}
|
||||
l.backup()
|
||||
@@ -123,7 +123,7 @@ func (l *lexer) accept(valid string) bool {
|
||||
|
||||
// acceptRun consumes a run of runes from the valid set.
|
||||
func (l *lexer) acceptRun(valid string) {
|
||||
for strings.IndexRune(valid, l.next()) >= 0 {
|
||||
for strings.ContainsRune(valid, l.next()) {
|
||||
}
|
||||
l.backup()
|
||||
}
|
||||
@@ -156,9 +156,9 @@ func (l *lexer) errorf(format string, args ...interface{}) stateFn {
|
||||
|
||||
// nextItem returns the next item from the input.
|
||||
func (l *lexer) nextItem() item {
|
||||
item := <-l.items
|
||||
l.lastPos = item.pos
|
||||
return item
|
||||
i := <-l.items
|
||||
l.lastPos = i.pos
|
||||
return i
|
||||
}
|
||||
|
||||
// lex creates a new scanner for the input string.
|
||||
@@ -279,8 +279,7 @@ func lexValue(l *lexer) stateFn {
|
||||
for {
|
||||
switch r := l.next(); {
|
||||
case isEscape(r):
|
||||
r := l.peek()
|
||||
if isEOL(r) {
|
||||
if isEOL(l.peek()) {
|
||||
l.next()
|
||||
l.acceptRun(whitespace)
|
||||
} else {
|
||||
|
||||
+5
-3
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 Frank Schroeder. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@@ -98,7 +98,7 @@ func MustLoadURL(url string) *Properties {
|
||||
return must(LoadURL(url))
|
||||
}
|
||||
|
||||
// MustLoadFiles reads the content of multiple URLs in the given order into a
|
||||
// MustLoadURLs reads the content of multiple URLs in the given order into a
|
||||
// Properties struct and panics on error. If 'ignoreMissing' is true then a 404
|
||||
// status code will not be reported as error.
|
||||
func MustLoadURLs(urls []string, ignoreMissing bool) *Properties {
|
||||
@@ -172,10 +172,12 @@ func loadURL(url string, ignoreMissing bool) (*Properties, error) {
|
||||
return nil, fmt.Errorf("properties: %s returned %d", url, resp.StatusCode)
|
||||
}
|
||||
body, err := ioutil.ReadAll(resp.Body)
|
||||
resp.Body.Close()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
|
||||
}
|
||||
if err = resp.Body.Close(); err != nil {
|
||||
return nil, fmt.Errorf("properties: %s error reading response. %s", url, err)
|
||||
}
|
||||
|
||||
ct := resp.Header.Get("Content-Type")
|
||||
var enc Encoding
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 Frank Schroeder. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
|
||||
+11
-4
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 Frank Schroeder. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
@@ -28,8 +28,10 @@ type ErrorHandlerFunc func(error)
|
||||
// functions. The default is LogFatalHandler.
|
||||
var ErrorHandler ErrorHandlerFunc = LogFatalHandler
|
||||
|
||||
// LogHandlerFunc defines the function prototype for logging errors.
|
||||
type LogHandlerFunc func(fmt string, args ...interface{})
|
||||
|
||||
// LogPrintf defines a log handler which uses log.Printf.
|
||||
var LogPrintf LogHandlerFunc = log.Printf
|
||||
|
||||
// LogFatalHandler handles the error by logging a fatal error and exiting.
|
||||
@@ -444,6 +446,8 @@ func (p *Properties) FilterRegexp(re *regexp.Regexp) *Properties {
|
||||
pp := NewProperties()
|
||||
for _, k := range p.k {
|
||||
if re.MatchString(k) {
|
||||
// TODO(fs): we are ignoring the error which flags a circular reference.
|
||||
// TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed)
|
||||
pp.Set(k, p.m[k])
|
||||
}
|
||||
}
|
||||
@@ -456,6 +460,8 @@ func (p *Properties) FilterPrefix(prefix string) *Properties {
|
||||
pp := NewProperties()
|
||||
for _, k := range p.k {
|
||||
if strings.HasPrefix(k, prefix) {
|
||||
// TODO(fs): we are ignoring the error which flags a circular reference.
|
||||
// TODO(fs): since we are just copying a subset of keys this cannot happen (fingers crossed)
|
||||
pp.Set(k, p.m[k])
|
||||
}
|
||||
}
|
||||
@@ -469,6 +475,9 @@ func (p *Properties) FilterStripPrefix(prefix string) *Properties {
|
||||
n := len(prefix)
|
||||
for _, k := range p.k {
|
||||
if len(k) > len(prefix) && strings.HasPrefix(k, prefix) {
|
||||
// TODO(fs): we are ignoring the error which flags a circular reference.
|
||||
// TODO(fs): since we are modifying keys I am not entirely sure whether we can create a circular reference
|
||||
// TODO(fs): this function should probably return an error but the signature is fixed
|
||||
pp.Set(k[n:], p.m[k])
|
||||
}
|
||||
}
|
||||
@@ -483,9 +492,7 @@ func (p *Properties) Len() int {
|
||||
// Keys returns all keys in the same order as in the input.
|
||||
func (p *Properties) Keys() []string {
|
||||
keys := make([]string, len(p.k))
|
||||
for i, k := range p.k {
|
||||
keys[i] = k
|
||||
}
|
||||
copy(keys, p.k)
|
||||
return keys
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
// Copyright 2016 Frank Schroeder. All rights reserved.
|
||||
// Copyright 2017 Frank Schroeder. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
|
||||
+18
-3
@@ -202,7 +202,7 @@ func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error
|
||||
d.config.DecodeHook,
|
||||
dataVal.Type(), val.Type(), data)
|
||||
if err != nil {
|
||||
return err
|
||||
return fmt.Errorf("error decoding '%s': %s", name, err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,6 +229,8 @@ func (d *Decoder) decode(name string, data interface{}, val reflect.Value) error
|
||||
err = d.decodePtr(name, data, val)
|
||||
case reflect.Slice:
|
||||
err = d.decodeSlice(name, data, val)
|
||||
case reflect.Func:
|
||||
err = d.decodeFunc(name, data, val)
|
||||
default:
|
||||
// If we reached this point then we weren't able to decode it
|
||||
return fmt.Errorf("%s: unsupported type: %s", name, dataKind)
|
||||
@@ -547,7 +549,7 @@ func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) er
|
||||
valType := val.Type()
|
||||
valElemType := valType.Elem()
|
||||
|
||||
realVal:=val
|
||||
realVal := val
|
||||
if realVal.IsNil() || d.config.ZeroFields {
|
||||
realVal = reflect.New(valElemType)
|
||||
}
|
||||
@@ -560,6 +562,19 @@ func (d *Decoder) decodePtr(name string, data interface{}, val reflect.Value) er
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeFunc(name string, data interface{}, val reflect.Value) error {
|
||||
// Create an element of the concrete (non pointer) type and decode
|
||||
// into that. Then set the value of the pointer to this type.
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
if val.Type() != dataVal.Type() {
|
||||
return fmt.Errorf(
|
||||
"'%s' expected type '%s', got unconvertible type '%s'",
|
||||
name, val.Type(), dataVal.Type())
|
||||
}
|
||||
val.Set(dataVal)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value) error {
|
||||
dataVal := reflect.Indirect(reflect.ValueOf(data))
|
||||
dataValKind := dataVal.Kind()
|
||||
@@ -567,7 +582,7 @@ func (d *Decoder) decodeSlice(name string, data interface{}, val reflect.Value)
|
||||
valElemType := valType.Elem()
|
||||
sliceType := reflect.SliceOf(valElemType)
|
||||
|
||||
valSlice:=val
|
||||
valSlice := val
|
||||
if valSlice.IsNil() || d.config.ZeroFields {
|
||||
|
||||
// Check input type
|
||||
|
||||
+26
-6
@@ -87,7 +87,7 @@ func toTomlValue(item interface{}, indent int) string {
|
||||
case nil:
|
||||
return ""
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported value type %T: %v", value, value))
|
||||
panic(fmt.Errorf("unsupported value type %T: %v", value, value))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -154,6 +154,23 @@ func (t *TomlTree) toToml(indent, keyspace string) string {
|
||||
return strings.Join(resultChunks, "")
|
||||
}
|
||||
|
||||
// Same as ToToml(), but does not panic and returns an error
|
||||
func (t *TomlTree) toTomlSafe(indent, keyspace string) (result string, err error) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
result = ""
|
||||
switch x := r.(type) {
|
||||
case error:
|
||||
err = x
|
||||
default:
|
||||
err = fmt.Errorf("unknown panic: %s", r)
|
||||
}
|
||||
}
|
||||
}()
|
||||
result = t.toToml(indent, keyspace)
|
||||
return
|
||||
}
|
||||
|
||||
func convertMapStringString(in map[string]string) map[string]interface{} {
|
||||
result := make(map[string]interface{}, len(in))
|
||||
for k, v := range in {
|
||||
@@ -170,15 +187,18 @@ func convertMapInterfaceInterface(in map[interface{}]interface{}) map[string]int
|
||||
return result
|
||||
}
|
||||
|
||||
// ToString is an alias for String
|
||||
func (t *TomlTree) ToString() string {
|
||||
return t.String()
|
||||
// ToString generates a human-readable representation of the current tree.
|
||||
// Output spans multiple lines, and is suitable for ingest by a TOML parser.
|
||||
// If the conversion cannot be performed, ToString returns a non-nil error.
|
||||
func (t *TomlTree) ToString() (string, error) {
|
||||
return t.toTomlSafe("", "")
|
||||
}
|
||||
|
||||
// String generates a human-readable representation of the current tree.
|
||||
// Output spans multiple lines, and is suitable for ingest by a TOML parser
|
||||
// Alias of ToString.
|
||||
func (t *TomlTree) String() string {
|
||||
return t.toToml("", "")
|
||||
result, _ := t.ToString()
|
||||
return result
|
||||
}
|
||||
|
||||
// ToMap recursively generates a representation of the current tree using map[string]interface{}.
|
||||
|
||||
+3
-5
@@ -11,7 +11,7 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extentions"
|
||||
BashCompFilenameExt = "cobra_annotation_bash_completion_filename_extensions"
|
||||
BashCompCustom = "cobra_annotation_bash_completion_custom"
|
||||
BashCompOneRequiredFlag = "cobra_annotation_bash_completion_one_required_flag"
|
||||
BashCompSubdirsInDir = "cobra_annotation_bash_completion_subdirs_in_dir"
|
||||
@@ -401,10 +401,8 @@ func writeLocalNonPersistentFlag(flag *pflag.Flag, w io.Writer) error {
|
||||
format += "="
|
||||
}
|
||||
format += "\")\n"
|
||||
if _, err := fmt.Fprintf(w, format, name); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
_, err := fmt.Fprintf(w, format, name)
|
||||
return err
|
||||
}
|
||||
|
||||
func writeFlags(cmd *Command, w io.Writer) error {
|
||||
|
||||
+17
-19
@@ -37,38 +37,36 @@ var templateFuncs = template.FuncMap{
|
||||
|
||||
var initializers []func()
|
||||
|
||||
// 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()) {
|
||||
for _, x := range y {
|
||||
initializers = append(initializers, x)
|
||||
}
|
||||
initializers = append(initializers, y...)
|
||||
}
|
||||
|
||||
//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)
|
||||
@@ -96,7 +94,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)
|
||||
@@ -116,7 +114,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
|
||||
@@ -124,7 +122,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)
|
||||
@@ -138,7 +136,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)
|
||||
|
||||
+93
-58
@@ -109,10 +109,11 @@ 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
|
||||
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
|
||||
helpTemplate string // Can be defined by Application
|
||||
helpFunc func(*Command, []string) // Help can be defined by application
|
||||
helpCommand *Command // The help command
|
||||
@@ -140,16 +141,22 @@ func (c *Command) SetOutput(output io.Writer) {
|
||||
c.output = &output
|
||||
}
|
||||
|
||||
// Usage can be defined by application
|
||||
// Usage can be defined by application.
|
||||
func (c *Command) SetUsageFunc(f func(*Command) error) {
|
||||
c.usageFunc = f
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// Can be defined by Application
|
||||
func (c *Command) SetHelpFunc(f func(*Command, []string)) {
|
||||
c.helpFunc = f
|
||||
@@ -159,7 +166,7 @@ func (c *Command) SetHelpCommand(cmd *Command) {
|
||||
c.helpCommand = cmd
|
||||
}
|
||||
|
||||
// Can be defined by Application
|
||||
// Can be defined by Application.
|
||||
func (c *Command) SetHelpTemplate(s string) {
|
||||
c.helpTemplate = s
|
||||
}
|
||||
@@ -195,7 +202,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
|
||||
@@ -214,22 +221,18 @@ func (c *Command) UsageFunc() (f func(*Command) error) {
|
||||
}
|
||||
}
|
||||
|
||||
// Output the usage for the command
|
||||
// Used when a user provides invalid input
|
||||
// Can be defined by user by overriding UsageFunc
|
||||
// Usage puts out 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) {
|
||||
cmd := c
|
||||
for cmd != nil {
|
||||
if cmd.helpFunc != nil {
|
||||
return cmd.helpFunc
|
||||
}
|
||||
cmd = cmd.parent
|
||||
if helpFunc := c.checkHelpFunc(); helpFunc != nil {
|
||||
return helpFunc
|
||||
}
|
||||
return func(*Command, []string) {
|
||||
c.mergePersistentFlags()
|
||||
@@ -240,9 +243,23 @@ func (c *Command) HelpFunc() func(*Command, []string) {
|
||||
}
|
||||
}
|
||||
|
||||
// Output the help for the command
|
||||
// Used when a user calls help [command]
|
||||
// Can be defined by user by overriding HelpFunc
|
||||
// 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.
|
||||
func (c *Command) Help() error {
|
||||
c.HelpFunc()(c, []string{})
|
||||
return nil
|
||||
@@ -257,6 +274,22 @@ 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
|
||||
|
||||
func (c *Command) UsagePadding() int {
|
||||
@@ -333,7 +366,7 @@ 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
|
||||
@@ -553,7 +586,7 @@ func (c *Command) execute(a []string) (err error) {
|
||||
|
||||
err = c.ParseFlags(a)
|
||||
if err != nil {
|
||||
return err
|
||||
return c.FlagErrorFunc()(c, err)
|
||||
}
|
||||
// If help is called, regardless of other flags, return we want help
|
||||
// Also say we need help if the command isn't runnable.
|
||||
@@ -712,6 +745,7 @@ 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())
|
||||
}
|
||||
@@ -745,13 +779,13 @@ func (c *Command) initHelpCmd() {
|
||||
c.AddCommand(c.helpCommand)
|
||||
}
|
||||
|
||||
// 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) }
|
||||
@@ -831,18 +865,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)
|
||||
@@ -859,7 +893,7 @@ func (c *Command) CommandPath() string {
|
||||
return str
|
||||
}
|
||||
|
||||
//The full usage for a given command (including parents)
|
||||
// UseLine puts out the full usage for a given command (including parents).
|
||||
func (c *Command) UseLine() string {
|
||||
str := ""
|
||||
if c.HasParent() {
|
||||
@@ -869,7 +903,7 @@ func (c *Command) UseLine() string {
|
||||
}
|
||||
|
||||
// For use in determining which flags have been assigned to which commands
|
||||
// and which persist
|
||||
// and which persist.
|
||||
func (c *Command) DebugFlags() {
|
||||
c.Println("DebugFlags called on", c.Name())
|
||||
var debugflags func(*Command)
|
||||
@@ -944,18 +978,18 @@ 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
|
||||
@@ -974,7 +1008,7 @@ func (c *Command) IsAvailableCommand() 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
|
||||
// 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
|
||||
@@ -993,9 +1027,9 @@ func (c *Command) IsHelpCommand() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// HasHelpSubCommands determines if a command has any avilable 'help' sub commands
|
||||
// HasHelpSubCommands determines if a command has any available '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
|
||||
@@ -1010,7 +1044,7 @@ 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)
|
||||
@@ -1026,17 +1060,18 @@ func (c *Command) HasAvailableSubCommands() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Determine if the command is a child command
|
||||
// HasParent determines 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
|
||||
}
|
||||
|
||||
// Get the complete FlagSet that applies to this command (local and persistent declared here and by all parents)
|
||||
// Flage returns 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)
|
||||
@@ -1048,7 +1083,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()
|
||||
|
||||
@@ -1061,7 +1096,7 @@ func (c *Command) LocalNonPersistentFlags() *flag.FlagSet {
|
||||
return out
|
||||
}
|
||||
|
||||
// Get the local FlagSet specifically set in the current command
|
||||
// LocalFlags returns the local FlagSet specifically set in the current command.
|
||||
func (c *Command) LocalFlags() *flag.FlagSet {
|
||||
c.mergePersistentFlags()
|
||||
|
||||
@@ -1079,7 +1114,7 @@ func (c *Command) LocalFlags() *flag.FlagSet {
|
||||
return local
|
||||
}
|
||||
|
||||
// All Flags which were inherited from parents commands
|
||||
// InheritedFlags returns all flags which were inherited from parents commands.
|
||||
func (c *Command) InheritedFlags() *flag.FlagSet {
|
||||
c.mergePersistentFlags()
|
||||
|
||||
@@ -1108,12 +1143,12 @@ func (c *Command) InheritedFlags() *flag.FlagSet {
|
||||
return inherited
|
||||
}
|
||||
|
||||
// All Flags which were not inherited from parent commands
|
||||
// NonInheritedFlags returns all flags which were not inherited from parent commands.
|
||||
func (c *Command) NonInheritedFlags() *flag.FlagSet {
|
||||
return c.LocalFlags()
|
||||
}
|
||||
|
||||
// Get the Persistent FlagSet specifically set in the current command
|
||||
// PersistentFlags returns 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)
|
||||
@@ -1125,7 +1160,7 @@ func (c *Command) PersistentFlags() *flag.FlagSet {
|
||||
return c.pflags
|
||||
}
|
||||
|
||||
// For use in testing
|
||||
// ResetFlags is used in testing.
|
||||
func (c *Command) ResetFlags() {
|
||||
c.flagErrorBuf = new(bytes.Buffer)
|
||||
c.flagErrorBuf.Reset()
|
||||
@@ -1135,50 +1170,50 @@ func (c *Command) ResetFlags() {
|
||||
c.pflags.SetOutput(c.flagErrorBuf)
|
||||
}
|
||||
|
||||
// Does the command contain 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()
|
||||
}
|
||||
|
||||
// Does the command contain persistent flags
|
||||
// Does the command contain persistent flags.
|
||||
func (c *Command) HasPersistentFlags() bool {
|
||||
return c.PersistentFlags().HasFlags()
|
||||
}
|
||||
|
||||
// Does the command has flags specifically declared locally
|
||||
// Does the command has flags specifically declared locally.
|
||||
func (c *Command) HasLocalFlags() bool {
|
||||
return c.LocalFlags().HasFlags()
|
||||
}
|
||||
|
||||
// Does the command have 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()
|
||||
}
|
||||
|
||||
// Does the command contain any flags (local plus persistent from the entire
|
||||
// structure) which are not hidden or deprecated
|
||||
// structure) which are not hidden or deprecated.
|
||||
func (c *Command) HasAvailableFlags() bool {
|
||||
return c.Flags().HasAvailableFlags()
|
||||
}
|
||||
|
||||
// Does the command contain 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()
|
||||
}
|
||||
|
||||
// Does the command has flags specifically declared locally which are not hidden
|
||||
// or deprecated
|
||||
// or deprecated.
|
||||
func (c *Command) HasAvailableLocalFlags() bool {
|
||||
return c.LocalFlags().HasAvailableFlags()
|
||||
}
|
||||
|
||||
// Does the command have flags inherited from its parent command which are
|
||||
// not hidden or deprecated
|
||||
// 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)
|
||||
|
||||
@@ -1189,7 +1224,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)
|
||||
@@ -1201,7 +1236,7 @@ func (c *Command) persistentFlag(name string) (flag *flag.Flag) {
|
||||
return
|
||||
}
|
||||
|
||||
// ParseFlags parses persistent flag tree & local flags
|
||||
// ParseFlags parses persistent flag tree and local flags.
|
||||
func (c *Command) ParseFlags(args []string) (err error) {
|
||||
if c.DisableFlagParsing {
|
||||
return nil
|
||||
@@ -1211,7 +1246,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
|
||||
}
|
||||
|
||||
+52
-10
@@ -752,7 +752,7 @@ func containsShorthand(arg, shorthand string) bool {
|
||||
return strings.Contains(arg, shorthand)
|
||||
}
|
||||
|
||||
func (f *FlagSet) parseLongArg(s string, args []string) (a []string, err error) {
|
||||
func (f *FlagSet) parseLongArg(s string, args []string, fn parseFunc) (a []string, err error) {
|
||||
a = args
|
||||
name := s[2:]
|
||||
if len(name) == 0 || name[0] == '-' || name[0] == '=' {
|
||||
@@ -786,11 +786,11 @@ func (f *FlagSet) parseLongArg(s string, args []string) (a []string, err error)
|
||||
err = f.failf("flag needs an argument: %s", s)
|
||||
return
|
||||
}
|
||||
err = f.setFlag(flag, value, s)
|
||||
err = fn(flag, value, s)
|
||||
return
|
||||
}
|
||||
|
||||
func (f *FlagSet) parseSingleShortArg(shorthands string, args []string) (outShorts string, outArgs []string, err error) {
|
||||
func (f *FlagSet) parseSingleShortArg(shorthands string, args []string, fn parseFunc) (outShorts string, outArgs []string, err error) {
|
||||
if strings.HasPrefix(shorthands, "test.") {
|
||||
return
|
||||
}
|
||||
@@ -825,16 +825,16 @@ func (f *FlagSet) parseSingleShortArg(shorthands string, args []string) (outShor
|
||||
err = f.failf("flag needs an argument: %q in -%s", c, shorthands)
|
||||
return
|
||||
}
|
||||
err = f.setFlag(flag, value, shorthands)
|
||||
err = fn(flag, value, shorthands)
|
||||
return
|
||||
}
|
||||
|
||||
func (f *FlagSet) parseShortArg(s string, args []string) (a []string, err error) {
|
||||
func (f *FlagSet) parseShortArg(s string, args []string, fn parseFunc) (a []string, err error) {
|
||||
a = args
|
||||
shorthands := s[1:]
|
||||
|
||||
for len(shorthands) > 0 {
|
||||
shorthands, a, err = f.parseSingleShortArg(shorthands, args)
|
||||
shorthands, a, err = f.parseSingleShortArg(shorthands, args, fn)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
@@ -843,7 +843,7 @@ func (f *FlagSet) parseShortArg(s string, args []string) (a []string, err error)
|
||||
return
|
||||
}
|
||||
|
||||
func (f *FlagSet) parseArgs(args []string) (err error) {
|
||||
func (f *FlagSet) parseArgs(args []string, fn parseFunc) (err error) {
|
||||
for len(args) > 0 {
|
||||
s := args[0]
|
||||
args = args[1:]
|
||||
@@ -863,9 +863,9 @@ func (f *FlagSet) parseArgs(args []string) (err error) {
|
||||
f.args = append(f.args, args...)
|
||||
break
|
||||
}
|
||||
args, err = f.parseLongArg(s, args)
|
||||
args, err = f.parseLongArg(s, args, fn)
|
||||
} else {
|
||||
args, err = f.parseShortArg(s, args)
|
||||
args, err = f.parseShortArg(s, args, fn)
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
@@ -881,7 +881,41 @@ func (f *FlagSet) parseArgs(args []string) (err error) {
|
||||
func (f *FlagSet) Parse(arguments []string) error {
|
||||
f.parsed = true
|
||||
f.args = make([]string, 0, len(arguments))
|
||||
err := f.parseArgs(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)
|
||||
if err != nil {
|
||||
switch f.errorHandling {
|
||||
case ContinueOnError:
|
||||
@@ -907,6 +941,14 @@ 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)
|
||||
|
||||
+40
-8
@@ -1,10 +1,20 @@
|
||||
package gojsonschema
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"bytes"
|
||||
"sync"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
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 {
|
||||
@@ -230,13 +240,35 @@ func newError(err ResultError, context *jsonContext, value interface{}, locale l
|
||||
err.SetDescription(formatErrorDescription(d, details))
|
||||
}
|
||||
|
||||
// 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.
|
||||
// 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.
|
||||
func formatErrorDescription(s string, details ErrorDetails) string {
|
||||
for name, val := range details {
|
||||
s = strings.Replace(s, "%"+strings.ToLower(name)+"%", fmt.Sprintf("%v", val), -1)
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
return s
|
||||
err = tpl.Execute(&descrAsBuffer, details)
|
||||
if err != nil {
|
||||
return err.Error()
|
||||
}
|
||||
|
||||
return descrAsBuffer.String()
|
||||
}
|
||||
|
||||
+21
-4
@@ -5,6 +5,7 @@ import (
|
||||
"net/url"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
@@ -59,6 +60,9 @@ type (
|
||||
|
||||
// UUIDFormatChecker validates a UUID is in the correct format
|
||||
UUIDFormatChecker struct{}
|
||||
|
||||
// RegexFormatChecker validates a regex is in the correct format
|
||||
RegexFormatChecker struct{}
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -73,6 +77,7 @@ var (
|
||||
"ipv6": IPV6FormatChecker{},
|
||||
"uri": URIFormatChecker{},
|
||||
"uuid": UUIDFormatChecker{},
|
||||
"regex": RegexFormatChecker{},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -80,7 +85,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\-]*[a-zA-Z0-9])\.)*([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$`)
|
||||
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]))*$`)
|
||||
|
||||
rxUUID = regexp.MustCompile("^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$")
|
||||
)
|
||||
@@ -132,13 +137,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 && ip.To4() != nil
|
||||
return ip != nil && strings.Contains(input, ".")
|
||||
}
|
||||
|
||||
// Credit: https://github.com/asaskevich/govalidator
|
||||
func (f IPV6FormatChecker) IsFormat(input string) bool {
|
||||
ip := net.ParseIP(input)
|
||||
return ip != nil && ip.To4() == nil
|
||||
return ip != nil && strings.Contains(input, ":")
|
||||
}
|
||||
|
||||
func (f DateTimeFormatChecker) IsFormat(input string) bool {
|
||||
@@ -169,9 +174,21 @@ func (f URIFormatChecker) IsFormat(input string) bool {
|
||||
}
|
||||
|
||||
func (f HostnameFormatChecker) IsFormat(input string) bool {
|
||||
return rxHostname.MatchString(input)
|
||||
return rxHostname.MatchString(input) && len(input) < 256
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
+114
-90
@@ -33,6 +33,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
@@ -40,34 +41,92 @@ import (
|
||||
"github.com/xeipuuv/gojsonreference"
|
||||
)
|
||||
|
||||
var osFS = osFileSystem(os.Open)
|
||||
|
||||
// JSON loader interface
|
||||
|
||||
type JSONLoader interface {
|
||||
jsonSource() interface{}
|
||||
loadJSON() (interface{}, error)
|
||||
loadSchema() (*Schema, error)
|
||||
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)
|
||||
}
|
||||
|
||||
// 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 NewReferenceLoader(source string) *jsonReferenceLoader {
|
||||
return &jsonReferenceLoader{source: source}
|
||||
func (l *jsonReferenceLoader) JsonReference() (gojsonreference.JsonReference, error) {
|
||||
return gojsonreference.NewJsonReference(l.JsonSource().(string))
|
||||
}
|
||||
|
||||
func (l *jsonReferenceLoader) loadJSON() (interface{}, error) {
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
|
||||
var err error
|
||||
|
||||
reference, err := gojsonreference.NewJsonReference(l.jsonSource().(string))
|
||||
reference, err := gojsonreference.NewJsonReference(l.JsonSource().(string))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -79,7 +138,7 @@ func (l *jsonReferenceLoader) loadJSON() (interface{}, error) {
|
||||
|
||||
if reference.HasFileScheme {
|
||||
|
||||
filename := strings.Replace(refToUrl.String(), "file://", "", -1)
|
||||
filename := strings.Replace(refToUrl.GetUrl().Path, "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
|
||||
@@ -87,7 +146,6 @@ func (l *jsonReferenceLoader) loadJSON() (interface{}, error) {
|
||||
filename = filename[1:]
|
||||
}
|
||||
filename = filepath.FromSlash(filename)
|
||||
filename = strings.Replace(filename, "%20", " ", -1)
|
||||
}
|
||||
|
||||
document, err = l.loadFromFile(filename)
|
||||
@@ -108,33 +166,6 @@ 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)
|
||||
@@ -157,8 +188,13 @@ 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.ReadFile(path)
|
||||
bodyBuff, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -173,45 +209,52 @@ 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)))
|
||||
|
||||
}
|
||||
|
||||
func (l *jsonStringLoader) loadSchema() (*Schema, error) {
|
||||
// JSON bytes loader
|
||||
|
||||
var err error
|
||||
type jsonBytesLoader struct {
|
||||
source []byte
|
||||
}
|
||||
|
||||
document, err := l.loadJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (l *jsonBytesLoader) JsonSource() interface{} {
|
||||
return l.source
|
||||
}
|
||||
|
||||
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) JsonReference() (gojsonreference.JsonReference, error) {
|
||||
return gojsonreference.NewJsonReference("#")
|
||||
}
|
||||
|
||||
err = d.parse(document)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
func (l *jsonBytesLoader) LoaderFactory() JSONLoaderFactory {
|
||||
return &DefaultJSONLoaderFactory{}
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
func NewBytesLoader(source []byte) *jsonBytesLoader {
|
||||
return &jsonBytesLoader{source: source}
|
||||
}
|
||||
|
||||
func (l *jsonBytesLoader) LoadJSON() (interface{}, error) {
|
||||
return decodeJsonUsingNumber(bytes.NewReader(l.JsonSource().([]byte)))
|
||||
}
|
||||
|
||||
// JSON Go (types) loader
|
||||
@@ -221,19 +264,27 @@ 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
|
||||
}
|
||||
@@ -242,33 +293,6 @@ func (l *jsonGoLoader) loadJSON() (interface{}, error) {
|
||||
|
||||
}
|
||||
|
||||
func (l *jsonGoLoader) loadSchema() (*Schema, error) {
|
||||
|
||||
var err error
|
||||
|
||||
document, err := l.loadJSON()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
d := Schema{}
|
||||
d.pool = newSchemaPool()
|
||||
d.referencePool = newSchemaReferencePool()
|
||||
d.documentReference, err = gojsonreference.NewJsonReference("#")
|
||||
d.pool.SetStandaloneDocument(document)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = d.parse(document)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &d, nil
|
||||
|
||||
}
|
||||
|
||||
func decodeJsonUsingNumber(r io.Reader) (interface{}, error) {
|
||||
|
||||
var document interface{}
|
||||
|
||||
+44
-39
@@ -37,6 +37,7 @@ type (
|
||||
MissingDependency() string
|
||||
Internal() string
|
||||
Enum() string
|
||||
ArrayNotEnoughItems() string
|
||||
ArrayNoAdditionalItems() string
|
||||
ArrayMinItems() string
|
||||
ArrayMaxItems() string
|
||||
@@ -83,11 +84,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 {
|
||||
@@ -107,157 +108,161 @@ 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%`
|
||||
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 (
|
||||
|
||||
+2
-1
@@ -48,6 +48,7 @@ type (
|
||||
Value() interface{}
|
||||
SetDetails(ErrorDetails)
|
||||
Details() ErrorDetails
|
||||
String() string
|
||||
}
|
||||
|
||||
// ResultErrorFields holds the fields for each ResultError implementation.
|
||||
@@ -126,7 +127,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 {
|
||||
|
||||
+54
-32
@@ -42,7 +42,39 @@ var (
|
||||
)
|
||||
|
||||
func NewSchema(l JSONLoader) (*Schema, error) {
|
||||
return l.loadSchema()
|
||||
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
|
||||
}
|
||||
|
||||
type Schema struct {
|
||||
@@ -116,14 +148,27 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
|
||||
}
|
||||
if k, ok := m[KEY_REF].(string); ok {
|
||||
|
||||
if sch, ok := d.referencePool.Get(currentSchema.ref.String() + k); 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 {
|
||||
|
||||
var err error
|
||||
err = d.parseReference(documentNode, currentSchema, k)
|
||||
err := d.parseReference(documentNode, currentSchema, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -755,30 +800,10 @@ func (d *Schema) parseSchema(documentNode interface{}, currentSchema *subSchema)
|
||||
return nil
|
||||
}
|
||||
|
||||
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()
|
||||
|
||||
func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSchema, reference string) error {
|
||||
var refdDocumentNode interface{}
|
||||
jsonPointer := currentSchema.ref.GetPointer()
|
||||
standaloneDocument := d.pool.GetStandaloneDocument()
|
||||
|
||||
if standaloneDocument != nil {
|
||||
|
||||
@@ -789,8 +814,6 @@ func (d *Schema) parseReference(documentNode interface{}, currentSchema *subSche
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
var err error
|
||||
dsp, err := d.pool.GetDocument(*currentSchema.ref)
|
||||
if err != nil {
|
||||
return err
|
||||
@@ -812,11 +835,10 @@ 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
|
||||
}
|
||||
|
||||
+5
-3
@@ -39,13 +39,15 @@ type schemaPoolDocument struct {
|
||||
type schemaPool struct {
|
||||
schemaPoolDocuments map[string]*schemaPoolDocument
|
||||
standaloneDocument interface{}
|
||||
jsonLoaderFactory JSONLoaderFactory
|
||||
}
|
||||
|
||||
func newSchemaPool() *schemaPool {
|
||||
func newSchemaPool(f JSONLoaderFactory) *schemaPool {
|
||||
|
||||
p := &schemaPool{}
|
||||
p.schemaPoolDocuments = make(map[string]*schemaPoolDocument)
|
||||
p.standaloneDocument = nil
|
||||
p.jsonLoaderFactory = f
|
||||
|
||||
return p
|
||||
}
|
||||
@@ -93,8 +95,8 @@ func (p *schemaPool) GetDocument(reference gojsonreference.JsonReference) (*sche
|
||||
return spd, nil
|
||||
}
|
||||
|
||||
jsonReferenceLoader := NewReferenceLoader(reference.String())
|
||||
document, err := jsonReferenceLoader.loadJSON()
|
||||
jsonReferenceLoader := p.jsonLoaderFactory.New(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+`"`)
|
||||
}
|
||||
|
||||
|
||||
+10
-4
@@ -34,7 +34,12 @@ import (
|
||||
)
|
||||
|
||||
func isKind(what interface{}, kind reflect.Kind) bool {
|
||||
return reflect.ValueOf(what).Kind() == kind
|
||||
target := what
|
||||
if isJsonNumber(what) {
|
||||
// JSON Numbers are strings!
|
||||
target = *mustBeNumber(what)
|
||||
}
|
||||
return reflect.ValueOf(target).Kind() == kind
|
||||
}
|
||||
|
||||
func existsMapKey(m map[string]interface{}, k string) bool {
|
||||
@@ -77,13 +82,14 @@ func checkJsonNumber(what interface{}) (isValidFloat64 bool, isValidInt64 bool,
|
||||
|
||||
jsonNumber := what.(json.Number)
|
||||
|
||||
_, errFloat64 := jsonNumber.Float64()
|
||||
_, errInt64 := jsonNumber.Int64()
|
||||
f64, errFloat64 := jsonNumber.Float64()
|
||||
s64 := strconv.FormatFloat(f64, 'f', -1, 64)
|
||||
_, errInt64 := strconv.ParseInt(s64, 10, 64)
|
||||
|
||||
isValidFloat64 = errFloat64 == nil
|
||||
isValidInt64 = errInt64 == nil
|
||||
|
||||
_, errInt32 := strconv.ParseInt(jsonNumber.String(), 10, 32)
|
||||
_, errInt32 := strconv.ParseInt(s64, 10, 32)
|
||||
isValidInt32 = isValidInt64 && errInt32 == nil
|
||||
|
||||
return
|
||||
|
||||
+23
-20
@@ -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)
|
||||
}
|
||||
|
||||
nbItems := len(value)
|
||||
nbValues := len(value)
|
||||
|
||||
// TODO explain
|
||||
if currentSubSchema.itemsChildrenIsSingleSchema {
|
||||
@@ -425,15 +425,18 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface
|
||||
if currentSubSchema.itemsChildren != nil && len(currentSubSchema.itemsChildren) > 0 {
|
||||
|
||||
nbItems := len(currentSubSchema.itemsChildren)
|
||||
nbValues := len(value)
|
||||
|
||||
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 {
|
||||
// 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.
|
||||
|
||||
switch currentSubSchema.additionalItems.(type) {
|
||||
case bool:
|
||||
if !currentSubSchema.additionalItems.(bool) {
|
||||
@@ -453,7 +456,7 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface
|
||||
|
||||
// minItems & maxItems
|
||||
if currentSubSchema.minItems != nil {
|
||||
if nbItems < int(*currentSubSchema.minItems) {
|
||||
if nbValues < int(*currentSubSchema.minItems) {
|
||||
result.addError(
|
||||
new(ArrayMinItemsError),
|
||||
context,
|
||||
@@ -463,7 +466,7 @@ func (v *subSchema) validateArray(currentSubSchema *subSchema, value []interface
|
||||
}
|
||||
}
|
||||
if currentSubSchema.maxItems != nil {
|
||||
if nbItems > int(*currentSubSchema.maxItems) {
|
||||
if nbValues > int(*currentSubSchema.maxItems) {
|
||||
result.addError(
|
||||
new(ArrayMaxItemsError),
|
||||
context,
|
||||
@@ -776,22 +779,22 @@ func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{
|
||||
if currentSubSchema.exclusiveMaximum {
|
||||
if float64Value >= *currentSubSchema.maximum {
|
||||
result.addError(
|
||||
new(NumberLTEError),
|
||||
new(NumberLTError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{
|
||||
"min": resultErrorFormatNumber(*currentSubSchema.maximum),
|
||||
"max": resultErrorFormatNumber(*currentSubSchema.maximum),
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if float64Value > *currentSubSchema.maximum {
|
||||
result.addError(
|
||||
new(NumberLTError),
|
||||
new(NumberLTEError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{
|
||||
"min": resultErrorFormatNumber(*currentSubSchema.maximum),
|
||||
"max": resultErrorFormatNumber(*currentSubSchema.maximum),
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -803,22 +806,22 @@ func (v *subSchema) validateNumber(currentSubSchema *subSchema, value interface{
|
||||
if currentSubSchema.exclusiveMinimum {
|
||||
if float64Value <= *currentSubSchema.minimum {
|
||||
result.addError(
|
||||
new(NumberGTEError),
|
||||
new(NumberGTError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{
|
||||
"max": resultErrorFormatNumber(*currentSubSchema.minimum),
|
||||
"min": resultErrorFormatNumber(*currentSubSchema.minimum),
|
||||
},
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if float64Value < *currentSubSchema.minimum {
|
||||
result.addError(
|
||||
new(NumberGTError),
|
||||
new(NumberGTEError),
|
||||
context,
|
||||
resultErrorFormatJsonNumber(number),
|
||||
ErrorDetails{
|
||||
"max": resultErrorFormatNumber(*currentSubSchema.minimum),
|
||||
"min": resultErrorFormatNumber(*currentSubSchema.minimum),
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user