Initial commit
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
deconnect.yaml
|
||||||
70
cmd/deconnect/deconnect.go
Normal file
70
cmd/deconnect/deconnect.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"sync"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/application"
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/domains"
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/domains/deconnector"
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/domains/dialer"
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/domains/listener"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
|
||||||
|
app := application.New(ctx)
|
||||||
|
|
||||||
|
app.Logger().Info("Starting deconnect...")
|
||||||
|
|
||||||
|
err := app.InitConfig()
|
||||||
|
if err != nil {
|
||||||
|
app.Logger().Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.InitLogger()
|
||||||
|
|
||||||
|
app.RegisterDomain(domains.DomainNameListener, listener.New(app))
|
||||||
|
app.RegisterDomain(domains.DomainNameDialer, dialer.New(app))
|
||||||
|
app.RegisterDomain(domains.DomainNameDeconnector, deconnector.New(app))
|
||||||
|
|
||||||
|
err = app.ConnectDependencies()
|
||||||
|
if err != nil {
|
||||||
|
app.Logger().Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// CTRL+C handler.
|
||||||
|
interrupt := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(
|
||||||
|
interrupt, syscall.SIGINT, syscall.SIGTERM,
|
||||||
|
)
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
app.RegisterGlobalWaitGroup(&wg)
|
||||||
|
|
||||||
|
err = app.StartDomains()
|
||||||
|
if err != nil {
|
||||||
|
app.Logger().Fatal(err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
app.Logger().Info("Started deconnect")
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
signalThing := <-interrupt
|
||||||
|
app.Logger().WithField("signal", signalThing.String()).
|
||||||
|
Info("Got terminating signal, shutting down...")
|
||||||
|
|
||||||
|
cancel()
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Wait for all domains to finish their cleanup
|
||||||
|
wg.Wait()
|
||||||
|
|
||||||
|
app.Logger().Info("deconnect shutdown complete")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
10
go.mod
Normal file
10
go.mod
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
module source.hodakov.me/hdkv/deconnect
|
||||||
|
|
||||||
|
go 1.26.3
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/goccy/go-yaml v1.19.2
|
||||||
|
github.com/sirupsen/logrus v1.9.4
|
||||||
|
)
|
||||||
|
|
||||||
|
require golang.org/x/sys v0.13.0 // indirect
|
||||||
14
go.sum
Normal file
14
go.sum
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
|
||||||
|
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=
|
||||||
|
github.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
golang.org/x/sys v0.13.0 h1:Af8nKPmuFypiUBjVoU9V20FiaFXOcuZI21p0ycVYYGE=
|
||||||
|
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
129
internal/application/application.go
Normal file
129
internal/application/application.go
Normal file
@@ -0,0 +1,129 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
"strconv"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/configuration"
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/domains"
|
||||||
|
)
|
||||||
|
|
||||||
|
type App struct {
|
||||||
|
ctx context.Context
|
||||||
|
logger *logrus.Entry
|
||||||
|
config *configuration.Config
|
||||||
|
wg *sync.WaitGroup
|
||||||
|
|
||||||
|
domains map[string]domains.Domain
|
||||||
|
domainsMutex sync.RWMutex
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) Config() *configuration.Config {
|
||||||
|
return a.config
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) Context() context.Context {
|
||||||
|
return a.ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) Logger() *logrus.Entry {
|
||||||
|
return a.logger
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(ctx context.Context) *App {
|
||||||
|
var m runtime.MemStats
|
||||||
|
|
||||||
|
runtime.ReadMemStats(&m)
|
||||||
|
|
||||||
|
app := new(App)
|
||||||
|
|
||||||
|
// Initialize standard logger with memory stats and context attached permanently.
|
||||||
|
logger := logrus.StandardLogger()
|
||||||
|
|
||||||
|
logger.SetFormatter(&logrus.TextFormatter{
|
||||||
|
FullTimestamp: true,
|
||||||
|
})
|
||||||
|
|
||||||
|
app.logger = logger.WithContext(ctx).WithFields(logrus.Fields{
|
||||||
|
"memalloc": fmt.Sprintf("%dMB", m.Alloc/1024/1024),
|
||||||
|
"memsys": fmt.Sprintf("%dMB", m.Sys/1024/1024),
|
||||||
|
"numgc": strconv.FormatUint(uint64(m.NumGC), 10),
|
||||||
|
})
|
||||||
|
|
||||||
|
app.ctx = ctx
|
||||||
|
|
||||||
|
app.domains = make(map[string]domains.Domain)
|
||||||
|
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) InitConfig() error {
|
||||||
|
config, err := configuration.New()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %w (%w)", ErrApplication, ErrConfigInitializationError, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
a.config = config
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) InitLogger() {
|
||||||
|
a.logger.Logger.SetLevel(a.config.Deconnect.LogLevel)
|
||||||
|
|
||||||
|
a.logger.WithField("log level", a.config.Deconnect.LogLevel).Debug("Set log level")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) RegisterDomain(name string, implementation domains.Domain) {
|
||||||
|
a.domainsMutex.Lock()
|
||||||
|
defer a.domainsMutex.Unlock()
|
||||||
|
|
||||||
|
a.domains[name] = implementation
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) RetrieveDomain(name string) any {
|
||||||
|
a.domainsMutex.RLock()
|
||||||
|
defer a.domainsMutex.RUnlock()
|
||||||
|
|
||||||
|
return a.domains[name]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) ConnectDependencies() error {
|
||||||
|
a.domainsMutex.RLock()
|
||||||
|
defer a.domainsMutex.RUnlock()
|
||||||
|
|
||||||
|
for _, domain := range a.domains {
|
||||||
|
err := domain.ConnectDependencies()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %w (%w)", ErrApplication, ErrConnectDependencies, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) StartDomains() error {
|
||||||
|
a.domainsMutex.RLock()
|
||||||
|
defer a.domainsMutex.RUnlock()
|
||||||
|
|
||||||
|
for _, domain := range a.domains {
|
||||||
|
err := domain.Start()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %w (%w)", ErrApplication, ErrDomainInit, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) RegisterGlobalWaitGroup(wg *sync.WaitGroup) {
|
||||||
|
a.wg = wg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *App) GetGlobalWaitGroup() *sync.WaitGroup {
|
||||||
|
return a.wg
|
||||||
|
}
|
||||||
10
internal/application/errors.go
Normal file
10
internal/application/errors.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrApplication = errors.New("application")
|
||||||
|
ErrConfigInitializationError = errors.New("config initialization error")
|
||||||
|
ErrConnectDependencies = errors.New("failed to connect dependencies")
|
||||||
|
ErrDomainInit = errors.New("failed to initialize domains")
|
||||||
|
)
|
||||||
46
internal/configuration/config.go
Normal file
46
internal/configuration/config.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Deconnect Deconnect `yaml:"deconnect"`
|
||||||
|
Upstream Upstream `yaml:"upstream"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Deconnect struct {
|
||||||
|
LogLevel logrus.Level `yaml:"log_level"`
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Port string `yaml:"port"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Upstream struct {
|
||||||
|
URL string `yaml:"url"`
|
||||||
|
InsecureTLS bool `yaml:"insecure_tls,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func New() (*Config, error) {
|
||||||
|
deconnectCfgPath := "/etc/deconnect.yaml"
|
||||||
|
if customPath, ok := os.LookupEnv("DECONNECT_CONFIG"); ok {
|
||||||
|
deconnectCfgPath = customPath
|
||||||
|
}
|
||||||
|
|
||||||
|
rawConfig, err := os.ReadFile(deconnectCfgPath)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %w (%w)", ErrConfiguration, ErrCantReadConfigFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
config := new(Config)
|
||||||
|
|
||||||
|
err = yaml.Unmarshal(rawConfig, config)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("%w: %w (%w)", ErrConfiguration, ErrCantParseConfigFile, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return config, nil
|
||||||
|
}
|
||||||
10
internal/configuration/errors.go
Normal file
10
internal/configuration/errors.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package configuration
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrConfiguration = errors.New("configuration")
|
||||||
|
ErrCantReadConfigFile = errors.New("can't read config file")
|
||||||
|
ErrCantParseConfigFile = errors.New("can't parse config file")
|
||||||
|
ErrSourceDirectoryDoesNotExist = errors.New("source directory does not exist")
|
||||||
|
)
|
||||||
11
internal/domains/deconnector.go
Normal file
11
internal/domains/deconnector.go
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package domains
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
const DomainNameDeconnector = "deconnector"
|
||||||
|
|
||||||
|
type Deconnector interface {
|
||||||
|
Handle(clientConn net.Conn)
|
||||||
|
}
|
||||||
33
internal/domains/deconnector/connect.go
Normal file
33
internal/domains/deconnector/connect.go
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
package deconnector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Deconnector) handleDeconnect(clientConn net.Conn, connectReq *http.Request, upstreamURL *url.URL) {
|
||||||
|
// Tell client the tunnel is open
|
||||||
|
fmt.Fprintf(clientConn, "HTTP/1.1 200 Connection established\r\n\r\n")
|
||||||
|
|
||||||
|
// Read the real HTTP request the client sends through the tunnel
|
||||||
|
innerReq, err := http.ReadRequest(bufio.NewReader(clientConn))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("failed to read inner request after CONNECT:80: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
innerReq.URL.Scheme = "http"
|
||||||
|
innerReq.URL.Host = connectReq.Host
|
||||||
|
innerReq.RequestURI = ""
|
||||||
|
|
||||||
|
d.app.Logger().
|
||||||
|
WithField("method", innerReq.Method).
|
||||||
|
WithField("url", innerReq.URL).
|
||||||
|
Info("Handling de-CONNECT request")
|
||||||
|
|
||||||
|
d.forwardHTTP(clientConn, innerReq, upstreamURL)
|
||||||
|
}
|
||||||
43
internal/domains/deconnector/deconnector.go
Normal file
43
internal/domains/deconnector/deconnector.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package deconnector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/application"
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/domains"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ domains.Deconnector = new(Deconnector)
|
||||||
|
_ domains.Domain = new(Deconnector)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Deconnector struct {
|
||||||
|
app *application.App
|
||||||
|
|
||||||
|
dialer domains.Dialer
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(app *application.App) *Deconnector {
|
||||||
|
return &Deconnector{
|
||||||
|
app: app,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Deconnector) ConnectDependencies() error {
|
||||||
|
dialer, ok := d.app.RetrieveDomain(domains.DomainNameDialer).(domains.Dialer)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%w: %w (%s)", ErrDeconnector, ErrConnectDependencies,
|
||||||
|
"dialer domain interface conversion failed",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.dialer = dialer
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Deconnector) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
8
internal/domains/deconnector/errors.go
Normal file
8
internal/domains/deconnector/errors.go
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
package deconnector
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrDeconnector = errors.New("deconnector")
|
||||||
|
ErrConnectDependencies = errors.New("failed to connect dependencies")
|
||||||
|
)
|
||||||
36
internal/domains/deconnector/forward.go
Normal file
36
internal/domains/deconnector/forward.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package deconnector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Deconnector) forwardHTTP(clientConn net.Conn, req *http.Request, upstreamURL *url.URL) {
|
||||||
|
upstreamConn, err := d.dialer.Dial()
|
||||||
|
if err != nil {
|
||||||
|
d.app.Logger().WithError(err).Error("upstream dial failed")
|
||||||
|
fmt.Fprintf(clientConn, "HTTP/1.1 502 Bad Gateway\r\n\r\n")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer upstreamConn.Close()
|
||||||
|
|
||||||
|
if err := req.WriteProxy(upstreamConn); err != nil {
|
||||||
|
d.app.Logger().WithError(err).Error("failed to write request")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(upstreamConn), req)
|
||||||
|
if err != nil {
|
||||||
|
d.app.Logger().WithError(err).Error("failed to read response")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer resp.Body.Close()
|
||||||
|
resp.Write(clientConn)
|
||||||
|
}
|
||||||
46
internal/domains/deconnector/handle.go
Normal file
46
internal/domains/deconnector/handle.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package deconnector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Deconnector) Handle(clientConn net.Conn) {
|
||||||
|
upstreamURL, err := d.dialer.UpstreamURL()
|
||||||
|
if err != nil {
|
||||||
|
d.app.Logger().WithError(err).Error("failed to get upstream URL")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
defer clientConn.Close()
|
||||||
|
|
||||||
|
req, err := http.ReadRequest(bufio.NewReader(clientConn))
|
||||||
|
if err != nil {
|
||||||
|
d.app.Logger().WithError(err).Error("failed to read request")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if req.Method == http.MethodConnect {
|
||||||
|
_, port, _ := net.SplitHostPort(req.Host)
|
||||||
|
if port == "443" {
|
||||||
|
d.handleTunnel(clientConn, req.Host, upstreamURL)
|
||||||
|
} else {
|
||||||
|
d.handleDeconnect(clientConn, req, upstreamURL)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
req.RequestURI = ""
|
||||||
|
if req.URL.Host == "" {
|
||||||
|
req.URL.Host = req.Host
|
||||||
|
}
|
||||||
|
req.URL.Scheme = "http"
|
||||||
|
d.app.Logger().
|
||||||
|
WithField("method", req.Method).
|
||||||
|
WithField("url", req.URL).
|
||||||
|
Info("Forwarding HTTP request")
|
||||||
|
|
||||||
|
d.forwardHTTP(clientConn, req, upstreamURL)
|
||||||
|
}
|
||||||
|
}
|
||||||
43
internal/domains/deconnector/tunnel.go
Normal file
43
internal/domains/deconnector/tunnel.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package deconnector
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Deconnector) handleTunnel(clientConn net.Conn, host string, upstreamURL *url.URL) {
|
||||||
|
d.app.Logger().WithField("host", host).Info("Handling CONNECT tunnel")
|
||||||
|
|
||||||
|
upstreamConn, err := d.dialer.Dial()
|
||||||
|
if err != nil {
|
||||||
|
d.app.Logger().WithError(err).Error("upstream dial failed")
|
||||||
|
fmt.Fprintf(clientConn, "HTTP/1.1 502 Bad Gateway\r\n\r\n")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer upstreamConn.Close()
|
||||||
|
|
||||||
|
connectLine := fmt.Sprintf(
|
||||||
|
"CONNECT %s HTTP/1.1\r\nHost: %s\r\n\r\n", host, host,
|
||||||
|
)
|
||||||
|
fmt.Fprint(upstreamConn, connectLine)
|
||||||
|
|
||||||
|
resp, err := http.ReadResponse(bufio.NewReader(upstreamConn), nil)
|
||||||
|
if err != nil || resp.StatusCode > 499 {
|
||||||
|
d.app.Logger().WithError(err).Error("upstream CONNECT failed")
|
||||||
|
fmt.Fprintf(clientConn, "HTTP/1.1 502 Bad Gateway\r\n\r\n")
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(clientConn, "HTTP/1.1 200 Connection established\r\n\r\n")
|
||||||
|
|
||||||
|
done := make(chan struct{}, 2)
|
||||||
|
go func() { io.Copy(upstreamConn, clientConn); done <- struct{}{} }()
|
||||||
|
go func() { io.Copy(clientConn, upstreamConn); done <- struct{}{} }()
|
||||||
|
<-done
|
||||||
|
}
|
||||||
13
internal/domains/dialer.go
Normal file
13
internal/domains/dialer.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package domains
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
const DomainNameDialer = "dialer"
|
||||||
|
|
||||||
|
type Dialer interface {
|
||||||
|
Dial() (net.Conn, error)
|
||||||
|
UpstreamURL() (*url.URL, error)
|
||||||
|
}
|
||||||
37
internal/domains/dialer/dial.go
Normal file
37
internal/domains/dialer/dial.go
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/tls"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/url"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (d *Dialer) Dial() (net.Conn, error) {
|
||||||
|
if d.dialURL.Scheme == "https" {
|
||||||
|
return tls.Dial("tcp", d.dialURL.Host, &tls.Config{
|
||||||
|
ServerName: d.dialURL.Hostname(),
|
||||||
|
InsecureSkipVerify: d.app.Config().Upstream.InsecureTLS,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return net.Dial("tcp", d.dialURL.Host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialer) UpstreamURL() (*url.URL, error) {
|
||||||
|
if d.app.Config().Upstream.URL == "" {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"%w: %w (%s)", ErrDialer, ErrParseURL,
|
||||||
|
"upstream URL is empty",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
u, err := url.Parse(d.app.Config().Upstream.URL)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf(
|
||||||
|
"%w: %w (%w)", ErrDialer, ErrParseURL, err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return u, nil
|
||||||
|
}
|
||||||
43
internal/domains/dialer/dialer.go
Normal file
43
internal/domains/dialer/dialer.go
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package dialer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/application"
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/domains"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ domains.Dialer = new(Dialer)
|
||||||
|
_ domains.Domain = new(Dialer)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Dialer struct {
|
||||||
|
app *application.App
|
||||||
|
|
||||||
|
dialURL *url.URL
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(app *application.App) *Dialer {
|
||||||
|
return &Dialer{
|
||||||
|
app: app,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialer) ConnectDependencies() error {
|
||||||
|
dialURL, err := d.UpstreamURL()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%w: %w (%w)", ErrDialer, ErrConnectDependencies, err,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
d.dialURL = dialURL
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (d *Dialer) Start() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
9
internal/domains/dialer/errors.go
Normal file
9
internal/domains/dialer/errors.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package dialer
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrDialer = errors.New("dialer")
|
||||||
|
ErrConnectDependencies = errors.New("failed to connect dependencies")
|
||||||
|
ErrParseURL = errors.New("failed to parse URL")
|
||||||
|
)
|
||||||
6
internal/domains/domain.go
Normal file
6
internal/domains/domain.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package domains
|
||||||
|
|
||||||
|
type Domain interface {
|
||||||
|
ConnectDependencies() error
|
||||||
|
Start() error
|
||||||
|
}
|
||||||
5
internal/domains/listener.go
Normal file
5
internal/domains/listener.go
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
package domains
|
||||||
|
|
||||||
|
const DomainNameListener = "listener"
|
||||||
|
|
||||||
|
type Listener any
|
||||||
10
internal/domains/listener/errors.go
Normal file
10
internal/domains/listener/errors.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package listener
|
||||||
|
|
||||||
|
import "errors"
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrListener = errors.New("listener")
|
||||||
|
ErrConnectDependencies = errors.New("failed to connect dependencies")
|
||||||
|
ErrFailedToGetWaitGroup = errors.New("failed to get global waitgroup")
|
||||||
|
ErrFailedToListen = errors.New("failed to listen")
|
||||||
|
)
|
||||||
31
internal/domains/listener/listen.go
Normal file
31
internal/domains/listener/listen.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package listener
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (l *Listener) Listen() error {
|
||||||
|
ln, err := net.Listen(
|
||||||
|
"tcp",
|
||||||
|
l.app.Config().Deconnect.Host+":"+l.app.Config().Deconnect.Port,
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("%w: %w (%w)", ErrListener, ErrFailedToListen, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.app.Logger().WithField("host", l.app.Config().Deconnect.Host).
|
||||||
|
WithField("port", l.app.Config().Deconnect.Port).
|
||||||
|
Info("Listening for incoming connections")
|
||||||
|
|
||||||
|
for {
|
||||||
|
conn, err := ln.Accept()
|
||||||
|
if err != nil {
|
||||||
|
l.app.Logger().WithError(err).Error("accept error")
|
||||||
|
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
go l.deconnector.Handle(conn)
|
||||||
|
}
|
||||||
|
}
|
||||||
52
internal/domains/listener/listener.go
Normal file
52
internal/domains/listener/listener.go
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
package listener
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/application"
|
||||||
|
"source.hodakov.me/hdkv/deconnect/internal/domains"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ domains.Listener = new(Listener)
|
||||||
|
_ domains.Domain = new(Listener)
|
||||||
|
)
|
||||||
|
|
||||||
|
type Listener struct {
|
||||||
|
app *application.App
|
||||||
|
|
||||||
|
deconnector domains.Deconnector
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(app *application.App) *Listener {
|
||||||
|
return &Listener{
|
||||||
|
app: app,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) ConnectDependencies() error {
|
||||||
|
deconnector, ok := l.app.RetrieveDomain(domains.DomainNameDeconnector).(domains.Deconnector)
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf(
|
||||||
|
"%w: %w (%s)", ErrListener, ErrConnectDependencies,
|
||||||
|
"deconnector domain interface conversion failed",
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
l.deconnector = deconnector
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *Listener) Start() error {
|
||||||
|
wg := l.app.GetGlobalWaitGroup()
|
||||||
|
if wg == nil {
|
||||||
|
return fmt.Errorf("%w: %w (%s)", ErrListener, ErrFailedToGetWaitGroup, "got nil waitgroup")
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Go(func() {
|
||||||
|
l.Listen()
|
||||||
|
})
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
31
vendor/github.com/goccy/go-yaml/.codecov.yml
generated
vendored
Normal file
31
vendor/github.com/goccy/go-yaml/.codecov.yml
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
codecov:
|
||||||
|
require_ci_to_pass: yes
|
||||||
|
|
||||||
|
coverage:
|
||||||
|
precision: 2
|
||||||
|
round: down
|
||||||
|
range: "70...100"
|
||||||
|
|
||||||
|
status:
|
||||||
|
project:
|
||||||
|
default:
|
||||||
|
target: 75%
|
||||||
|
threshold: 2%
|
||||||
|
patch: off
|
||||||
|
changes: no
|
||||||
|
|
||||||
|
parsers:
|
||||||
|
gcov:
|
||||||
|
branch_detection:
|
||||||
|
conditional: yes
|
||||||
|
loop: yes
|
||||||
|
method: no
|
||||||
|
macro: no
|
||||||
|
|
||||||
|
comment:
|
||||||
|
layout: "header,diff"
|
||||||
|
behavior: default
|
||||||
|
require_changes: no
|
||||||
|
|
||||||
|
ignore:
|
||||||
|
- ast
|
||||||
3
vendor/github.com/goccy/go-yaml/.gitignore
generated
vendored
Normal file
3
vendor/github.com/goccy/go-yaml/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
bin/
|
||||||
|
.idea/
|
||||||
|
cover.out
|
||||||
65
vendor/github.com/goccy/go-yaml/.golangci.yml
generated
vendored
Normal file
65
vendor/github.com/goccy/go-yaml/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
version: "2"
|
||||||
|
linters:
|
||||||
|
default: none
|
||||||
|
enable:
|
||||||
|
- errcheck
|
||||||
|
- govet
|
||||||
|
- ineffassign
|
||||||
|
- misspell
|
||||||
|
- perfsprint
|
||||||
|
- staticcheck
|
||||||
|
- unused
|
||||||
|
settings:
|
||||||
|
errcheck:
|
||||||
|
without_tests: true
|
||||||
|
govet:
|
||||||
|
disable:
|
||||||
|
- tests
|
||||||
|
misspell:
|
||||||
|
locale: US
|
||||||
|
perfsprint:
|
||||||
|
int-conversion: false
|
||||||
|
err-error: false
|
||||||
|
errorf: true
|
||||||
|
sprintf1: false
|
||||||
|
strconcat: false
|
||||||
|
staticcheck:
|
||||||
|
checks:
|
||||||
|
- -ST1000
|
||||||
|
- -ST1005
|
||||||
|
- all
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
presets:
|
||||||
|
- comments
|
||||||
|
- common-false-positives
|
||||||
|
- legacy
|
||||||
|
- std-error-handling
|
||||||
|
rules:
|
||||||
|
- linters:
|
||||||
|
- staticcheck
|
||||||
|
path: _test\.go
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
formatters:
|
||||||
|
enable:
|
||||||
|
- gci
|
||||||
|
- gofmt
|
||||||
|
settings:
|
||||||
|
gci:
|
||||||
|
sections:
|
||||||
|
- standard
|
||||||
|
- default
|
||||||
|
- prefix(github.com/goccy/go-yaml)
|
||||||
|
- blank
|
||||||
|
- dot
|
||||||
|
gofmt:
|
||||||
|
simplify: true
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
186
vendor/github.com/goccy/go-yaml/CHANGELOG.md
generated
vendored
Normal file
186
vendor/github.com/goccy/go-yaml/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
# 1.11.2 - 2023-09-15
|
||||||
|
|
||||||
|
### Fix bugs
|
||||||
|
|
||||||
|
- Fix quoted comments ( #370 )
|
||||||
|
- Fix handle of space at start or last ( #376 )
|
||||||
|
- Fix sequence with comment ( #390 )
|
||||||
|
|
||||||
|
# 1.11.1 - 2023-09-14
|
||||||
|
|
||||||
|
### Fix bugs
|
||||||
|
|
||||||
|
- Handle `\r` in a double-quoted string the same as `\n` ( #372 )
|
||||||
|
- Replace loop with n.Values = append(n.Values, target.Values...) ( #380 )
|
||||||
|
- Skip encoding an inline field if it is null ( #386 )
|
||||||
|
- Fix comment parsing with null value ( #388 )
|
||||||
|
|
||||||
|
# 1.11.0 - 2023-04-03
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Supports dynamically switch encode and decode processing for a given type
|
||||||
|
|
||||||
|
# 1.10.1 - 2023-03-28
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
- Quote YAML 1.1 bools at encoding time for compatibility with other legacy parsers
|
||||||
|
- Add support of 32-bit architecture
|
||||||
|
|
||||||
|
### Fix bugs
|
||||||
|
|
||||||
|
- Don't trim all space characters in block style sequence
|
||||||
|
- Support strings starting with `@`
|
||||||
|
|
||||||
|
# 1.10.0 - 2023-03-01
|
||||||
|
|
||||||
|
### Fix bugs
|
||||||
|
|
||||||
|
Reversible conversion of comments was not working in various cases, which has been corrected.
|
||||||
|
**Breaking Change** exists in the comment map interface. However, if you are dealing with CommentMap directly, there is no problem.
|
||||||
|
|
||||||
|
|
||||||
|
# 1.9.8 - 2022-12-19
|
||||||
|
|
||||||
|
### Fix feature
|
||||||
|
|
||||||
|
- Append new line at the end of file ( #329 )
|
||||||
|
|
||||||
|
### Fix bugs
|
||||||
|
|
||||||
|
- Fix custom marshaler ( #333, #334 )
|
||||||
|
- Fix behavior when struct fields conflicted( #335 )
|
||||||
|
- Fix position calculation for literal, folded and raw folded strings ( #330 )
|
||||||
|
|
||||||
|
# 1.9.7 - 2022-12-03
|
||||||
|
|
||||||
|
### Fix bugs
|
||||||
|
|
||||||
|
- Fix handling of quoted map key ( #328 )
|
||||||
|
- Fix resusing process of scanning context ( #322 )
|
||||||
|
|
||||||
|
## v1.9.6 - 2022-10-26
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
- Introduce MapKeyNode interface to limit node types for map key ( #312 )
|
||||||
|
|
||||||
|
### Fix bugs
|
||||||
|
|
||||||
|
- Quote strings with special characters in flow mode ( #270 )
|
||||||
|
- typeError implements PrettyPrinter interface ( #280 )
|
||||||
|
- Fix incorrect const type ( #284 )
|
||||||
|
- Fix large literals type inference on 32 bits ( #293 )
|
||||||
|
- Fix UTF-8 characters ( #294 )
|
||||||
|
- Fix decoding of unknown aliases ( #317 )
|
||||||
|
- Fix stream encoder for insert a separator between each encoded document ( #318 )
|
||||||
|
|
||||||
|
### Update
|
||||||
|
|
||||||
|
- Update golang.org/x/sys ( #289 )
|
||||||
|
- Update Go version in CI ( #295 )
|
||||||
|
- Add test cases for missing keys to struct literals ( #300 )
|
||||||
|
|
||||||
|
## v1.9.5 - 2022-01-12
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
* Add UseSingleQuote option ( #265 )
|
||||||
|
|
||||||
|
### Fix bugs
|
||||||
|
|
||||||
|
* Preserve defaults while decoding nested structs ( #260 )
|
||||||
|
* Fix minor typo in decodeInit error ( #264 )
|
||||||
|
* Handle empty sequence entries ( #275 )
|
||||||
|
* Fix encoding of sequence with multiline string ( #276 )
|
||||||
|
* Fix encoding of BytesMarshaler type ( #277 )
|
||||||
|
* Fix indentState logic for multi-line value ( #278 )
|
||||||
|
|
||||||
|
## v1.9.4 - 2021-10-12
|
||||||
|
|
||||||
|
### Fix bugs
|
||||||
|
|
||||||
|
* Keep prev/next reference between tokens containing comments when filtering comment tokens ( #257 )
|
||||||
|
* Supports escaping reserved keywords in PathBuilder ( #258 )
|
||||||
|
|
||||||
|
## v1.9.3 - 2021-09-07
|
||||||
|
|
||||||
|
### New Features
|
||||||
|
|
||||||
|
* Support encoding and decoding `time.Duration` fields ( #246 )
|
||||||
|
* Allow reserved characters for key name in YAMLPath ( #251 )
|
||||||
|
* Support getting YAMLPath from ast.Node ( #252 )
|
||||||
|
* Support CommentToMap option ( #253 )
|
||||||
|
|
||||||
|
### Fix bugs
|
||||||
|
|
||||||
|
* Fix encoding nested sequences with `yaml.IndentSequence` ( #241 )
|
||||||
|
* Fix error reporting on inline structs in strict mode ( #244, #245 )
|
||||||
|
* Fix encoding of large floats ( #247 )
|
||||||
|
|
||||||
|
### Improve workflow
|
||||||
|
|
||||||
|
* Migrate CI from CircleCI to GitHub Action ( #249 )
|
||||||
|
* Add workflow for ycat ( #250 )
|
||||||
|
|
||||||
|
## v1.9.2 - 2021-07-26
|
||||||
|
|
||||||
|
### Support WithComment option ( #238 )
|
||||||
|
|
||||||
|
`yaml.WithComment` is a option for encoding with comment.
|
||||||
|
The position where you want to add a comment is represented by YAMLPath, and it is the key of `yaml.CommentMap`.
|
||||||
|
Also, you can select `Head` comment or `Line` comment as the comment type.
|
||||||
|
|
||||||
|
## v1.9.1 - 2021-07-20
|
||||||
|
|
||||||
|
### Fix DecodeFromNode ( #237 )
|
||||||
|
|
||||||
|
- Fix YAML handling where anchor exists
|
||||||
|
|
||||||
|
## v1.9.0 - 2021-07-19
|
||||||
|
|
||||||
|
### New features
|
||||||
|
|
||||||
|
- Support encoding of comment node ( #233 )
|
||||||
|
- Support `yaml.NodeToValue(ast.Node, interface{}, ...DecodeOption) error` ( #236 )
|
||||||
|
- Can convert a AST node to a value directly
|
||||||
|
|
||||||
|
### Fix decoder for comment
|
||||||
|
|
||||||
|
- Fix parsing of literal with comment ( #234 )
|
||||||
|
|
||||||
|
### Rename API ( #235 )
|
||||||
|
|
||||||
|
- Rename `MarshalWithContext` to `MarshalContext`
|
||||||
|
- Rename `UnmarshalWithContext` to `UnmarshalContext`
|
||||||
|
|
||||||
|
## v1.8.10 - 2021-07-02
|
||||||
|
|
||||||
|
### Fixed bugs
|
||||||
|
|
||||||
|
- Fix searching anchor by alias name ( #212 )
|
||||||
|
- Fixing Issue 186, scanner should account for newline characters when processing multi-line text. Without this source annotations line/column number (for this and all subsequent tokens) is inconsistent with plain text editors. e.g. https://github.com/goccy/go-yaml/issues/186. This addresses the issue specifically for single and double quote text only. ( #210 )
|
||||||
|
- Add error for unterminated flow mapping node ( #213 )
|
||||||
|
- Handle missing required field validation ( #221 )
|
||||||
|
- Nicely format unexpected node type errors ( #229 )
|
||||||
|
- Support to encode map which has defined type key ( #231 )
|
||||||
|
|
||||||
|
### New features
|
||||||
|
|
||||||
|
- Support sequence indentation by EncodeOption ( #232 )
|
||||||
|
|
||||||
|
## v1.8.9 - 2021-03-01
|
||||||
|
|
||||||
|
### Fixed bugs
|
||||||
|
|
||||||
|
- Fix origin buffer for DocumentHeader and DocumentEnd and Directive
|
||||||
|
- Fix origin buffer for anchor value
|
||||||
|
- Fix syntax error about map value
|
||||||
|
- Fix parsing MergeKey ('<<') characters
|
||||||
|
- Fix encoding of float value
|
||||||
|
- Fix incorrect column annotation when single or double quotes are used
|
||||||
|
|
||||||
|
### New features
|
||||||
|
|
||||||
|
- Support to encode/decode of ast.Node directly
|
||||||
21
vendor/github.com/goccy/go-yaml/LICENSE
generated
vendored
Normal file
21
vendor/github.com/goccy/go-yaml/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2019 Masaaki Goshima
|
||||||
|
|
||||||
|
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.
|
||||||
55
vendor/github.com/goccy/go-yaml/Makefile
generated
vendored
Normal file
55
vendor/github.com/goccy/go-yaml/Makefile
generated
vendored
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
## Location to install dependencies to
|
||||||
|
LOCALBIN ?= $(shell pwd)/bin
|
||||||
|
TESTMOD := testdata/go_test.mod
|
||||||
|
|
||||||
|
$(LOCALBIN):
|
||||||
|
mkdir -p $(LOCALBIN)
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
test:
|
||||||
|
go test -v -race ./...
|
||||||
|
go test -v -race ./testdata -modfile=$(TESTMOD)
|
||||||
|
|
||||||
|
.PHONY: simple-test
|
||||||
|
simple-test:
|
||||||
|
go test -v ./...
|
||||||
|
go test -v ./testdata -modfile=$(TESTMOD)
|
||||||
|
|
||||||
|
.PHONY: fuzz
|
||||||
|
fuzz:
|
||||||
|
go test -fuzz=Fuzz -fuzztime 60s
|
||||||
|
|
||||||
|
.PHONY: cover
|
||||||
|
cover:
|
||||||
|
go test -coverpkg=.,./ast,./lexer,./parser,./printer,./scanner,./token -coverprofile=cover.out -modfile=$(TESTMOD) ./... ./testdata
|
||||||
|
|
||||||
|
.PHONY: cover-html
|
||||||
|
cover-html: cover
|
||||||
|
go tool cover -html=cover.out
|
||||||
|
|
||||||
|
.PHONY: ycat/build
|
||||||
|
ycat/build: $(LOCALBIN)
|
||||||
|
cd ./cmd/ycat && go build -o $(LOCALBIN)/ycat .
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
lint: golangci-lint ## Run golangci-lint
|
||||||
|
@$(GOLANGCI_LINT) run
|
||||||
|
|
||||||
|
.PHONY: fmt
|
||||||
|
fmt: golangci-lint ## Ensure consistent code style
|
||||||
|
@go mod tidy
|
||||||
|
@go fmt ./...
|
||||||
|
@$(GOLANGCI_LINT) run --fix
|
||||||
|
|
||||||
|
## Tool Binaries
|
||||||
|
GOLANGCI_LINT ?= $(LOCALBIN)/golangci-lint
|
||||||
|
|
||||||
|
## Tool Versions
|
||||||
|
GOLANGCI_VERSION := 2.1.2
|
||||||
|
|
||||||
|
.PHONY: golangci-lint
|
||||||
|
.PHONY: $(GOLANGCI_LINT)
|
||||||
|
golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary.
|
||||||
|
$(GOLANGCI_LINT): $(LOCALBIN)
|
||||||
|
@test -s $(LOCALBIN)/golangci-lint && $(LOCALBIN)/golangci-lint version --short | grep -q $(GOLANGCI_VERSION) || \
|
||||||
|
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(LOCALBIN) v$(GOLANGCI_VERSION)
|
||||||
420
vendor/github.com/goccy/go-yaml/README.md
generated
vendored
Normal file
420
vendor/github.com/goccy/go-yaml/README.md
generated
vendored
Normal file
@@ -0,0 +1,420 @@
|
|||||||
|
# YAML support for the Go language
|
||||||
|
|
||||||
|
[](https://pkg.go.dev/github.com/goccy/go-yaml)
|
||||||
|

|
||||||
|
[](https://codecov.io/gh/goccy/go-yaml)
|
||||||
|
[](https://goreportcard.com/report/github.com/goccy/go-yaml)
|
||||||
|
|
||||||
|
<img width="300px" src="https://user-images.githubusercontent.com/209884/67159116-64d94b80-f37b-11e9-9b28-f8379636a43c.png"></img>
|
||||||
|
|
||||||
|
## This library has **NO** relation to the go-yaml/yaml library
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> This library is developed from scratch to replace [`go-yaml/yaml`](https://github.com/go-yaml/yaml).
|
||||||
|
> If you're looking for a better YAML library, this one should be helpful.
|
||||||
|
|
||||||
|
# Why a new library?
|
||||||
|
|
||||||
|
As of this writing, there already exists a de facto standard library for YAML processing for Go: [https://github.com/go-yaml/yaml](https://github.com/go-yaml/yaml). However, we believe that a new YAML library is necessary for the following reasons:
|
||||||
|
|
||||||
|
- Not actively maintained
|
||||||
|
- `go-yaml/yaml` has ported the libyaml written in C to Go, so the source code is not written in Go style
|
||||||
|
- There is a lot of content that cannot be parsed
|
||||||
|
- YAML is often used for configuration, and it is common to include validation along with it. However, the errors in `go-yaml/yaml` are not intuitive, and it is difficult to provide meaningful validation errors
|
||||||
|
- When creating tools that use YAML, there are cases where reversible transformation of YAML is required. However, to perform reversible transformations of content that includes Comments or Anchors/Aliases, manipulating the AST is the only option
|
||||||
|
- Non-intuitive [Marshaler](https://pkg.go.dev/gopkg.in/yaml.v3#Marshaler) / [Unmarshaler](https://pkg.go.dev/gopkg.in/yaml.v3#Unmarshaler)
|
||||||
|
|
||||||
|
By the way, libraries such as [ghodss/yaml](https://github.com/ghodss/yaml) and [sigs.k8s.io/yaml](https://github.com/kubernetes-sigs/yaml) also depend on go-yaml/yaml, so if you are using these libraries, the same issues apply: they cannot parse things that go-yaml/yaml cannot parse, and they inherit many of the problems that go-yaml/yaml has.
|
||||||
|
|
||||||
|
# Features
|
||||||
|
|
||||||
|
- No dependencies
|
||||||
|
- A better parser than `go-yaml/yaml`.
|
||||||
|
- [Support recursive processing](https://github.com/apple/device-management/blob/release/docs/schema.yaml)
|
||||||
|
- Higher coverage in the [YAML Test Suite](https://github.com/yaml/yaml-test-suite?tab=readme-ov-file)
|
||||||
|
- YAML Test Suite consists of 402 cases in total, of which `gopkg.in/yaml.v3` passes `295`. In addition to passing all those test cases, `goccy/go-yaml` successfully passes nearly 60 additional test cases ( 2024/12/15 )
|
||||||
|
- The test code is [here](https://github.com/goccy/go-yaml/blob/master/yaml_test_suite_test.go#L77)
|
||||||
|
- Ease and sustainability of maintenance
|
||||||
|
- The main maintainer is [@goccy](https://github.com/goccy), but we are also building a system to develop as a team with trusted developers
|
||||||
|
- Since it is written from scratch, the code is easy to read for Gophers
|
||||||
|
- An API structure that allows the use of not only `Encoder`/`Decoder` but also `Tokenizer` and `Parser` functionalities.
|
||||||
|
- [lexer.Tokenize](https://pkg.go.dev/github.com/goccy/go-yaml@v1.15.4/lexer#Tokenize)
|
||||||
|
- [parser.Parse](https://pkg.go.dev/github.com/goccy/go-yaml@v1.15.4/parser#Parse)
|
||||||
|
- Filtering, replacing, and merging YAML content using YAML Path
|
||||||
|
- Reversible transformation without using the AST for YAML that includes Anchors, Aliases, and Comments
|
||||||
|
- Customize the Marshal/Unmarshal behavior for primitive types and third-party library types ([RegisterCustomMarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#RegisterCustomMarshaler), [RegisterCustomUnmarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#RegisterCustomUnmarshaler))
|
||||||
|
- Respects `encoding/json` behavior
|
||||||
|
- Accept the `json` tag. Note that not all options from the `json` tag will have significance when parsing YAML documents. If both tags exist, `yaml` tag will take precedence.
|
||||||
|
- [json.Marshaler](https://pkg.go.dev/encoding/json#Marshaler) style [marshaler](https://pkg.go.dev/github.com/goccy/go-yaml#BytesMarshaler)
|
||||||
|
- [json.Unmarshaler](https://pkg.go.dev/encoding/json#Unmarshaler) style [unmarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#BytesUnmarshaler)
|
||||||
|
- Options for using `MarshalJSON` and `UnmarshalJSON` ([UseJSONMarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#UseJSONMarshaler), [UseJSONUnmarshaler](https://pkg.go.dev/github.com/goccy/go-yaml#UseJSONUnmarshaler))
|
||||||
|
- Pretty format for error notifications
|
||||||
|
- Smart validation processing combined with [go-playground/validator](https://github.com/go-playground/validator)
|
||||||
|
- [example test code is here](https://github.com/goccy/go-yaml/blob/45889c98b0a0967240eb595a1bd6896e2f575106/testdata/validate_test.go#L12)
|
||||||
|
- Allow referencing elements declared in another file via anchors
|
||||||
|
|
||||||
|
# Users
|
||||||
|
|
||||||
|
The repositories that use goccy/go-yaml are listed here.
|
||||||
|
|
||||||
|
- https://github.com/goccy/go-yaml/wiki/Users
|
||||||
|
|
||||||
|
The source data is [here](https://github.com/goccy/go-yaml/network/dependents).
|
||||||
|
It is already being used in many repositories. Now it's your turn 😄
|
||||||
|
|
||||||
|
# Playground
|
||||||
|
|
||||||
|
The Playground visualizes how go-yaml processes YAML text. Use it to assist with your debugging or issue reporting.
|
||||||
|
|
||||||
|
https://goccy.github.io/go-yaml
|
||||||
|
|
||||||
|
# Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
go get github.com/goccy/go-yaml
|
||||||
|
```
|
||||||
|
|
||||||
|
# Synopsis
|
||||||
|
|
||||||
|
## 1. Simple Encode/Decode
|
||||||
|
|
||||||
|
Has an interface like `go-yaml/yaml` using `reflect`
|
||||||
|
|
||||||
|
```go
|
||||||
|
var v struct {
|
||||||
|
A int
|
||||||
|
B string
|
||||||
|
}
|
||||||
|
v.A = 1
|
||||||
|
v.B = "hello"
|
||||||
|
bytes, err := yaml.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
fmt.Println(string(bytes)) // "a: 1\nb: hello\n"
|
||||||
|
```
|
||||||
|
|
||||||
|
```go
|
||||||
|
yml := `
|
||||||
|
%YAML 1.2
|
||||||
|
---
|
||||||
|
a: 1
|
||||||
|
b: c
|
||||||
|
`
|
||||||
|
var v struct {
|
||||||
|
A int
|
||||||
|
B string
|
||||||
|
}
|
||||||
|
if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To control marshal/unmarshal behavior, you can use the `yaml` tag.
|
||||||
|
|
||||||
|
```go
|
||||||
|
yml := `---
|
||||||
|
foo: 1
|
||||||
|
bar: c
|
||||||
|
`
|
||||||
|
var v struct {
|
||||||
|
A int `yaml:"foo"`
|
||||||
|
B string `yaml:"bar"`
|
||||||
|
}
|
||||||
|
if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For convenience, we also accept the `json` tag. Note that not all options from
|
||||||
|
the `json` tag will have significance when parsing YAML documents. If both
|
||||||
|
tags exist, `yaml` tag will take precedence.
|
||||||
|
|
||||||
|
```go
|
||||||
|
yml := `---
|
||||||
|
foo: 1
|
||||||
|
bar: c
|
||||||
|
`
|
||||||
|
var v struct {
|
||||||
|
A int `json:"foo"`
|
||||||
|
B string `json:"bar"`
|
||||||
|
}
|
||||||
|
if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For custom marshal/unmarshaling, implement either `Bytes` or `Interface` variant of marshaler/unmarshaler. The difference is that while `BytesMarshaler`/`BytesUnmarshaler` behaves like [`encoding/json`](https://pkg.go.dev/encoding/json) and `InterfaceMarshaler`/`InterfaceUnmarshaler` behaves like [`gopkg.in/yaml.v2`](https://pkg.go.dev/gopkg.in/yaml.v2).
|
||||||
|
|
||||||
|
Semantically both are the same, but they differ in performance. Because indentation matters in YAML, you cannot simply accept a valid YAML fragment from a Marshaler, and expect it to work when it is attached to the parent container's serialized form. Therefore when we receive use the `BytesMarshaler`, which returns `[]byte`, we must decode it once to figure out how to make it work in the given context. If you use the `InterfaceMarshaler`, we can skip the decoding.
|
||||||
|
|
||||||
|
If you are repeatedly marshaling complex objects, the latter is always better
|
||||||
|
performance wise. But if you are, for example, just providing a choice between
|
||||||
|
a config file format that is read only once, the former is probably easier to
|
||||||
|
code.
|
||||||
|
|
||||||
|
## 2. Reference elements declared in another file
|
||||||
|
|
||||||
|
`testdata` directory contains `anchor.yml` file:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
├── testdata
|
||||||
|
└── anchor.yml
|
||||||
|
```
|
||||||
|
|
||||||
|
And `anchor.yml` is defined as follows:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
a: &a
|
||||||
|
b: 1
|
||||||
|
c: hello
|
||||||
|
```
|
||||||
|
|
||||||
|
Then, if `yaml.ReferenceDirs("testdata")` option is passed to `yaml.Decoder`,
|
||||||
|
`Decoder` tries to find the anchor definition from YAML files the under `testdata` directory.
|
||||||
|
|
||||||
|
```go
|
||||||
|
buf := bytes.NewBufferString("a: *a\n")
|
||||||
|
dec := yaml.NewDecoder(buf, yaml.ReferenceDirs("testdata"))
|
||||||
|
var v struct {
|
||||||
|
A struct {
|
||||||
|
B int
|
||||||
|
C string
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := dec.Decode(&v); err != nil {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
fmt.Printf("%+v\n", v) // {A:{B:1 C:hello}}
|
||||||
|
```
|
||||||
|
|
||||||
|
## 3. Encode with `Anchor` and `Alias`
|
||||||
|
|
||||||
|
### 3.1. Explicitly declared `Anchor` name and `Alias` name
|
||||||
|
|
||||||
|
If you want to use `anchor`, you can define it as a struct tag.
|
||||||
|
If the value specified for an anchor is a pointer type and the same address as the pointer is found, the value is automatically set to alias.
|
||||||
|
If an explicit alias name is specified, an error is raised if its value is different from the value specified in the anchor.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type T struct {
|
||||||
|
A int
|
||||||
|
B string
|
||||||
|
}
|
||||||
|
var v struct {
|
||||||
|
C *T `yaml:"c,anchor=x"`
|
||||||
|
D *T `yaml:"d,alias=x"`
|
||||||
|
}
|
||||||
|
v.C = &T{A: 1, B: "hello"}
|
||||||
|
v.D = v.C
|
||||||
|
bytes, err := yaml.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Println(string(bytes))
|
||||||
|
/*
|
||||||
|
c: &x
|
||||||
|
a: 1
|
||||||
|
b: hello
|
||||||
|
d: *x
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.2. Implicitly declared `Anchor` and `Alias` names
|
||||||
|
|
||||||
|
If you do not explicitly declare the anchor name, the default behavior is to
|
||||||
|
use the equivalent of `strings.ToLower($FieldName)` as the name of the anchor.
|
||||||
|
If the value specified for an anchor is a pointer type and the same address as the pointer is found, the value is automatically set to alias.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type T struct {
|
||||||
|
I int
|
||||||
|
S string
|
||||||
|
}
|
||||||
|
var v struct {
|
||||||
|
A *T `yaml:"a,anchor"`
|
||||||
|
B *T `yaml:"b,anchor"`
|
||||||
|
C *T `yaml:"c"`
|
||||||
|
D *T `yaml:"d"`
|
||||||
|
}
|
||||||
|
v.A = &T{I: 1, S: "hello"}
|
||||||
|
v.B = &T{I: 2, S: "world"}
|
||||||
|
v.C = v.A // C has same pointer address to A
|
||||||
|
v.D = v.B // D has same pointer address to B
|
||||||
|
bytes, err := yaml.Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
fmt.Println(string(bytes))
|
||||||
|
/*
|
||||||
|
a: &a
|
||||||
|
i: 1
|
||||||
|
s: hello
|
||||||
|
b: &b
|
||||||
|
i: 2
|
||||||
|
s: world
|
||||||
|
c: *a
|
||||||
|
d: *b
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3.3 MergeKey and Alias
|
||||||
|
|
||||||
|
Merge key and alias ( `<<: *alias` ) can be used by embedding a structure with the `inline,alias` tag.
|
||||||
|
|
||||||
|
```go
|
||||||
|
type Person struct {
|
||||||
|
*Person `yaml:",omitempty,inline,alias"` // embed Person type for default value
|
||||||
|
Name string `yaml:",omitempty"`
|
||||||
|
Age int `yaml:",omitempty"`
|
||||||
|
}
|
||||||
|
defaultPerson := &Person{
|
||||||
|
Name: "John Smith",
|
||||||
|
Age: 20,
|
||||||
|
}
|
||||||
|
people := []*Person{
|
||||||
|
{
|
||||||
|
Person: defaultPerson, // assign default value
|
||||||
|
Name: "Ken", // override Name property
|
||||||
|
Age: 10, // override Age property
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Person: defaultPerson, // assign default value only
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var doc struct {
|
||||||
|
Default *Person `yaml:"default,anchor"`
|
||||||
|
People []*Person `yaml:"people"`
|
||||||
|
}
|
||||||
|
doc.Default = defaultPerson
|
||||||
|
doc.People = people
|
||||||
|
bytes, err := yaml.Marshal(doc)
|
||||||
|
if err != nil {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
fmt.Println(string(bytes))
|
||||||
|
/*
|
||||||
|
default: &default
|
||||||
|
name: John Smith
|
||||||
|
age: 20
|
||||||
|
people:
|
||||||
|
- <<: *default
|
||||||
|
name: Ken
|
||||||
|
age: 10
|
||||||
|
- <<: *default
|
||||||
|
*/
|
||||||
|
```
|
||||||
|
|
||||||
|
## 4. Pretty Formatted Errors
|
||||||
|
|
||||||
|
Error values produced during parsing have two extra features over regular
|
||||||
|
error values.
|
||||||
|
|
||||||
|
First, by default, they contain extra information on the location of the error
|
||||||
|
from the source YAML document, to make it easier to find the error location.
|
||||||
|
|
||||||
|
Second, the error messages can optionally be colorized.
|
||||||
|
|
||||||
|
If you would like to control exactly how the output looks like, consider
|
||||||
|
using `yaml.FormatError`, which accepts two boolean values to
|
||||||
|
control turning these features on or off.
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/209884/67358124-587f0980-f59a-11e9-96fc-7205aab77695.png"></img>
|
||||||
|
|
||||||
|
## 5. Use YAMLPath
|
||||||
|
|
||||||
|
```go
|
||||||
|
yml := `
|
||||||
|
store:
|
||||||
|
book:
|
||||||
|
- author: john
|
||||||
|
price: 10
|
||||||
|
- author: ken
|
||||||
|
price: 12
|
||||||
|
bicycle:
|
||||||
|
color: red
|
||||||
|
price: 19.95
|
||||||
|
`
|
||||||
|
path, err := yaml.PathString("$.store.book[*].author")
|
||||||
|
if err != nil {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
var authors []string
|
||||||
|
if err := path.Read(strings.NewReader(yml), &authors); err != nil {
|
||||||
|
//...
|
||||||
|
}
|
||||||
|
fmt.Println(authors)
|
||||||
|
// [john ken]
|
||||||
|
```
|
||||||
|
|
||||||
|
### 5.1 Print customized error with YAML source code
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
yml := `
|
||||||
|
a: 1
|
||||||
|
b: "hello"
|
||||||
|
`
|
||||||
|
var v struct {
|
||||||
|
A int
|
||||||
|
B string
|
||||||
|
}
|
||||||
|
if err := yaml.Unmarshal([]byte(yml), &v); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
if v.A != 2 {
|
||||||
|
// output error with YAML source
|
||||||
|
path, err := yaml.PathString("$.a")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
source, err := path.AnnotateSource([]byte(yml), true)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
fmt.Printf("a value expected 2 but actual %d:\n%s\n", v.A, string(source))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
output result is the following:
|
||||||
|
|
||||||
|
<img src="https://user-images.githubusercontent.com/209884/84148813-7aca8680-aa9a-11ea-8fc9-37dece2ebdac.png"></img>
|
||||||
|
|
||||||
|
|
||||||
|
# Tools
|
||||||
|
|
||||||
|
## ycat
|
||||||
|
|
||||||
|
print yaml file with color
|
||||||
|
|
||||||
|
<img width="713" alt="ycat" src="https://user-images.githubusercontent.com/209884/66986084-19b00600-f0f9-11e9-9f0e-1f91eb072fe0.png">
|
||||||
|
|
||||||
|
### Installation
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git clone https://github.com/goccy/go-yaml.git
|
||||||
|
cd go-yaml/cmd/ycat && go install .
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
# For Developers
|
||||||
|
|
||||||
|
> [!NOTE]
|
||||||
|
> In this project, we manage such test code under the `testdata` directory to avoid adding dependencies on libraries that are only needed for testing to the top `go.mod` file. Therefore, if you want to add test cases that use 3rd party libraries, please add the test code to the `testdata` directory.
|
||||||
|
|
||||||
|
# Looking for Sponsors
|
||||||
|
|
||||||
|
I'm looking for sponsors this library. This library is being developed as a personal project in my spare time. If you want a quick response or problem resolution when using this library in your project, please register as a [sponsor](https://github.com/sponsors/goccy). I will cooperate as much as possible. Of course, this library is developed as an MIT license, so you can use it freely for free.
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
MIT
|
||||||
2381
vendor/github.com/goccy/go-yaml/ast/ast.go
generated
vendored
Normal file
2381
vendor/github.com/goccy/go-yaml/ast/ast.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
37
vendor/github.com/goccy/go-yaml/context.go
generated
vendored
Normal file
37
vendor/github.com/goccy/go-yaml/context.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
import "context"
|
||||||
|
|
||||||
|
type (
|
||||||
|
ctxMergeKey struct{}
|
||||||
|
ctxAnchorKey struct{}
|
||||||
|
)
|
||||||
|
|
||||||
|
func withMerge(ctx context.Context) context.Context {
|
||||||
|
return context.WithValue(ctx, ctxMergeKey{}, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isMerge(ctx context.Context) bool {
|
||||||
|
v, ok := ctx.Value(ctxMergeKey{}).(bool)
|
||||||
|
if !ok {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func withAnchor(ctx context.Context, name string) context.Context {
|
||||||
|
anchorMap := getAnchorMap(ctx)
|
||||||
|
if anchorMap == nil {
|
||||||
|
anchorMap = make(map[string]struct{})
|
||||||
|
}
|
||||||
|
anchorMap[name] = struct{}{}
|
||||||
|
return context.WithValue(ctx, ctxAnchorKey{}, anchorMap)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getAnchorMap(ctx context.Context) map[string]struct{} {
|
||||||
|
v, ok := ctx.Value(ctxAnchorKey{}).(map[string]struct{})
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
2037
vendor/github.com/goccy/go-yaml/decode.go
generated
vendored
Normal file
2037
vendor/github.com/goccy/go-yaml/decode.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
1074
vendor/github.com/goccy/go-yaml/encode.go
generated
vendored
Normal file
1074
vendor/github.com/goccy/go-yaml/encode.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
77
vendor/github.com/goccy/go-yaml/error.go
generated
vendored
Normal file
77
vendor/github.com/goccy/go-yaml/error.go
generated
vendored
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml/ast"
|
||||||
|
"github.com/goccy/go-yaml/internal/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
ErrInvalidQuery = errors.New("invalid query")
|
||||||
|
ErrInvalidPath = errors.New("invalid path instance")
|
||||||
|
ErrInvalidPathString = errors.New("invalid path string")
|
||||||
|
ErrNotFoundNode = errors.New("node not found")
|
||||||
|
ErrUnknownCommentPositionType = errors.New("unknown comment position type")
|
||||||
|
ErrInvalidCommentMapValue = errors.New("invalid comment map value. it must be not nil value")
|
||||||
|
ErrDecodeRequiredPointerType = errors.New("required pointer type value")
|
||||||
|
ErrExceededMaxDepth = errors.New("exceeded max depth")
|
||||||
|
FormatErrorWithToken = errors.FormatError
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
SyntaxError = errors.SyntaxError
|
||||||
|
TypeError = errors.TypeError
|
||||||
|
OverflowError = errors.OverflowError
|
||||||
|
DuplicateKeyError = errors.DuplicateKeyError
|
||||||
|
UnknownFieldError = errors.UnknownFieldError
|
||||||
|
UnexpectedNodeTypeError = errors.UnexpectedNodeTypeError
|
||||||
|
Error = errors.Error
|
||||||
|
)
|
||||||
|
|
||||||
|
func ErrUnsupportedHeadPositionType(node ast.Node) error {
|
||||||
|
return fmt.Errorf("unsupported comment head position for %s", node.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrUnsupportedLinePositionType(node ast.Node) error {
|
||||||
|
return fmt.Errorf("unsupported comment line position for %s", node.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrUnsupportedFootPositionType(node ast.Node) error {
|
||||||
|
return fmt.Errorf("unsupported comment foot position for %s", node.Type())
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInvalidQueryError whether err is ErrInvalidQuery or not.
|
||||||
|
func IsInvalidQueryError(err error) bool {
|
||||||
|
return errors.Is(err, ErrInvalidQuery)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInvalidPathError whether err is ErrInvalidPath or not.
|
||||||
|
func IsInvalidPathError(err error) bool {
|
||||||
|
return errors.Is(err, ErrInvalidPath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInvalidPathStringError whether err is ErrInvalidPathString or not.
|
||||||
|
func IsInvalidPathStringError(err error) bool {
|
||||||
|
return errors.Is(err, ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsNotFoundNodeError whether err is ErrNotFoundNode or not.
|
||||||
|
func IsNotFoundNodeError(err error) bool {
|
||||||
|
return errors.Is(err, ErrNotFoundNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInvalidTokenTypeError whether err is ast.ErrInvalidTokenType or not.
|
||||||
|
func IsInvalidTokenTypeError(err error) bool {
|
||||||
|
return errors.Is(err, ast.ErrInvalidTokenType)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInvalidAnchorNameError whether err is ast.ErrInvalidAnchorName or not.
|
||||||
|
func IsInvalidAnchorNameError(err error) bool {
|
||||||
|
return errors.Is(err, ast.ErrInvalidAnchorName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsInvalidAliasNameError whether err is ast.ErrInvalidAliasName or not.
|
||||||
|
func IsInvalidAliasNameError(err error) bool {
|
||||||
|
return errors.Is(err, ast.ErrInvalidAliasName)
|
||||||
|
}
|
||||||
246
vendor/github.com/goccy/go-yaml/internal/errors/error.go
generated
vendored
Normal file
246
vendor/github.com/goccy/go-yaml/internal/errors/error.go
generated
vendored
Normal file
@@ -0,0 +1,246 @@
|
|||||||
|
package errors
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml/ast"
|
||||||
|
"github.com/goccy/go-yaml/printer"
|
||||||
|
"github.com/goccy/go-yaml/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
As = errors.As
|
||||||
|
Is = errors.Is
|
||||||
|
New = errors.New
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
defaultFormatColor = false
|
||||||
|
defaultIncludeSource = true
|
||||||
|
)
|
||||||
|
|
||||||
|
type Error interface {
|
||||||
|
error
|
||||||
|
GetToken() *token.Token
|
||||||
|
GetMessage() string
|
||||||
|
FormatError(bool, bool) string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
_ Error = new(SyntaxError)
|
||||||
|
_ Error = new(TypeError)
|
||||||
|
_ Error = new(OverflowError)
|
||||||
|
_ Error = new(DuplicateKeyError)
|
||||||
|
_ Error = new(UnknownFieldError)
|
||||||
|
_ Error = new(UnexpectedNodeTypeError)
|
||||||
|
)
|
||||||
|
|
||||||
|
type SyntaxError struct {
|
||||||
|
Message string
|
||||||
|
Token *token.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
type TypeError struct {
|
||||||
|
DstType reflect.Type
|
||||||
|
SrcType reflect.Type
|
||||||
|
StructFieldName *string
|
||||||
|
Token *token.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
type OverflowError struct {
|
||||||
|
DstType reflect.Type
|
||||||
|
SrcNum string
|
||||||
|
Token *token.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
type DuplicateKeyError struct {
|
||||||
|
Message string
|
||||||
|
Token *token.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnknownFieldError struct {
|
||||||
|
Message string
|
||||||
|
Token *token.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
type UnexpectedNodeTypeError struct {
|
||||||
|
Actual ast.NodeType
|
||||||
|
Expected ast.NodeType
|
||||||
|
Token *token.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrSyntax create syntax error instance with message and token
|
||||||
|
func ErrSyntax(msg string, tk *token.Token) *SyntaxError {
|
||||||
|
return &SyntaxError{
|
||||||
|
Message: msg,
|
||||||
|
Token: tk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrOverflow creates an overflow error instance with message and a token.
|
||||||
|
func ErrOverflow(dstType reflect.Type, num string, tk *token.Token) *OverflowError {
|
||||||
|
return &OverflowError{
|
||||||
|
DstType: dstType,
|
||||||
|
SrcNum: num,
|
||||||
|
Token: tk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrTypeMismatch cerates an type mismatch error instance with token.
|
||||||
|
func ErrTypeMismatch(dstType, srcType reflect.Type, token *token.Token) *TypeError {
|
||||||
|
return &TypeError{
|
||||||
|
DstType: dstType,
|
||||||
|
SrcType: srcType,
|
||||||
|
Token: token,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrDuplicateKey creates an duplicate key error instance with token.
|
||||||
|
func ErrDuplicateKey(msg string, tk *token.Token) *DuplicateKeyError {
|
||||||
|
return &DuplicateKeyError{
|
||||||
|
Message: msg,
|
||||||
|
Token: tk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrUnknownField creates an unknown field error instance with token.
|
||||||
|
func ErrUnknownField(msg string, tk *token.Token) *UnknownFieldError {
|
||||||
|
return &UnknownFieldError{
|
||||||
|
Message: msg,
|
||||||
|
Token: tk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrUnexpectedNodeType(actual, expected ast.NodeType, tk *token.Token) *UnexpectedNodeTypeError {
|
||||||
|
return &UnexpectedNodeTypeError{
|
||||||
|
Actual: actual,
|
||||||
|
Expected: expected,
|
||||||
|
Token: tk,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SyntaxError) GetMessage() string {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SyntaxError) GetToken() *token.Token {
|
||||||
|
return e.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SyntaxError) Error() string {
|
||||||
|
return e.FormatError(defaultFormatColor, defaultIncludeSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *SyntaxError) FormatError(colored, inclSource bool) string {
|
||||||
|
return FormatError(e.Message, e.Token, colored, inclSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *OverflowError) GetMessage() string {
|
||||||
|
return e.msg()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *OverflowError) GetToken() *token.Token {
|
||||||
|
return e.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *OverflowError) Error() string {
|
||||||
|
return e.FormatError(defaultFormatColor, defaultIncludeSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *OverflowError) FormatError(colored, inclSource bool) string {
|
||||||
|
return FormatError(e.msg(), e.Token, colored, inclSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *OverflowError) msg() string {
|
||||||
|
return fmt.Sprintf("cannot unmarshal %s into Go value of type %s ( overflow )", e.SrcNum, e.DstType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TypeError) msg() string {
|
||||||
|
if e.StructFieldName != nil {
|
||||||
|
return fmt.Sprintf("cannot unmarshal %s into Go struct field %s of type %s", e.SrcType, *e.StructFieldName, e.DstType)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("cannot unmarshal %s into Go value of type %s", e.SrcType, e.DstType)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TypeError) GetMessage() string {
|
||||||
|
return e.msg()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TypeError) GetToken() *token.Token {
|
||||||
|
return e.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TypeError) Error() string {
|
||||||
|
return e.FormatError(defaultFormatColor, defaultIncludeSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *TypeError) FormatError(colored, inclSource bool) string {
|
||||||
|
return FormatError(e.msg(), e.Token, colored, inclSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DuplicateKeyError) GetMessage() string {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DuplicateKeyError) GetToken() *token.Token {
|
||||||
|
return e.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DuplicateKeyError) Error() string {
|
||||||
|
return e.FormatError(defaultFormatColor, defaultIncludeSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *DuplicateKeyError) FormatError(colored, inclSource bool) string {
|
||||||
|
return FormatError(e.Message, e.Token, colored, inclSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnknownFieldError) GetMessage() string {
|
||||||
|
return e.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnknownFieldError) GetToken() *token.Token {
|
||||||
|
return e.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnknownFieldError) Error() string {
|
||||||
|
return e.FormatError(defaultFormatColor, defaultIncludeSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnknownFieldError) FormatError(colored, inclSource bool) string {
|
||||||
|
return FormatError(e.Message, e.Token, colored, inclSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnexpectedNodeTypeError) GetMessage() string {
|
||||||
|
return e.msg()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnexpectedNodeTypeError) GetToken() *token.Token {
|
||||||
|
return e.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnexpectedNodeTypeError) Error() string {
|
||||||
|
return e.FormatError(defaultFormatColor, defaultIncludeSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnexpectedNodeTypeError) FormatError(colored, inclSource bool) string {
|
||||||
|
return FormatError(e.msg(), e.Token, colored, inclSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UnexpectedNodeTypeError) msg() string {
|
||||||
|
return fmt.Sprintf("%s was used where %s is expected", e.Actual.YAMLName(), e.Expected.YAMLName())
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatError(errMsg string, token *token.Token, colored, inclSource bool) string {
|
||||||
|
var pp printer.Printer
|
||||||
|
if token == nil {
|
||||||
|
return pp.PrintErrorMessage(errMsg, colored)
|
||||||
|
}
|
||||||
|
pos := fmt.Sprintf("[%d:%d] ", token.Position.Line, token.Position.Column)
|
||||||
|
msg := pp.PrintErrorMessage(fmt.Sprintf("%s%s", pos, errMsg), colored)
|
||||||
|
if inclSource {
|
||||||
|
msg += "\n" + pp.PrintErrorToken(token, colored)
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
541
vendor/github.com/goccy/go-yaml/internal/format/format.go
generated
vendored
Normal file
541
vendor/github.com/goccy/go-yaml/internal/format/format.go
generated
vendored
Normal file
@@ -0,0 +1,541 @@
|
|||||||
|
package format
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml/ast"
|
||||||
|
"github.com/goccy/go-yaml/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func FormatNodeWithResolvedAlias(n ast.Node, anchorNodeMap map[string]ast.Node) string {
|
||||||
|
tk := getFirstToken(n)
|
||||||
|
if tk == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
formatter := newFormatter(tk, hasComment(n))
|
||||||
|
formatter.anchorNodeMap = anchorNodeMap
|
||||||
|
return formatter.format(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatNode(n ast.Node) string {
|
||||||
|
tk := getFirstToken(n)
|
||||||
|
if tk == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return newFormatter(tk, hasComment(n)).format(n)
|
||||||
|
}
|
||||||
|
|
||||||
|
func FormatFile(file *ast.File) string {
|
||||||
|
if len(file.Docs) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
tk := getFirstToken(file.Docs[0])
|
||||||
|
if tk == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return newFormatter(tk, hasCommentFile(file)).formatFile(file)
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasCommentFile(f *ast.File) bool {
|
||||||
|
for _, doc := range f.Docs {
|
||||||
|
if hasComment(doc.Body) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func hasComment(n ast.Node) bool {
|
||||||
|
if n == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
switch nn := n.(type) {
|
||||||
|
case *ast.DocumentNode:
|
||||||
|
return hasComment(nn.Body)
|
||||||
|
case *ast.NullNode:
|
||||||
|
return nn.Comment != nil
|
||||||
|
case *ast.BoolNode:
|
||||||
|
return nn.Comment != nil
|
||||||
|
case *ast.IntegerNode:
|
||||||
|
return nn.Comment != nil
|
||||||
|
case *ast.FloatNode:
|
||||||
|
return nn.Comment != nil
|
||||||
|
case *ast.StringNode:
|
||||||
|
return nn.Comment != nil
|
||||||
|
case *ast.InfinityNode:
|
||||||
|
return nn.Comment != nil
|
||||||
|
case *ast.NanNode:
|
||||||
|
return nn.Comment != nil
|
||||||
|
case *ast.LiteralNode:
|
||||||
|
return nn.Comment != nil
|
||||||
|
case *ast.DirectiveNode:
|
||||||
|
if nn.Comment != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, value := range nn.Values {
|
||||||
|
if hasComment(value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.TagNode:
|
||||||
|
if nn.Comment != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return hasComment(nn.Value)
|
||||||
|
case *ast.MappingNode:
|
||||||
|
if nn.Comment != nil || nn.FootComment != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, value := range nn.Values {
|
||||||
|
if value.Comment != nil || value.FootComment != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if hasComment(value.Key) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if hasComment(value.Value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.MappingKeyNode:
|
||||||
|
return nn.Comment != nil
|
||||||
|
case *ast.MergeKeyNode:
|
||||||
|
return nn.Comment != nil
|
||||||
|
case *ast.SequenceNode:
|
||||||
|
if nn.Comment != nil || nn.FootComment != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
for _, entry := range nn.Entries {
|
||||||
|
if entry.Comment != nil || entry.HeadComment != nil || entry.LineComment != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if hasComment(entry.Value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.AnchorNode:
|
||||||
|
if nn.Comment != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if hasComment(nn.Name) || hasComment(nn.Value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
case *ast.AliasNode:
|
||||||
|
if nn.Comment != nil {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if hasComment(nn.Value) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFirstToken(n ast.Node) *token.Token {
|
||||||
|
if n == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch nn := n.(type) {
|
||||||
|
case *ast.DocumentNode:
|
||||||
|
if nn.Start != nil {
|
||||||
|
return nn.Start
|
||||||
|
}
|
||||||
|
return getFirstToken(nn.Body)
|
||||||
|
case *ast.NullNode:
|
||||||
|
return nn.Token
|
||||||
|
case *ast.BoolNode:
|
||||||
|
return nn.Token
|
||||||
|
case *ast.IntegerNode:
|
||||||
|
return nn.Token
|
||||||
|
case *ast.FloatNode:
|
||||||
|
return nn.Token
|
||||||
|
case *ast.StringNode:
|
||||||
|
return nn.Token
|
||||||
|
case *ast.InfinityNode:
|
||||||
|
return nn.Token
|
||||||
|
case *ast.NanNode:
|
||||||
|
return nn.Token
|
||||||
|
case *ast.LiteralNode:
|
||||||
|
return nn.Start
|
||||||
|
case *ast.DirectiveNode:
|
||||||
|
return nn.Start
|
||||||
|
case *ast.TagNode:
|
||||||
|
return nn.Start
|
||||||
|
case *ast.MappingNode:
|
||||||
|
if nn.IsFlowStyle {
|
||||||
|
return nn.Start
|
||||||
|
}
|
||||||
|
if len(nn.Values) == 0 {
|
||||||
|
return nn.Start
|
||||||
|
}
|
||||||
|
return getFirstToken(nn.Values[0].Key)
|
||||||
|
case *ast.MappingKeyNode:
|
||||||
|
return nn.Start
|
||||||
|
case *ast.MergeKeyNode:
|
||||||
|
return nn.Token
|
||||||
|
case *ast.SequenceNode:
|
||||||
|
return nn.Start
|
||||||
|
case *ast.AnchorNode:
|
||||||
|
return nn.Start
|
||||||
|
case *ast.AliasNode:
|
||||||
|
return nn.Start
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type Formatter struct {
|
||||||
|
existsComment bool
|
||||||
|
tokenToOriginMap map[*token.Token]string
|
||||||
|
anchorNodeMap map[string]ast.Node
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFormatter(tk *token.Token, existsComment bool) *Formatter {
|
||||||
|
tokenToOriginMap := make(map[*token.Token]string)
|
||||||
|
for tk.Prev != nil {
|
||||||
|
tk = tk.Prev
|
||||||
|
}
|
||||||
|
tokenToOriginMap[tk] = tk.Origin
|
||||||
|
|
||||||
|
var origin string
|
||||||
|
for tk.Next != nil {
|
||||||
|
tk = tk.Next
|
||||||
|
if tk.Type == token.CommentType {
|
||||||
|
origin += strings.Repeat("\n", strings.Count(normalizeNewLineChars(tk.Origin), "\n"))
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
origin += tk.Origin
|
||||||
|
tokenToOriginMap[tk] = origin
|
||||||
|
origin = ""
|
||||||
|
}
|
||||||
|
return &Formatter{
|
||||||
|
existsComment: existsComment,
|
||||||
|
tokenToOriginMap: tokenToOriginMap,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIndentNumByFirstLineToken(tk *token.Token) int {
|
||||||
|
defaultIndent := tk.Position.Column - 1
|
||||||
|
|
||||||
|
// key: value
|
||||||
|
// ^
|
||||||
|
// next
|
||||||
|
if tk.Type == token.SequenceEntryType {
|
||||||
|
// If the current token is the sequence entry.
|
||||||
|
// the indent is calculated from the column value of the current token.
|
||||||
|
return defaultIndent
|
||||||
|
}
|
||||||
|
|
||||||
|
// key: value
|
||||||
|
// ^
|
||||||
|
// next
|
||||||
|
if tk.Next != nil && tk.Next.Type == token.MappingValueType {
|
||||||
|
// If the current token is the key in the mapping-value,
|
||||||
|
// the indent is calculated from the column value of the current token.
|
||||||
|
return defaultIndent
|
||||||
|
}
|
||||||
|
|
||||||
|
if tk.Prev == nil {
|
||||||
|
return defaultIndent
|
||||||
|
}
|
||||||
|
prev := tk.Prev
|
||||||
|
|
||||||
|
// key: value
|
||||||
|
// ^
|
||||||
|
// prev
|
||||||
|
if prev.Type == token.MappingValueType {
|
||||||
|
// If the current token is the value in the mapping-value,
|
||||||
|
// the indent is calculated from the column value of the key two steps back.
|
||||||
|
if prev.Prev == nil {
|
||||||
|
return defaultIndent
|
||||||
|
}
|
||||||
|
return prev.Prev.Position.Column - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// - value
|
||||||
|
// ^
|
||||||
|
// prev
|
||||||
|
if prev.Type == token.SequenceEntryType {
|
||||||
|
// If the value is not a mapping-value and the previous token was a sequence entry,
|
||||||
|
// the indent is calculated using the column value of the sequence entry token.
|
||||||
|
return prev.Position.Column - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
return defaultIndent
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) format(n ast.Node) string {
|
||||||
|
return f.trimSpacePrefix(
|
||||||
|
f.trimIndentSpace(
|
||||||
|
getIndentNumByFirstLineToken(getFirstToken(n)),
|
||||||
|
f.trimNewLineCharPrefix(f.formatNode(n)),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatFile(file *ast.File) string {
|
||||||
|
if len(file.Docs) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var ret string
|
||||||
|
for _, doc := range file.Docs {
|
||||||
|
ret += f.formatDocument(doc)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) origin(tk *token.Token) string {
|
||||||
|
if tk == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if f.existsComment {
|
||||||
|
return tk.Origin
|
||||||
|
}
|
||||||
|
return f.tokenToOriginMap[tk]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatDocument(n *ast.DocumentNode) string {
|
||||||
|
return f.origin(n.Start) + f.formatNode(n.Body) + f.origin(n.End)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatNull(n *ast.NullNode) string {
|
||||||
|
return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatString(n *ast.StringNode) string {
|
||||||
|
return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatInteger(n *ast.IntegerNode) string {
|
||||||
|
return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatFloat(n *ast.FloatNode) string {
|
||||||
|
return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatBool(n *ast.BoolNode) string {
|
||||||
|
return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatInfinity(n *ast.InfinityNode) string {
|
||||||
|
return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatNan(n *ast.NanNode) string {
|
||||||
|
return f.origin(n.Token) + f.formatCommentGroup(n.Comment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatLiteral(n *ast.LiteralNode) string {
|
||||||
|
return f.origin(n.Start) + f.formatCommentGroup(n.Comment) + f.origin(n.Value.Token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatMergeKey(n *ast.MergeKeyNode) string {
|
||||||
|
return f.origin(n.Token)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatMappingValue(n *ast.MappingValueNode) string {
|
||||||
|
return f.formatCommentGroup(n.Comment) +
|
||||||
|
f.origin(n.Key.GetToken()) + ":" + f.formatCommentGroup(n.Key.GetComment()) + f.formatNode(n.Value) +
|
||||||
|
f.formatCommentGroup(n.FootComment)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatDirective(n *ast.DirectiveNode) string {
|
||||||
|
ret := f.origin(n.Start) + f.formatNode(n.Name)
|
||||||
|
for _, val := range n.Values {
|
||||||
|
ret += f.formatNode(val)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatMapping(n *ast.MappingNode) string {
|
||||||
|
var ret string
|
||||||
|
if n.IsFlowStyle {
|
||||||
|
ret = f.origin(n.Start)
|
||||||
|
} else {
|
||||||
|
ret += f.formatCommentGroup(n.Comment)
|
||||||
|
}
|
||||||
|
for _, value := range n.Values {
|
||||||
|
if value.CollectEntry != nil {
|
||||||
|
ret += f.origin(value.CollectEntry)
|
||||||
|
}
|
||||||
|
ret += f.formatMappingValue(value)
|
||||||
|
}
|
||||||
|
if n.IsFlowStyle {
|
||||||
|
ret += f.origin(n.End)
|
||||||
|
ret += f.formatCommentGroup(n.Comment)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatTag(n *ast.TagNode) string {
|
||||||
|
return f.origin(n.Start) + f.formatNode(n.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatMappingKey(n *ast.MappingKeyNode) string {
|
||||||
|
return f.origin(n.Start) + f.formatNode(n.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatSequence(n *ast.SequenceNode) string {
|
||||||
|
var ret string
|
||||||
|
if n.IsFlowStyle {
|
||||||
|
ret = f.origin(n.Start)
|
||||||
|
} else {
|
||||||
|
// add head comment.
|
||||||
|
ret += f.formatCommentGroup(n.Comment)
|
||||||
|
}
|
||||||
|
for _, entry := range n.Entries {
|
||||||
|
ret += f.formatNode(entry)
|
||||||
|
}
|
||||||
|
if n.IsFlowStyle {
|
||||||
|
ret += f.origin(n.End)
|
||||||
|
ret += f.formatCommentGroup(n.Comment)
|
||||||
|
}
|
||||||
|
ret += f.formatCommentGroup(n.FootComment)
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatSequenceEntry(n *ast.SequenceEntryNode) string {
|
||||||
|
return f.formatCommentGroup(n.HeadComment) + f.origin(n.Start) + f.formatCommentGroup(n.LineComment) + f.formatNode(n.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatAnchor(n *ast.AnchorNode) string {
|
||||||
|
return f.origin(n.Start) + f.formatNode(n.Name) + f.formatNode(n.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatAlias(n *ast.AliasNode) string {
|
||||||
|
if f.anchorNodeMap != nil {
|
||||||
|
anchorName := n.Value.GetToken().Value
|
||||||
|
node := f.anchorNodeMap[anchorName]
|
||||||
|
if node != nil {
|
||||||
|
formatted := f.formatNode(node)
|
||||||
|
// If formatted text contains newline characters, indentation needs to be considered.
|
||||||
|
if strings.Contains(formatted, "\n") {
|
||||||
|
// If the first character is not a newline, the first line should be output without indentation.
|
||||||
|
isIgnoredFirstLine := !strings.HasPrefix(formatted, "\n")
|
||||||
|
formatted = f.addIndentSpace(n.GetToken().Position.IndentNum, formatted, isIgnoredFirstLine)
|
||||||
|
}
|
||||||
|
return formatted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return f.origin(n.Start) + f.formatNode(n.Value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatNode(n ast.Node) string {
|
||||||
|
switch nn := n.(type) {
|
||||||
|
case *ast.DocumentNode:
|
||||||
|
return f.formatDocument(nn)
|
||||||
|
case *ast.NullNode:
|
||||||
|
return f.formatNull(nn)
|
||||||
|
case *ast.BoolNode:
|
||||||
|
return f.formatBool(nn)
|
||||||
|
case *ast.IntegerNode:
|
||||||
|
return f.formatInteger(nn)
|
||||||
|
case *ast.FloatNode:
|
||||||
|
return f.formatFloat(nn)
|
||||||
|
case *ast.StringNode:
|
||||||
|
return f.formatString(nn)
|
||||||
|
case *ast.InfinityNode:
|
||||||
|
return f.formatInfinity(nn)
|
||||||
|
case *ast.NanNode:
|
||||||
|
return f.formatNan(nn)
|
||||||
|
case *ast.LiteralNode:
|
||||||
|
return f.formatLiteral(nn)
|
||||||
|
case *ast.DirectiveNode:
|
||||||
|
return f.formatDirective(nn)
|
||||||
|
case *ast.TagNode:
|
||||||
|
return f.formatTag(nn)
|
||||||
|
case *ast.MappingNode:
|
||||||
|
return f.formatMapping(nn)
|
||||||
|
case *ast.MappingKeyNode:
|
||||||
|
return f.formatMappingKey(nn)
|
||||||
|
case *ast.MappingValueNode:
|
||||||
|
return f.formatMappingValue(nn)
|
||||||
|
case *ast.MergeKeyNode:
|
||||||
|
return f.formatMergeKey(nn)
|
||||||
|
case *ast.SequenceNode:
|
||||||
|
return f.formatSequence(nn)
|
||||||
|
case *ast.SequenceEntryNode:
|
||||||
|
return f.formatSequenceEntry(nn)
|
||||||
|
case *ast.AnchorNode:
|
||||||
|
return f.formatAnchor(nn)
|
||||||
|
case *ast.AliasNode:
|
||||||
|
return f.formatAlias(nn)
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatCommentGroup(g *ast.CommentGroupNode) string {
|
||||||
|
if g == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
var ret string
|
||||||
|
for _, cm := range g.Comments {
|
||||||
|
ret += f.formatComment(cm)
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) formatComment(n *ast.CommentNode) string {
|
||||||
|
if n == nil {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return n.Token.Origin
|
||||||
|
}
|
||||||
|
|
||||||
|
// nolint: unused
|
||||||
|
func (f *Formatter) formatIndent(col int) string {
|
||||||
|
if col <= 1 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
return strings.Repeat(" ", col-1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) trimNewLineCharPrefix(v string) string {
|
||||||
|
return strings.TrimLeftFunc(v, func(r rune) bool {
|
||||||
|
return r == '\n' || r == '\r'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) trimSpacePrefix(v string) string {
|
||||||
|
return strings.TrimLeftFunc(v, func(r rune) bool {
|
||||||
|
return r == ' '
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) trimIndentSpace(trimIndentNum int, v string) string {
|
||||||
|
if trimIndentNum == 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
lines := strings.Split(normalizeNewLineChars(v), "\n")
|
||||||
|
out := make([]string, 0, len(lines))
|
||||||
|
for _, line := range lines {
|
||||||
|
var cnt int
|
||||||
|
out = append(out, strings.TrimLeftFunc(line, func(r rune) bool {
|
||||||
|
cnt++
|
||||||
|
return r == ' ' && cnt <= trimIndentNum
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
return strings.Join(out, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Formatter) addIndentSpace(indentNum int, v string, isIgnoredFirstLine bool) string {
|
||||||
|
if indentNum == 0 {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
indent := strings.Repeat(" ", indentNum)
|
||||||
|
lines := strings.Split(normalizeNewLineChars(v), "\n")
|
||||||
|
out := make([]string, 0, len(lines))
|
||||||
|
for idx, line := range lines {
|
||||||
|
if line == "" || (isIgnoredFirstLine && idx == 0) {
|
||||||
|
out = append(out, line)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out = append(out, indent+line)
|
||||||
|
}
|
||||||
|
return strings.Join(out, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// normalizeNewLineChars normalize CRLF and CR to LF.
|
||||||
|
func normalizeNewLineChars(v string) string {
|
||||||
|
return strings.ReplaceAll(strings.ReplaceAll(v, "\r\n", "\n"), "\r", "\n")
|
||||||
|
}
|
||||||
23
vendor/github.com/goccy/go-yaml/lexer/lexer.go
generated
vendored
Normal file
23
vendor/github.com/goccy/go-yaml/lexer/lexer.go
generated
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
package lexer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml/scanner"
|
||||||
|
"github.com/goccy/go-yaml/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Tokenize split to token instances from string
|
||||||
|
func Tokenize(src string) token.Tokens {
|
||||||
|
var s scanner.Scanner
|
||||||
|
s.Init(src)
|
||||||
|
var tokens token.Tokens
|
||||||
|
for {
|
||||||
|
subTokens, err := s.Scan()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tokens.Add(subTokens...)
|
||||||
|
}
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
352
vendor/github.com/goccy/go-yaml/option.go
generated
vendored
Normal file
352
vendor/github.com/goccy/go-yaml/option.go
generated
vendored
Normal file
@@ -0,0 +1,352 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml/ast"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DecodeOption functional option type for Decoder
|
||||||
|
type DecodeOption func(d *Decoder) error
|
||||||
|
|
||||||
|
// ReferenceReaders pass to Decoder that reference to anchor defined by passed readers
|
||||||
|
func ReferenceReaders(readers ...io.Reader) DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
d.referenceReaders = append(d.referenceReaders, readers...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReferenceFiles pass to Decoder that reference to anchor defined by passed files
|
||||||
|
func ReferenceFiles(files ...string) DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
d.referenceFiles = files
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReferenceDirs pass to Decoder that reference to anchor defined by files under the passed dirs
|
||||||
|
func ReferenceDirs(dirs ...string) DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
d.referenceDirs = dirs
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RecursiveDir search yaml file recursively from passed dirs by ReferenceDirs option
|
||||||
|
func RecursiveDir(isRecursive bool) DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
d.isRecursiveDir = isRecursive
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Validator set StructValidator instance to Decoder
|
||||||
|
func Validator(v StructValidator) DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
d.validator = v
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Strict enable DisallowUnknownField
|
||||||
|
func Strict() DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
d.disallowUnknownField = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// DisallowUnknownField causes the Decoder to return an error when the destination
|
||||||
|
// is a struct and the input contains object keys which do not match any
|
||||||
|
// non-ignored, exported fields in the destination.
|
||||||
|
func DisallowUnknownField() DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
d.disallowUnknownField = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowFieldPrefixes, when paired with [DisallowUnknownField], allows fields
|
||||||
|
// with the specified prefixes to bypass the unknown field check.
|
||||||
|
func AllowFieldPrefixes(prefixes ...string) DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
d.allowedFieldPrefixes = append(d.allowedFieldPrefixes, prefixes...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllowDuplicateMapKey ignore syntax error when mapping keys that are duplicates.
|
||||||
|
func AllowDuplicateMapKey() DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
d.allowDuplicateMapKey = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseOrderedMap can be interpreted as a map,
|
||||||
|
// and uses MapSlice ( ordered map ) aggressively if there is no type specification
|
||||||
|
func UseOrderedMap() DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
d.useOrderedMap = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseJSONUnmarshaler if neither `BytesUnmarshaler` nor `InterfaceUnmarshaler` is implemented
|
||||||
|
// and `UnmashalJSON([]byte)error` is implemented, convert the argument from `YAML` to `JSON` and then call it.
|
||||||
|
func UseJSONUnmarshaler() DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
d.useJSONUnmarshaler = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomUnmarshaler overrides any decoding process for the type specified in generics.
|
||||||
|
//
|
||||||
|
// NOTE: If RegisterCustomUnmarshaler and CustomUnmarshaler of DecodeOption are specified for the same type,
|
||||||
|
// the CustomUnmarshaler specified in DecodeOption takes precedence.
|
||||||
|
func CustomUnmarshaler[T any](unmarshaler func(*T, []byte) error) DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
var typ *T
|
||||||
|
d.customUnmarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}, b []byte) error {
|
||||||
|
return unmarshaler(v.(*T), b)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomUnmarshalerContext overrides any decoding process for the type specified in generics.
|
||||||
|
// Similar to CustomUnmarshaler, but allows passing a context to the unmarshaler function.
|
||||||
|
func CustomUnmarshalerContext[T any](unmarshaler func(context.Context, *T, []byte) error) DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
var typ *T
|
||||||
|
d.customUnmarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}, b []byte) error {
|
||||||
|
return unmarshaler(ctx, v.(*T), b)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// EncodeOption functional option type for Encoder
|
||||||
|
type EncodeOption func(e *Encoder) error
|
||||||
|
|
||||||
|
// Indent change indent number
|
||||||
|
func Indent(spaces int) EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
e.indentNum = spaces
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndentSequence causes sequence values to be indented the same value as Indent
|
||||||
|
func IndentSequence(indent bool) EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
e.indentSequence = indent
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseSingleQuote determines if single or double quotes should be preferred for strings.
|
||||||
|
func UseSingleQuote(sq bool) EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
e.singleQuote = sq
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flow encoding by flow style
|
||||||
|
func Flow(isFlowStyle bool) EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
e.isFlowStyle = isFlowStyle
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithSmartAnchor when multiple map values share the same pointer,
|
||||||
|
// an anchor is automatically assigned to the first occurrence, and aliases are used for subsequent elements.
|
||||||
|
// The map key name is used as the anchor name by default.
|
||||||
|
// If key names conflict, a suffix is automatically added to avoid collisions.
|
||||||
|
// This is an experimental feature and cannot be used simultaneously with anchor tags.
|
||||||
|
func WithSmartAnchor() EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
e.enableSmartAnchor = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseLiteralStyleIfMultiline causes encoding multiline strings with a literal syntax,
|
||||||
|
// no matter what characters they include
|
||||||
|
func UseLiteralStyleIfMultiline(useLiteralStyleIfMultiline bool) EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
e.useLiteralStyleIfMultiline = useLiteralStyleIfMultiline
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSON encode in JSON format
|
||||||
|
func JSON() EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
e.isJSONStyle = true
|
||||||
|
e.isFlowStyle = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalAnchor call back if encoder find an anchor during encoding
|
||||||
|
func MarshalAnchor(callback func(*ast.AnchorNode, interface{}) error) EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
e.anchorCallback = callback
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UseJSONMarshaler if neither `BytesMarshaler` nor `InterfaceMarshaler`
|
||||||
|
// nor `encoding.TextMarshaler` is implemented and `MarshalJSON()([]byte, error)` is implemented,
|
||||||
|
// call `MarshalJSON` to convert the returned `JSON` to `YAML` for processing.
|
||||||
|
func UseJSONMarshaler() EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
e.useJSONMarshaler = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomMarshaler overrides any encoding process for the type specified in generics.
|
||||||
|
//
|
||||||
|
// NOTE: If type T implements MarshalYAML for pointer receiver, the type specified in CustomMarshaler must be *T.
|
||||||
|
// If RegisterCustomMarshaler and CustomMarshaler of EncodeOption are specified for the same type,
|
||||||
|
// the CustomMarshaler specified in EncodeOption takes precedence.
|
||||||
|
func CustomMarshaler[T any](marshaler func(T) ([]byte, error)) EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
var typ T
|
||||||
|
e.customMarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}) ([]byte, error) {
|
||||||
|
return marshaler(v.(T))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CustomMarshalerContext overrides any encoding process for the type specified in generics.
|
||||||
|
// Similar to CustomMarshaler, but allows passing a context to the marshaler function.
|
||||||
|
func CustomMarshalerContext[T any](marshaler func(context.Context, T) ([]byte, error)) EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
var typ T
|
||||||
|
e.customMarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}) ([]byte, error) {
|
||||||
|
return marshaler(ctx, v.(T))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// AutoInt automatically converts floating-point numbers to integers when the fractional part is zero.
|
||||||
|
// For example, a value of 1.0 will be encoded as 1.
|
||||||
|
func AutoInt() EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
e.autoInt = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OmitEmpty behaves in the same way as the interpretation of the omitempty tag in the encoding/json library.
|
||||||
|
// set on all the fields.
|
||||||
|
// In the current implementation, the omitempty tag is not implemented in the same way as encoding/json,
|
||||||
|
// so please specify this option if you expect the same behavior.
|
||||||
|
func OmitEmpty() EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
e.omitEmpty = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// OmitZero forces the encoder to assume an `omitzero` struct tag is
|
||||||
|
// set on all the fields. See `Marshal` commentary for the `omitzero` tag logic.
|
||||||
|
func OmitZero() EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
e.omitZero = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommentPosition type of the position for comment.
|
||||||
|
type CommentPosition int
|
||||||
|
|
||||||
|
const (
|
||||||
|
CommentHeadPosition CommentPosition = CommentPosition(iota)
|
||||||
|
CommentLinePosition
|
||||||
|
CommentFootPosition
|
||||||
|
)
|
||||||
|
|
||||||
|
func (p CommentPosition) String() string {
|
||||||
|
switch p {
|
||||||
|
case CommentHeadPosition:
|
||||||
|
return "Head"
|
||||||
|
case CommentLinePosition:
|
||||||
|
return "Line"
|
||||||
|
case CommentFootPosition:
|
||||||
|
return "Foot"
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// LineComment create a one-line comment for CommentMap.
|
||||||
|
func LineComment(text string) *Comment {
|
||||||
|
return &Comment{
|
||||||
|
Texts: []string{text},
|
||||||
|
Position: CommentLinePosition,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HeadComment create a multiline comment for CommentMap.
|
||||||
|
func HeadComment(texts ...string) *Comment {
|
||||||
|
return &Comment{
|
||||||
|
Texts: texts,
|
||||||
|
Position: CommentHeadPosition,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// FootComment create a multiline comment for CommentMap.
|
||||||
|
func FootComment(texts ...string) *Comment {
|
||||||
|
return &Comment{
|
||||||
|
Texts: texts,
|
||||||
|
Position: CommentFootPosition,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Comment raw data for comment.
|
||||||
|
type Comment struct {
|
||||||
|
Texts []string
|
||||||
|
Position CommentPosition
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommentMap map of the position of the comment and the comment information.
|
||||||
|
type CommentMap map[string][]*Comment
|
||||||
|
|
||||||
|
// WithComment add a comment using the location and text information given in the CommentMap.
|
||||||
|
func WithComment(cm CommentMap) EncodeOption {
|
||||||
|
return func(e *Encoder) error {
|
||||||
|
commentMap := map[*Path][]*Comment{}
|
||||||
|
for k, v := range cm {
|
||||||
|
path, err := PathString(k)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
commentMap[path] = v
|
||||||
|
}
|
||||||
|
e.commentMap = commentMap
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// CommentToMap apply the position and content of comments in a YAML document to a CommentMap.
|
||||||
|
func CommentToMap(cm CommentMap) DecodeOption {
|
||||||
|
return func(d *Decoder) error {
|
||||||
|
if cm == nil {
|
||||||
|
return ErrInvalidCommentMapValue
|
||||||
|
}
|
||||||
|
d.toCommentMap = cm
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
28
vendor/github.com/goccy/go-yaml/parser/color.go
generated
vendored
Normal file
28
vendor/github.com/goccy/go-yaml/parser/color.go
generated
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
const (
|
||||||
|
colorFgHiBlack int = iota + 90
|
||||||
|
colorFgHiRed
|
||||||
|
colorFgHiGreen
|
||||||
|
colorFgHiYellow
|
||||||
|
colorFgHiBlue
|
||||||
|
colorFgHiMagenta
|
||||||
|
colorFgHiCyan
|
||||||
|
)
|
||||||
|
|
||||||
|
var colorTable = []int{
|
||||||
|
colorFgHiRed,
|
||||||
|
colorFgHiGreen,
|
||||||
|
colorFgHiYellow,
|
||||||
|
colorFgHiBlue,
|
||||||
|
colorFgHiMagenta,
|
||||||
|
colorFgHiCyan,
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorize(idx int, content string) string {
|
||||||
|
colorIdx := idx % len(colorTable)
|
||||||
|
color := colorTable[colorIdx]
|
||||||
|
return fmt.Sprintf("\x1b[1;%dm", color) + content + "\x1b[22;0m"
|
||||||
|
}
|
||||||
187
vendor/github.com/goccy/go-yaml/parser/context.go
generated
vendored
Normal file
187
vendor/github.com/goccy/go-yaml/parser/context.go
generated
vendored
Normal file
@@ -0,0 +1,187 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// context context at parsing
|
||||||
|
type context struct {
|
||||||
|
tokenRef *tokenRef
|
||||||
|
path string
|
||||||
|
isFlow bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type tokenRef struct {
|
||||||
|
tokens []*Token
|
||||||
|
size int
|
||||||
|
idx int
|
||||||
|
}
|
||||||
|
|
||||||
|
var pathSpecialChars = []string{
|
||||||
|
"$", "*", ".", "[", "]",
|
||||||
|
}
|
||||||
|
|
||||||
|
func containsPathSpecialChar(path string) bool {
|
||||||
|
for _, char := range pathSpecialChars {
|
||||||
|
if strings.Contains(path, char) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func normalizePath(path string) string {
|
||||||
|
if containsPathSpecialChar(path) {
|
||||||
|
return fmt.Sprintf("'%s'", path)
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) currentToken() *Token {
|
||||||
|
if c.tokenRef.idx >= c.tokenRef.size {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.tokenRef.tokens[c.tokenRef.idx]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) isComment() bool {
|
||||||
|
return c.currentToken().Type() == token.CommentType
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) nextToken() *Token {
|
||||||
|
if c.tokenRef.idx+1 >= c.tokenRef.size {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return c.tokenRef.tokens[c.tokenRef.idx+1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) nextNotCommentToken() *Token {
|
||||||
|
for i := c.tokenRef.idx + 1; i < c.tokenRef.size; i++ {
|
||||||
|
tk := c.tokenRef.tokens[i]
|
||||||
|
if tk.Type() == token.CommentType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) isTokenNotFound() bool {
|
||||||
|
return c.currentToken() == nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) withGroup(g *TokenGroup) *context {
|
||||||
|
ctx := *c
|
||||||
|
ctx.tokenRef = &tokenRef{
|
||||||
|
tokens: g.Tokens,
|
||||||
|
size: len(g.Tokens),
|
||||||
|
}
|
||||||
|
return &ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) withChild(path string) *context {
|
||||||
|
ctx := *c
|
||||||
|
ctx.path = c.path + "." + normalizePath(path)
|
||||||
|
return &ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) withIndex(idx uint) *context {
|
||||||
|
ctx := *c
|
||||||
|
ctx.path = c.path + "[" + fmt.Sprint(idx) + "]"
|
||||||
|
return &ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) withFlow(isFlow bool) *context {
|
||||||
|
ctx := *c
|
||||||
|
ctx.isFlow = isFlow
|
||||||
|
return &ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContext() *context {
|
||||||
|
return &context{
|
||||||
|
path: "$",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) goNext() {
|
||||||
|
ref := c.tokenRef
|
||||||
|
if ref.size <= ref.idx+1 {
|
||||||
|
ref.idx = ref.size
|
||||||
|
} else {
|
||||||
|
ref.idx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) next() bool {
|
||||||
|
return c.tokenRef.idx < c.tokenRef.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) insertNullToken(tk *Token) *Token {
|
||||||
|
nullToken := c.createImplicitNullToken(tk)
|
||||||
|
c.insertToken(nullToken)
|
||||||
|
c.goNext()
|
||||||
|
|
||||||
|
return nullToken
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) addNullValueToken(tk *Token) *Token {
|
||||||
|
nullToken := c.createImplicitNullToken(tk)
|
||||||
|
rawTk := nullToken.RawToken()
|
||||||
|
|
||||||
|
// add space for map or sequence value.
|
||||||
|
rawTk.Position.Column++
|
||||||
|
|
||||||
|
c.addToken(nullToken)
|
||||||
|
c.goNext()
|
||||||
|
|
||||||
|
return nullToken
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) createImplicitNullToken(base *Token) *Token {
|
||||||
|
pos := *(base.RawToken().Position)
|
||||||
|
pos.Column++
|
||||||
|
tk := token.New("null", " null", &pos)
|
||||||
|
tk.Type = token.ImplicitNullType
|
||||||
|
return &Token{Token: tk}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) insertToken(tk *Token) {
|
||||||
|
ref := c.tokenRef
|
||||||
|
idx := ref.idx
|
||||||
|
if ref.size < idx {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if ref.size == idx {
|
||||||
|
curToken := ref.tokens[ref.size-1]
|
||||||
|
tk.RawToken().Next = curToken.RawToken()
|
||||||
|
curToken.RawToken().Prev = tk.RawToken()
|
||||||
|
|
||||||
|
ref.tokens = append(ref.tokens, tk)
|
||||||
|
ref.size = len(ref.tokens)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
curToken := ref.tokens[idx]
|
||||||
|
tk.RawToken().Next = curToken.RawToken()
|
||||||
|
curToken.RawToken().Prev = tk.RawToken()
|
||||||
|
|
||||||
|
ref.tokens = append(ref.tokens[:idx+1], ref.tokens[idx:]...)
|
||||||
|
ref.tokens[idx] = tk
|
||||||
|
ref.size = len(ref.tokens)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *context) addToken(tk *Token) {
|
||||||
|
ref := c.tokenRef
|
||||||
|
lastTk := ref.tokens[ref.size-1]
|
||||||
|
if lastTk.Group != nil {
|
||||||
|
lastTk = lastTk.Group.Last()
|
||||||
|
}
|
||||||
|
lastTk.RawToken().Next = tk.RawToken()
|
||||||
|
tk.RawToken().Prev = lastTk.RawToken()
|
||||||
|
|
||||||
|
ref.tokens = append(ref.tokens, tk)
|
||||||
|
ref.size = len(ref.tokens)
|
||||||
|
}
|
||||||
257
vendor/github.com/goccy/go-yaml/parser/node.go
generated
vendored
Normal file
257
vendor/github.com/goccy/go-yaml/parser/node.go
generated
vendored
Normal file
@@ -0,0 +1,257 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml/ast"
|
||||||
|
"github.com/goccy/go-yaml/internal/errors"
|
||||||
|
"github.com/goccy/go-yaml/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newMappingNode(ctx *context, tk *Token, isFlow bool, values ...*ast.MappingValueNode) (*ast.MappingNode, error) {
|
||||||
|
node := ast.Mapping(tk.RawToken(), isFlow, values...)
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMappingValueNode(ctx *context, colonTk, entryTk *Token, key ast.MapKeyNode, value ast.Node) (*ast.MappingValueNode, error) {
|
||||||
|
node := ast.MappingValue(colonTk.RawToken(), key, value)
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
node.CollectEntry = entryTk.RawToken()
|
||||||
|
if key.GetToken().Position.Line == value.GetToken().Position.Line {
|
||||||
|
// originally key was commented, but now that null value has been added, value must be commented.
|
||||||
|
if err := setLineComment(ctx, value, colonTk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// set line comment by colonTk or entryTk.
|
||||||
|
if err := setLineComment(ctx, value, entryTk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := setLineComment(ctx, key, colonTk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// set line comment by colonTk or entryTk.
|
||||||
|
if err := setLineComment(ctx, key, entryTk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMappingKeyNode(ctx *context, tk *Token) (*ast.MappingKeyNode, error) {
|
||||||
|
node := ast.MappingKey(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAnchorNode(ctx *context, tk *Token) (*ast.AnchorNode, error) {
|
||||||
|
node := ast.Anchor(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newAliasNode(ctx *context, tk *Token) (*ast.AliasNode, error) {
|
||||||
|
node := ast.Alias(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newDirectiveNode(ctx *context, tk *Token) (*ast.DirectiveNode, error) {
|
||||||
|
node := ast.Directive(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMergeKeyNode(ctx *context, tk *Token) (*ast.MergeKeyNode, error) {
|
||||||
|
node := ast.MergeKey(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNullNode(ctx *context, tk *Token) (*ast.NullNode, error) {
|
||||||
|
node := ast.Null(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newBoolNode(ctx *context, tk *Token) (*ast.BoolNode, error) {
|
||||||
|
node := ast.Bool(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIntegerNode(ctx *context, tk *Token) (*ast.IntegerNode, error) {
|
||||||
|
node := ast.Integer(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newFloatNode(ctx *context, tk *Token) (*ast.FloatNode, error) {
|
||||||
|
node := ast.Float(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newInfinityNode(ctx *context, tk *Token) (*ast.InfinityNode, error) {
|
||||||
|
node := ast.Infinity(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newNanNode(ctx *context, tk *Token) (*ast.NanNode, error) {
|
||||||
|
node := ast.Nan(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newStringNode(ctx *context, tk *Token) (*ast.StringNode, error) {
|
||||||
|
node := ast.String(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newLiteralNode(ctx *context, tk *Token) (*ast.LiteralNode, error) {
|
||||||
|
node := ast.Literal(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTagNode(ctx *context, tk *Token) (*ast.TagNode, error) {
|
||||||
|
node := ast.Tag(tk.RawToken())
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSequenceNode(ctx *context, tk *Token, isFlow bool) (*ast.SequenceNode, error) {
|
||||||
|
node := ast.Sequence(tk.RawToken(), isFlow)
|
||||||
|
node.SetPath(ctx.path)
|
||||||
|
if err := setLineComment(ctx, node, tk); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTagDefaultScalarValueNode(ctx *context, tag *token.Token) (ast.ScalarNode, error) {
|
||||||
|
pos := *(tag.Position)
|
||||||
|
pos.Column++
|
||||||
|
|
||||||
|
var (
|
||||||
|
tk *Token
|
||||||
|
node ast.ScalarNode
|
||||||
|
)
|
||||||
|
switch token.ReservedTagKeyword(tag.Value) {
|
||||||
|
case token.IntegerTag:
|
||||||
|
tk = &Token{Token: token.New("0", "0", &pos)}
|
||||||
|
n, err := newIntegerNode(ctx, tk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
node = n
|
||||||
|
case token.FloatTag:
|
||||||
|
tk = &Token{Token: token.New("0", "0", &pos)}
|
||||||
|
n, err := newFloatNode(ctx, tk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
node = n
|
||||||
|
case token.StringTag, token.BinaryTag, token.TimestampTag:
|
||||||
|
tk = &Token{Token: token.New("", "", &pos)}
|
||||||
|
n, err := newStringNode(ctx, tk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
node = n
|
||||||
|
case token.BooleanTag:
|
||||||
|
tk = &Token{Token: token.New("false", "false", &pos)}
|
||||||
|
n, err := newBoolNode(ctx, tk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
node = n
|
||||||
|
case token.NullTag:
|
||||||
|
tk = &Token{Token: token.New("null", "null", &pos)}
|
||||||
|
n, err := newNullNode(ctx, tk)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
node = n
|
||||||
|
default:
|
||||||
|
return nil, errors.ErrSyntax(fmt.Sprintf("cannot assign default value for %q tag", tag.Value), tag)
|
||||||
|
}
|
||||||
|
ctx.insertToken(tk)
|
||||||
|
ctx.goNext()
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setLineComment(ctx *context, node ast.Node, tk *Token) error {
|
||||||
|
if tk == nil || tk.LineComment == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
comment := ast.CommentGroup([]*token.Token{tk.LineComment})
|
||||||
|
comment.SetPath(ctx.path)
|
||||||
|
if err := node.SetComment(comment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func setHeadComment(cm *ast.CommentGroupNode, value ast.Node) error {
|
||||||
|
if cm == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
switch n := value.(type) {
|
||||||
|
case *ast.MappingNode:
|
||||||
|
if len(n.Values) != 0 && value.GetComment() == nil {
|
||||||
|
cm.SetPath(n.Values[0].GetPath())
|
||||||
|
return n.Values[0].SetComment(cm)
|
||||||
|
}
|
||||||
|
case *ast.MappingValueNode:
|
||||||
|
cm.SetPath(n.GetPath())
|
||||||
|
return n.SetComment(cm)
|
||||||
|
}
|
||||||
|
cm.SetPath(value.GetPath())
|
||||||
|
return value.SetComment(cm)
|
||||||
|
}
|
||||||
12
vendor/github.com/goccy/go-yaml/parser/option.go
generated
vendored
Normal file
12
vendor/github.com/goccy/go-yaml/parser/option.go
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
// Option represents parser's option.
|
||||||
|
type Option func(p *parser)
|
||||||
|
|
||||||
|
// AllowDuplicateMapKey allow the use of keys with the same name in the same map,
|
||||||
|
// but by default, this is not permitted.
|
||||||
|
func AllowDuplicateMapKey() Option {
|
||||||
|
return func(p *parser) {
|
||||||
|
p.allowDuplicateMapKey = true
|
||||||
|
}
|
||||||
|
}
|
||||||
1330
vendor/github.com/goccy/go-yaml/parser/parser.go
generated
vendored
Normal file
1330
vendor/github.com/goccy/go-yaml/parser/parser.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
746
vendor/github.com/goccy/go-yaml/parser/token.go
generated
vendored
Normal file
746
vendor/github.com/goccy/go-yaml/parser/token.go
generated
vendored
Normal file
@@ -0,0 +1,746 @@
|
|||||||
|
package parser
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml/internal/errors"
|
||||||
|
"github.com/goccy/go-yaml/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TokenGroupType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
TokenGroupNone TokenGroupType = iota
|
||||||
|
TokenGroupDirective
|
||||||
|
TokenGroupDirectiveName
|
||||||
|
TokenGroupDocument
|
||||||
|
TokenGroupDocumentBody
|
||||||
|
TokenGroupAnchor
|
||||||
|
TokenGroupAnchorName
|
||||||
|
TokenGroupAlias
|
||||||
|
TokenGroupLiteral
|
||||||
|
TokenGroupFolded
|
||||||
|
TokenGroupScalarTag
|
||||||
|
TokenGroupMapKey
|
||||||
|
TokenGroupMapKeyValue
|
||||||
|
)
|
||||||
|
|
||||||
|
func (t TokenGroupType) String() string {
|
||||||
|
switch t {
|
||||||
|
case TokenGroupNone:
|
||||||
|
return "none"
|
||||||
|
case TokenGroupDirective:
|
||||||
|
return "directive"
|
||||||
|
case TokenGroupDirectiveName:
|
||||||
|
return "directive_name"
|
||||||
|
case TokenGroupDocument:
|
||||||
|
return "document"
|
||||||
|
case TokenGroupDocumentBody:
|
||||||
|
return "document_body"
|
||||||
|
case TokenGroupAnchor:
|
||||||
|
return "anchor"
|
||||||
|
case TokenGroupAnchorName:
|
||||||
|
return "anchor_name"
|
||||||
|
case TokenGroupAlias:
|
||||||
|
return "alias"
|
||||||
|
case TokenGroupLiteral:
|
||||||
|
return "literal"
|
||||||
|
case TokenGroupFolded:
|
||||||
|
return "folded"
|
||||||
|
case TokenGroupScalarTag:
|
||||||
|
return "scalar_tag"
|
||||||
|
case TokenGroupMapKey:
|
||||||
|
return "map_key"
|
||||||
|
case TokenGroupMapKeyValue:
|
||||||
|
return "map_key_value"
|
||||||
|
}
|
||||||
|
return "none"
|
||||||
|
}
|
||||||
|
|
||||||
|
type Token struct {
|
||||||
|
Token *token.Token
|
||||||
|
Group *TokenGroup
|
||||||
|
LineComment *token.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) RawToken() *token.Token {
|
||||||
|
if t == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if t.Token != nil {
|
||||||
|
return t.Token
|
||||||
|
}
|
||||||
|
return t.Group.RawToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) Type() token.Type {
|
||||||
|
if t == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if t.Token != nil {
|
||||||
|
return t.Token.Type
|
||||||
|
}
|
||||||
|
return t.Group.TokenType()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) GroupType() TokenGroupType {
|
||||||
|
if t == nil {
|
||||||
|
return TokenGroupNone
|
||||||
|
}
|
||||||
|
if t.Token != nil {
|
||||||
|
return TokenGroupNone
|
||||||
|
}
|
||||||
|
return t.Group.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) Line() int {
|
||||||
|
if t == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if t.Token != nil {
|
||||||
|
return t.Token.Position.Line
|
||||||
|
}
|
||||||
|
return t.Group.Line()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) Column() int {
|
||||||
|
if t == nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if t.Token != nil {
|
||||||
|
return t.Token.Position.Column
|
||||||
|
}
|
||||||
|
return t.Group.Column()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) SetGroupType(typ TokenGroupType) {
|
||||||
|
if t.Group == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Group.Type = typ
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) Dump() {
|
||||||
|
ctx := new(groupTokenRenderContext)
|
||||||
|
if t.Token != nil {
|
||||||
|
fmt.Fprint(os.Stdout, t.Token.Value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Group.dump(ctx)
|
||||||
|
fmt.Fprintf(os.Stdout, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Token) dump(ctx *groupTokenRenderContext) {
|
||||||
|
if t.Token != nil {
|
||||||
|
fmt.Fprint(os.Stdout, t.Token.Value)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
t.Group.dump(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
type groupTokenRenderContext struct {
|
||||||
|
num int
|
||||||
|
}
|
||||||
|
|
||||||
|
type TokenGroup struct {
|
||||||
|
Type TokenGroupType
|
||||||
|
Tokens []*Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *TokenGroup) First() *Token {
|
||||||
|
if len(g.Tokens) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return g.Tokens[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *TokenGroup) Last() *Token {
|
||||||
|
if len(g.Tokens) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return g.Tokens[len(g.Tokens)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *TokenGroup) dump(ctx *groupTokenRenderContext) {
|
||||||
|
num := ctx.num
|
||||||
|
fmt.Fprint(os.Stdout, colorize(num, "("))
|
||||||
|
ctx.num++
|
||||||
|
for _, tk := range g.Tokens {
|
||||||
|
tk.dump(ctx)
|
||||||
|
}
|
||||||
|
fmt.Fprint(os.Stdout, colorize(num, ")"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *TokenGroup) RawToken() *token.Token {
|
||||||
|
if len(g.Tokens) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return g.Tokens[0].RawToken()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *TokenGroup) Line() int {
|
||||||
|
if len(g.Tokens) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return g.Tokens[0].Line()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *TokenGroup) Column() int {
|
||||||
|
if len(g.Tokens) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return g.Tokens[0].Column()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *TokenGroup) TokenType() token.Type {
|
||||||
|
if len(g.Tokens) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return g.Tokens[0].Type()
|
||||||
|
}
|
||||||
|
|
||||||
|
func CreateGroupedTokens(tokens token.Tokens) ([]*Token, error) {
|
||||||
|
var err error
|
||||||
|
tks := newTokens(tokens)
|
||||||
|
tks = createLineCommentTokenGroups(tks)
|
||||||
|
tks, err = createLiteralAndFoldedTokenGroups(tks)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tks, err = createAnchorAndAliasTokenGroups(tks)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tks, err = createScalarTagTokenGroups(tks)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tks, err = createAnchorWithScalarTagTokenGroups(tks)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tks, err = createMapKeyTokenGroups(tks)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tks = createMapKeyValueTokenGroups(tks)
|
||||||
|
tks, err = createDirectiveTokenGroups(tks)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
tks, err = createDocumentTokens(tks)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return tks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTokens(tks token.Tokens) []*Token {
|
||||||
|
ret := make([]*Token, 0, len(tks))
|
||||||
|
for _, tk := range tks {
|
||||||
|
ret = append(ret, &Token{Token: tk})
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func createLineCommentTokenGroups(tokens []*Token) []*Token {
|
||||||
|
ret := make([]*Token, 0, len(tokens))
|
||||||
|
for i := 0; i < len(tokens); i++ {
|
||||||
|
tk := tokens[i]
|
||||||
|
switch tk.Type() {
|
||||||
|
case token.CommentType:
|
||||||
|
if i > 0 && tokens[i-1].Line() == tk.Line() {
|
||||||
|
tokens[i-1].LineComment = tk.RawToken()
|
||||||
|
} else {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ret = append(ret, tk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func createLiteralAndFoldedTokenGroups(tokens []*Token) ([]*Token, error) {
|
||||||
|
ret := make([]*Token, 0, len(tokens))
|
||||||
|
for i := 0; i < len(tokens); i++ {
|
||||||
|
tk := tokens[i]
|
||||||
|
switch tk.Type() {
|
||||||
|
case token.LiteralType:
|
||||||
|
tks := []*Token{tk}
|
||||||
|
if i+1 < len(tokens) {
|
||||||
|
tks = append(tks, tokens[i+1])
|
||||||
|
}
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupLiteral,
|
||||||
|
Tokens: tks,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
i++
|
||||||
|
case token.FoldedType:
|
||||||
|
tks := []*Token{tk}
|
||||||
|
if i+1 < len(tokens) {
|
||||||
|
tks = append(tks, tokens[i+1])
|
||||||
|
}
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupFolded,
|
||||||
|
Tokens: tks,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
i++
|
||||||
|
default:
|
||||||
|
ret = append(ret, tk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createAnchorAndAliasTokenGroups(tokens []*Token) ([]*Token, error) {
|
||||||
|
ret := make([]*Token, 0, len(tokens))
|
||||||
|
for i := 0; i < len(tokens); i++ {
|
||||||
|
tk := tokens[i]
|
||||||
|
switch tk.Type() {
|
||||||
|
case token.AnchorType:
|
||||||
|
if i+1 >= len(tokens) {
|
||||||
|
return nil, errors.ErrSyntax("undefined anchor name", tk.RawToken())
|
||||||
|
}
|
||||||
|
if i+2 >= len(tokens) {
|
||||||
|
return nil, errors.ErrSyntax("undefined anchor value", tk.RawToken())
|
||||||
|
}
|
||||||
|
anchorName := &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupAnchorName,
|
||||||
|
Tokens: []*Token{tk, tokens[i+1]},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
valueTk := tokens[i+2]
|
||||||
|
if tk.Line() == valueTk.Line() && valueTk.Type() == token.SequenceEntryType {
|
||||||
|
return nil, errors.ErrSyntax("sequence entries are not allowed after anchor on the same line", valueTk.RawToken())
|
||||||
|
}
|
||||||
|
if tk.Line() == valueTk.Line() && isScalarType(valueTk) {
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupAnchor,
|
||||||
|
Tokens: []*Token{anchorName, valueTk},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
ret = append(ret, anchorName)
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
case token.AliasType:
|
||||||
|
if i+1 == len(tokens) {
|
||||||
|
return nil, errors.ErrSyntax("undefined alias name", tk.RawToken())
|
||||||
|
}
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupAlias,
|
||||||
|
Tokens: []*Token{tk, tokens[i+1]},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
i++
|
||||||
|
default:
|
||||||
|
ret = append(ret, tk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createScalarTagTokenGroups(tokens []*Token) ([]*Token, error) {
|
||||||
|
ret := make([]*Token, 0, len(tokens))
|
||||||
|
for i := 0; i < len(tokens); i++ {
|
||||||
|
tk := tokens[i]
|
||||||
|
if tk.Type() != token.TagType {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
tag := tk.RawToken()
|
||||||
|
if strings.HasPrefix(tag.Value, "!!") {
|
||||||
|
// secondary tag.
|
||||||
|
switch token.ReservedTagKeyword(tag.Value) {
|
||||||
|
case token.IntegerTag, token.FloatTag, token.StringTag, token.BinaryTag, token.TimestampTag, token.BooleanTag, token.NullTag:
|
||||||
|
if len(tokens) <= i+1 {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tk.Line() != tokens[i+1].Line() {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tokens[i+1].GroupType() == TokenGroupAnchorName {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if isScalarType(tokens[i+1]) {
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupScalarTag,
|
||||||
|
Tokens: []*Token{tk, tokens[i+1]},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
}
|
||||||
|
case token.MergeTag:
|
||||||
|
if len(tokens) <= i+1 {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tk.Line() != tokens[i+1].Line() {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tokens[i+1].GroupType() == TokenGroupAnchorName {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tokens[i+1].Type() != token.MergeKeyType {
|
||||||
|
return nil, errors.ErrSyntax("could not find merge key", tokens[i+1].RawToken())
|
||||||
|
}
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupScalarTag,
|
||||||
|
Tokens: []*Token{tk, tokens[i+1]},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
i++
|
||||||
|
default:
|
||||||
|
ret = append(ret, tk)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if len(tokens) <= i+1 {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tk.Line() != tokens[i+1].Line() {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if tokens[i+1].GroupType() == TokenGroupAnchorName {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if isFlowType(tokens[i+1]) {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupScalarTag,
|
||||||
|
Tokens: []*Token{tk, tokens[i+1]},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createAnchorWithScalarTagTokenGroups(tokens []*Token) ([]*Token, error) {
|
||||||
|
ret := make([]*Token, 0, len(tokens))
|
||||||
|
for i := 0; i < len(tokens); i++ {
|
||||||
|
tk := tokens[i]
|
||||||
|
switch tk.GroupType() {
|
||||||
|
case TokenGroupAnchorName:
|
||||||
|
if i+1 >= len(tokens) {
|
||||||
|
return nil, errors.ErrSyntax("undefined anchor value", tk.RawToken())
|
||||||
|
}
|
||||||
|
valueTk := tokens[i+1]
|
||||||
|
if tk.Line() == valueTk.Line() && valueTk.GroupType() == TokenGroupScalarTag {
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupAnchor,
|
||||||
|
Tokens: []*Token{tk, tokens[i+1]},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ret = append(ret, tk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMapKeyTokenGroups(tokens []*Token) ([]*Token, error) {
|
||||||
|
tks, err := createMapKeyByMappingKey(tokens)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return createMapKeyByMappingValue(tks)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMapKeyByMappingKey(tokens []*Token) ([]*Token, error) {
|
||||||
|
ret := make([]*Token, 0, len(tokens))
|
||||||
|
for i := 0; i < len(tokens); i++ {
|
||||||
|
tk := tokens[i]
|
||||||
|
switch tk.Type() {
|
||||||
|
case token.MappingKeyType:
|
||||||
|
if i+1 >= len(tokens) {
|
||||||
|
return nil, errors.ErrSyntax("undefined map key", tk.RawToken())
|
||||||
|
}
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupMapKey,
|
||||||
|
Tokens: []*Token{tk, tokens[i+1]},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
i++
|
||||||
|
default:
|
||||||
|
ret = append(ret, tk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMapKeyByMappingValue(tokens []*Token) ([]*Token, error) {
|
||||||
|
ret := make([]*Token, 0, len(tokens))
|
||||||
|
for i := 0; i < len(tokens); i++ {
|
||||||
|
tk := tokens[i]
|
||||||
|
switch tk.Type() {
|
||||||
|
case token.MappingValueType:
|
||||||
|
if i == 0 {
|
||||||
|
return nil, errors.ErrSyntax("unexpected key name", tk.RawToken())
|
||||||
|
}
|
||||||
|
mapKeyTk := tokens[i-1]
|
||||||
|
if isNotMapKeyType(mapKeyTk) {
|
||||||
|
return nil, errors.ErrSyntax("found an invalid key for this map", tokens[i].RawToken())
|
||||||
|
}
|
||||||
|
newTk := &Token{Token: mapKeyTk.Token, Group: mapKeyTk.Group}
|
||||||
|
mapKeyTk.Token = nil
|
||||||
|
mapKeyTk.Group = &TokenGroup{
|
||||||
|
Type: TokenGroupMapKey,
|
||||||
|
Tokens: []*Token{newTk, tk},
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ret = append(ret, tk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createMapKeyValueTokenGroups(tokens []*Token) []*Token {
|
||||||
|
ret := make([]*Token, 0, len(tokens))
|
||||||
|
for i := 0; i < len(tokens); i++ {
|
||||||
|
tk := tokens[i]
|
||||||
|
switch tk.GroupType() {
|
||||||
|
case TokenGroupMapKey:
|
||||||
|
if len(tokens) <= i+1 {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
valueTk := tokens[i+1]
|
||||||
|
if tk.Line() != valueTk.Line() {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if valueTk.GroupType() == TokenGroupAnchorName {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if valueTk.Type() == token.TagType && valueTk.GroupType() != TokenGroupScalarTag {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if isScalarType(valueTk) || valueTk.Type() == token.TagType {
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupMapKeyValue,
|
||||||
|
Tokens: []*Token{tk, valueTk},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
i++
|
||||||
|
} else {
|
||||||
|
ret = append(ret, tk)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ret = append(ret, tk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func createDirectiveTokenGroups(tokens []*Token) ([]*Token, error) {
|
||||||
|
ret := make([]*Token, 0, len(tokens))
|
||||||
|
for i := 0; i < len(tokens); i++ {
|
||||||
|
tk := tokens[i]
|
||||||
|
switch tk.Type() {
|
||||||
|
case token.DirectiveType:
|
||||||
|
if i+1 >= len(tokens) {
|
||||||
|
return nil, errors.ErrSyntax("undefined directive value", tk.RawToken())
|
||||||
|
}
|
||||||
|
directiveName := &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupDirectiveName,
|
||||||
|
Tokens: []*Token{tk, tokens[i+1]},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
var valueTks []*Token
|
||||||
|
for j := i + 1; j < len(tokens); j++ {
|
||||||
|
if tokens[j].Line() != tk.Line() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
valueTks = append(valueTks, tokens[j])
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
if i+1 >= len(tokens) || tokens[i+1].Type() != token.DocumentHeaderType {
|
||||||
|
return nil, errors.ErrSyntax("unexpected directive value. document not started", tk.RawToken())
|
||||||
|
}
|
||||||
|
if len(valueTks) != 0 {
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupDirective,
|
||||||
|
Tokens: append([]*Token{directiveName}, valueTks...),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
ret = append(ret, directiveName)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
ret = append(ret, tk)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createDocumentTokens(tokens []*Token) ([]*Token, error) {
|
||||||
|
var ret []*Token
|
||||||
|
for i := 0; i < len(tokens); i++ {
|
||||||
|
tk := tokens[i]
|
||||||
|
switch tk.Type() {
|
||||||
|
case token.DocumentHeaderType:
|
||||||
|
if i != 0 {
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{Tokens: tokens[:i]},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if i+1 == len(tokens) {
|
||||||
|
// if current token is last token, add DocumentHeader only tokens to ret.
|
||||||
|
return append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupDocument,
|
||||||
|
Tokens: []*Token{tk},
|
||||||
|
},
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
if tokens[i+1].Type() == token.DocumentHeaderType {
|
||||||
|
return append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupDocument,
|
||||||
|
Tokens: []*Token{tk},
|
||||||
|
},
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
if tokens[i].Line() == tokens[i+1].Line() {
|
||||||
|
switch tokens[i+1].GroupType() {
|
||||||
|
case TokenGroupMapKey, TokenGroupMapKeyValue:
|
||||||
|
return nil, errors.ErrSyntax("value cannot be placed after document separator", tokens[i+1].RawToken())
|
||||||
|
}
|
||||||
|
switch tokens[i+1].Type() {
|
||||||
|
case token.SequenceEntryType:
|
||||||
|
return nil, errors.ErrSyntax("value cannot be placed after document separator", tokens[i+1].RawToken())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tks, err := createDocumentTokens(tokens[i+1:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if len(tks) != 0 {
|
||||||
|
tks[0].SetGroupType(TokenGroupDocument)
|
||||||
|
tks[0].Group.Tokens = append([]*Token{tk}, tks[0].Group.Tokens...)
|
||||||
|
return append(ret, tks...), nil
|
||||||
|
}
|
||||||
|
return append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupDocument,
|
||||||
|
Tokens: []*Token{tk},
|
||||||
|
},
|
||||||
|
}), nil
|
||||||
|
case token.DocumentEndType:
|
||||||
|
if i != 0 {
|
||||||
|
ret = append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupDocument,
|
||||||
|
Tokens: tokens[0 : i+1],
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if i+1 == len(tokens) {
|
||||||
|
return ret, nil
|
||||||
|
}
|
||||||
|
if isScalarType(tokens[i+1]) {
|
||||||
|
return nil, errors.ErrSyntax("unexpected end content", tokens[i+1].RawToken())
|
||||||
|
}
|
||||||
|
|
||||||
|
tks, err := createDocumentTokens(tokens[i+1:])
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return append(ret, tks...), nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return append(ret, &Token{
|
||||||
|
Group: &TokenGroup{
|
||||||
|
Type: TokenGroupDocument,
|
||||||
|
Tokens: tokens,
|
||||||
|
},
|
||||||
|
}), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func isScalarType(tk *Token) bool {
|
||||||
|
switch tk.GroupType() {
|
||||||
|
case TokenGroupMapKey, TokenGroupMapKeyValue:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
typ := tk.Type()
|
||||||
|
return typ == token.AnchorType ||
|
||||||
|
typ == token.AliasType ||
|
||||||
|
typ == token.LiteralType ||
|
||||||
|
typ == token.FoldedType ||
|
||||||
|
typ == token.NullType ||
|
||||||
|
typ == token.ImplicitNullType ||
|
||||||
|
typ == token.BoolType ||
|
||||||
|
typ == token.IntegerType ||
|
||||||
|
typ == token.BinaryIntegerType ||
|
||||||
|
typ == token.OctetIntegerType ||
|
||||||
|
typ == token.HexIntegerType ||
|
||||||
|
typ == token.FloatType ||
|
||||||
|
typ == token.InfinityType ||
|
||||||
|
typ == token.NanType ||
|
||||||
|
typ == token.StringType ||
|
||||||
|
typ == token.SingleQuoteType ||
|
||||||
|
typ == token.DoubleQuoteType
|
||||||
|
}
|
||||||
|
|
||||||
|
func isNotMapKeyType(tk *Token) bool {
|
||||||
|
typ := tk.Type()
|
||||||
|
return typ == token.DirectiveType ||
|
||||||
|
typ == token.DocumentHeaderType ||
|
||||||
|
typ == token.DocumentEndType ||
|
||||||
|
typ == token.CollectEntryType ||
|
||||||
|
typ == token.MappingStartType ||
|
||||||
|
typ == token.MappingValueType ||
|
||||||
|
typ == token.MappingEndType ||
|
||||||
|
typ == token.SequenceStartType ||
|
||||||
|
typ == token.SequenceEntryType ||
|
||||||
|
typ == token.SequenceEndType
|
||||||
|
}
|
||||||
|
|
||||||
|
func isFlowType(tk *Token) bool {
|
||||||
|
typ := tk.Type()
|
||||||
|
return typ == token.MappingStartType ||
|
||||||
|
typ == token.MappingEndType ||
|
||||||
|
typ == token.SequenceStartType ||
|
||||||
|
typ == token.SequenceEntryType
|
||||||
|
}
|
||||||
835
vendor/github.com/goccy/go-yaml/path.go
generated
vendored
Normal file
835
vendor/github.com/goccy/go-yaml/path.go
generated
vendored
Normal file
@@ -0,0 +1,835 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml/ast"
|
||||||
|
"github.com/goccy/go-yaml/parser"
|
||||||
|
"github.com/goccy/go-yaml/printer"
|
||||||
|
)
|
||||||
|
|
||||||
|
// PathString create Path from string
|
||||||
|
//
|
||||||
|
// YAMLPath rule
|
||||||
|
// $ : the root object/element
|
||||||
|
// . : child operator
|
||||||
|
// .. : recursive descent
|
||||||
|
// [num] : object/element of array by number
|
||||||
|
// [*] : all objects/elements for array.
|
||||||
|
//
|
||||||
|
// If you want to use reserved characters such as `.` and `*` as a key name,
|
||||||
|
// enclose them in single quotation as follows ( $.foo.'bar.baz-*'.hoge ).
|
||||||
|
// If you want to use a single quote with reserved characters, escape it with `\` ( $.foo.'bar.baz\'s value'.hoge ).
|
||||||
|
func PathString(s string) (*Path, error) {
|
||||||
|
buf := []rune(s)
|
||||||
|
length := len(buf)
|
||||||
|
cursor := 0
|
||||||
|
builder := &PathBuilder{}
|
||||||
|
for cursor < length {
|
||||||
|
c := buf[cursor]
|
||||||
|
switch c {
|
||||||
|
case '$':
|
||||||
|
builder = builder.Root()
|
||||||
|
cursor++
|
||||||
|
case '.':
|
||||||
|
b, buf, c, err := parsePathDot(builder, buf, cursor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
length = len(buf)
|
||||||
|
builder = b
|
||||||
|
cursor = c
|
||||||
|
case '[':
|
||||||
|
b, buf, c, err := parsePathIndex(builder, buf, cursor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
length = len(buf)
|
||||||
|
builder = b
|
||||||
|
cursor = c
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("invalid path at %d: %w", cursor, ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return builder.Build(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePathRecursive(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) {
|
||||||
|
length := len(buf)
|
||||||
|
cursor += 2 // skip .. characters
|
||||||
|
start := cursor
|
||||||
|
for ; cursor < length; cursor++ {
|
||||||
|
c := buf[cursor]
|
||||||
|
switch c {
|
||||||
|
case '$':
|
||||||
|
return nil, nil, 0, fmt.Errorf("specified '$' after '..' character: %w", ErrInvalidPathString)
|
||||||
|
case '*':
|
||||||
|
return nil, nil, 0, fmt.Errorf("specified '*' after '..' character: %w", ErrInvalidPathString)
|
||||||
|
case '.', '[':
|
||||||
|
goto end
|
||||||
|
case ']':
|
||||||
|
return nil, nil, 0, fmt.Errorf("specified ']' after '..' character: %w", ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
if start == cursor {
|
||||||
|
return nil, nil, 0, fmt.Errorf("not found recursive selector: %w", ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
return b.Recursive(string(buf[start:cursor])), buf, cursor, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePathDot(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) {
|
||||||
|
if b.root == nil || b.node == nil {
|
||||||
|
return nil, nil, 0, fmt.Errorf("required '$' character at first: %w", ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
length := len(buf)
|
||||||
|
if cursor+1 < length && buf[cursor+1] == '.' {
|
||||||
|
b, buf, c, err := parsePathRecursive(b, buf, cursor)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
return b, buf, c, nil
|
||||||
|
}
|
||||||
|
cursor++ // skip . character
|
||||||
|
start := cursor
|
||||||
|
|
||||||
|
// if started single quote, looking for end single quote char
|
||||||
|
if cursor < length && buf[cursor] == '\'' {
|
||||||
|
return parseQuotedKey(b, buf, cursor)
|
||||||
|
}
|
||||||
|
for ; cursor < length; cursor++ {
|
||||||
|
c := buf[cursor]
|
||||||
|
switch c {
|
||||||
|
case '$':
|
||||||
|
return nil, nil, 0, fmt.Errorf("specified '$' after '.' character: %w", ErrInvalidPathString)
|
||||||
|
case '*':
|
||||||
|
return nil, nil, 0, fmt.Errorf("specified '*' after '.' character: %w", ErrInvalidPathString)
|
||||||
|
case '.', '[':
|
||||||
|
goto end
|
||||||
|
case ']':
|
||||||
|
return nil, nil, 0, fmt.Errorf("specified ']' after '.' character: %w", ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
if start == cursor {
|
||||||
|
return nil, nil, 0, fmt.Errorf("could not find by empty key: %w", ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
return b.child(string(buf[start:cursor])), buf, cursor, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseQuotedKey(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) {
|
||||||
|
if b.root == nil || b.node == nil {
|
||||||
|
return nil, nil, 0, fmt.Errorf("required '$' character at first: %w", ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
|
||||||
|
cursor++ // skip single quote
|
||||||
|
start := cursor
|
||||||
|
length := len(buf)
|
||||||
|
var foundEndDelim bool
|
||||||
|
for ; cursor < length; cursor++ {
|
||||||
|
switch buf[cursor] {
|
||||||
|
case '\\':
|
||||||
|
buf = append(append([]rune{}, buf[:cursor]...), buf[cursor+1:]...)
|
||||||
|
length = len(buf)
|
||||||
|
case '\'':
|
||||||
|
foundEndDelim = true
|
||||||
|
goto end
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end:
|
||||||
|
if !foundEndDelim {
|
||||||
|
return nil, nil, 0, fmt.Errorf("could not find end delimiter for key: %w", ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
if start == cursor {
|
||||||
|
return nil, nil, 0, fmt.Errorf("could not find by empty key: %w", ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
selector := buf[start:cursor]
|
||||||
|
cursor++
|
||||||
|
if cursor < length {
|
||||||
|
switch buf[cursor] {
|
||||||
|
case '$':
|
||||||
|
return nil, nil, 0, fmt.Errorf("specified '$' after '.' character: %w", ErrInvalidPathString)
|
||||||
|
case '*':
|
||||||
|
return nil, nil, 0, fmt.Errorf("specified '*' after '.' character: %w", ErrInvalidPathString)
|
||||||
|
case ']':
|
||||||
|
return nil, nil, 0, fmt.Errorf("specified ']' after '.' character: %w", ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return b.child(string(selector)), buf, cursor, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func parsePathIndex(b *PathBuilder, buf []rune, cursor int) (*PathBuilder, []rune, int, error) {
|
||||||
|
if b.root == nil || b.node == nil {
|
||||||
|
return nil, nil, 0, fmt.Errorf("required '$' character at first: %w", ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
|
||||||
|
length := len(buf)
|
||||||
|
cursor++ // skip '[' character
|
||||||
|
if length <= cursor {
|
||||||
|
return nil, nil, 0, fmt.Errorf("unexpected end of YAML Path: %w", ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
c := buf[cursor]
|
||||||
|
switch c {
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '*':
|
||||||
|
start := cursor
|
||||||
|
cursor++
|
||||||
|
for ; cursor < length; cursor++ {
|
||||||
|
c := buf[cursor]
|
||||||
|
switch c {
|
||||||
|
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if buf[cursor] != ']' {
|
||||||
|
return nil, nil, 0, fmt.Errorf("invalid character %s at %d: %w", string(buf[cursor]), cursor, ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
numOrAll := string(buf[start:cursor])
|
||||||
|
if numOrAll == "*" {
|
||||||
|
return b.IndexAll(), buf, cursor + 1, nil
|
||||||
|
}
|
||||||
|
num, err := strconv.ParseInt(numOrAll, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, 0, err
|
||||||
|
}
|
||||||
|
return b.Index(uint(num)), buf, cursor + 1, nil
|
||||||
|
}
|
||||||
|
return nil, nil, 0, fmt.Errorf("invalid character %q at %d: %w", c, cursor, ErrInvalidPathString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Path represent YAMLPath ( like a JSONPath ).
|
||||||
|
type Path struct {
|
||||||
|
node pathNode
|
||||||
|
}
|
||||||
|
|
||||||
|
// String path to text.
|
||||||
|
func (p *Path) String() string {
|
||||||
|
return p.node.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Read decode from r and set extracted value by YAMLPath to v.
|
||||||
|
func (p *Path) Read(r io.Reader, v interface{}) error {
|
||||||
|
node, err := p.ReadNode(r)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := Unmarshal([]byte(node.String()), v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReadNode create AST from r and extract node by YAMLPath.
|
||||||
|
func (p *Path) ReadNode(r io.Reader) (ast.Node, error) {
|
||||||
|
if p.node == nil {
|
||||||
|
return nil, ErrInvalidPath
|
||||||
|
}
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if _, err := io.Copy(&buf, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
f, err := parser.ParseBytes(buf.Bytes(), 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
node, err := p.FilterFile(f)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter filter from target by YAMLPath and set it to v.
|
||||||
|
func (p *Path) Filter(target, v interface{}) error {
|
||||||
|
b, err := Marshal(target)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.Read(bytes.NewBuffer(b), v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterFile filter from ast.File by YAMLPath.
|
||||||
|
func (p *Path) FilterFile(f *ast.File) (ast.Node, error) {
|
||||||
|
for _, doc := range f.Docs {
|
||||||
|
// For simplicity, directives cannot be the target of operations
|
||||||
|
if doc.Body != nil && doc.Body.Type() == ast.DirectiveType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
node, err := p.FilterNode(doc.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if node != nil {
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("failed to find path ( %s ): %w", p.node, ErrNotFoundNode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FilterNode filter from node by YAMLPath.
|
||||||
|
func (p *Path) FilterNode(node ast.Node) (ast.Node, error) {
|
||||||
|
if node == nil {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
n, err := p.node.filter(node)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return n, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeFromReader merge YAML text into ast.File.
|
||||||
|
func (p *Path) MergeFromReader(dst *ast.File, src io.Reader) error {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if _, err := io.Copy(&buf, src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file, err := parser.ParseBytes(buf.Bytes(), 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.MergeFromFile(dst, file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeFromFile merge ast.File into ast.File.
|
||||||
|
func (p *Path) MergeFromFile(dst *ast.File, src *ast.File) error {
|
||||||
|
base, err := p.FilterFile(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, doc := range src.Docs {
|
||||||
|
if err := ast.Merge(base, doc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// MergeFromNode merge ast.Node into ast.File.
|
||||||
|
func (p *Path) MergeFromNode(dst *ast.File, src ast.Node) error {
|
||||||
|
base, err := p.FilterFile(dst)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := ast.Merge(base, src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceWithReader replace ast.File with io.Reader.
|
||||||
|
func (p *Path) ReplaceWithReader(dst *ast.File, src io.Reader) error {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if _, err := io.Copy(&buf, src); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
file, err := parser.ParseBytes(buf.Bytes(), 0)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := p.ReplaceWithFile(dst, file); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceWithFile replace ast.File with ast.File.
|
||||||
|
func (p *Path) ReplaceWithFile(dst *ast.File, src *ast.File) error {
|
||||||
|
for _, doc := range src.Docs {
|
||||||
|
if err := p.ReplaceWithNode(dst, doc); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceNode replace ast.File with ast.Node.
|
||||||
|
func (p *Path) ReplaceWithNode(dst *ast.File, node ast.Node) error {
|
||||||
|
for _, doc := range dst.Docs {
|
||||||
|
// For simplicity, directives cannot be the target of operations
|
||||||
|
if doc.Body != nil && doc.Body.Type() == ast.DirectiveType {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if node.Type() == ast.DocumentType {
|
||||||
|
node = node.(*ast.DocumentNode).Body
|
||||||
|
}
|
||||||
|
if err := p.node.replace(doc.Body, node); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AnnotateSource add annotation to passed source ( see section 5.1 in README.md ).
|
||||||
|
func (p *Path) AnnotateSource(source []byte, colored bool) ([]byte, error) {
|
||||||
|
file, err := parser.ParseBytes(source, 0)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
node, err := p.FilterFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var pp printer.Printer
|
||||||
|
return []byte(pp.PrintErrorToken(node.GetToken(), colored)), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// PathBuilder represent builder for YAMLPath.
|
||||||
|
type PathBuilder struct {
|
||||||
|
root *rootNode
|
||||||
|
node pathNode
|
||||||
|
}
|
||||||
|
|
||||||
|
// Root add '$' to current path.
|
||||||
|
func (b *PathBuilder) Root() *PathBuilder {
|
||||||
|
root := newRootNode()
|
||||||
|
return &PathBuilder{root: root, node: root}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IndexAll add '[*]' to current path.
|
||||||
|
func (b *PathBuilder) IndexAll() *PathBuilder {
|
||||||
|
b.node = b.node.chain(newIndexAllNode())
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Recursive add '..selector' to current path.
|
||||||
|
func (b *PathBuilder) Recursive(selector string) *PathBuilder {
|
||||||
|
b.node = b.node.chain(newRecursiveNode(selector))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PathBuilder) containsReservedPathCharacters(path string) bool {
|
||||||
|
if strings.Contains(path, ".") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if strings.Contains(path, "*") {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PathBuilder) enclosedSingleQuote(name string) bool {
|
||||||
|
return strings.HasPrefix(name, "'") && strings.HasSuffix(name, "'")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PathBuilder) normalizeSelectorName(name string) string {
|
||||||
|
if b.enclosedSingleQuote(name) {
|
||||||
|
// already escaped name
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
if b.containsReservedPathCharacters(name) {
|
||||||
|
escapedName := strings.ReplaceAll(name, `'`, `\'`)
|
||||||
|
return "'" + escapedName + "'"
|
||||||
|
}
|
||||||
|
return name
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *PathBuilder) child(name string) *PathBuilder {
|
||||||
|
b.node = b.node.chain(newSelectorNode(name))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Child add '.name' to current path.
|
||||||
|
func (b *PathBuilder) Child(name string) *PathBuilder {
|
||||||
|
return b.child(b.normalizeSelectorName(name))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Index add '[idx]' to current path.
|
||||||
|
func (b *PathBuilder) Index(idx uint) *PathBuilder {
|
||||||
|
b.node = b.node.chain(newIndexNode(idx))
|
||||||
|
return b
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build build YAMLPath.
|
||||||
|
func (b *PathBuilder) Build() *Path {
|
||||||
|
return &Path{node: b.root}
|
||||||
|
}
|
||||||
|
|
||||||
|
type pathNode interface {
|
||||||
|
fmt.Stringer
|
||||||
|
chain(pathNode) pathNode
|
||||||
|
filter(ast.Node) (ast.Node, error)
|
||||||
|
replace(ast.Node, ast.Node) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type basePathNode struct {
|
||||||
|
child pathNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *basePathNode) chain(node pathNode) pathNode {
|
||||||
|
n.child = node
|
||||||
|
return node
|
||||||
|
}
|
||||||
|
|
||||||
|
type rootNode struct {
|
||||||
|
*basePathNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRootNode() *rootNode {
|
||||||
|
return &rootNode{basePathNode: &basePathNode{}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *rootNode) String() string {
|
||||||
|
s := "$"
|
||||||
|
if n.child != nil {
|
||||||
|
s += n.child.String()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *rootNode) filter(node ast.Node) (ast.Node, error) {
|
||||||
|
if n.child == nil {
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
filtered, err := n.child.filter(node)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return filtered, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *rootNode) replace(node ast.Node, target ast.Node) error {
|
||||||
|
if n.child == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := n.child.replace(node, target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type selectorNode struct {
|
||||||
|
*basePathNode
|
||||||
|
selector string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSelectorNode(selector string) *selectorNode {
|
||||||
|
return &selectorNode{
|
||||||
|
basePathNode: &basePathNode{},
|
||||||
|
selector: selector,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *selectorNode) filter(node ast.Node) (ast.Node, error) {
|
||||||
|
selector := n.selector
|
||||||
|
if len(selector) > 1 && selector[0] == '\'' && selector[len(selector)-1] == '\'' {
|
||||||
|
selector = selector[1 : len(selector)-1]
|
||||||
|
}
|
||||||
|
switch node.Type() {
|
||||||
|
case ast.MappingType:
|
||||||
|
for _, value := range node.(*ast.MappingNode).Values {
|
||||||
|
key := value.Key.GetToken().Value
|
||||||
|
if len(key) > 0 {
|
||||||
|
switch key[0] {
|
||||||
|
case '"':
|
||||||
|
var err error
|
||||||
|
key, err = strconv.Unquote(key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
case '\'':
|
||||||
|
if len(key) > 1 && key[len(key)-1] == '\'' {
|
||||||
|
key = key[1 : len(key)-1]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if key == selector {
|
||||||
|
if n.child == nil {
|
||||||
|
return value.Value, nil
|
||||||
|
}
|
||||||
|
filtered, err := n.child.filter(value.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return filtered, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ast.MappingValueType:
|
||||||
|
value, _ := node.(*ast.MappingValueNode)
|
||||||
|
key := value.Key.GetToken().Value
|
||||||
|
if key == selector {
|
||||||
|
if n.child == nil {
|
||||||
|
return value.Value, nil
|
||||||
|
}
|
||||||
|
filtered, err := n.child.filter(value.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return filtered, nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("expected node type is map or map value. but got %s: %w", node.Type(), ErrInvalidQuery)
|
||||||
|
}
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *selectorNode) replaceMapValue(value *ast.MappingValueNode, target ast.Node) error {
|
||||||
|
key := value.Key.GetToken().Value
|
||||||
|
if key != n.selector {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if n.child == nil {
|
||||||
|
if err := value.Replace(target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := n.child.replace(value.Value, target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *selectorNode) replace(node ast.Node, target ast.Node) error {
|
||||||
|
switch node.Type() {
|
||||||
|
case ast.MappingType:
|
||||||
|
for _, value := range node.(*ast.MappingNode).Values {
|
||||||
|
if err := n.replaceMapValue(value, target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case ast.MappingValueType:
|
||||||
|
value, _ := node.(*ast.MappingValueNode)
|
||||||
|
if err := n.replaceMapValue(value, target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("expected node type is map or map value. but got %s: %w", node.Type(), ErrInvalidQuery)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *selectorNode) String() string {
|
||||||
|
var builder PathBuilder
|
||||||
|
selector := builder.normalizeSelectorName(n.selector)
|
||||||
|
s := fmt.Sprintf(".%s", selector)
|
||||||
|
if n.child != nil {
|
||||||
|
s += n.child.String()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type indexNode struct {
|
||||||
|
*basePathNode
|
||||||
|
selector uint
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIndexNode(selector uint) *indexNode {
|
||||||
|
return &indexNode{
|
||||||
|
basePathNode: &basePathNode{},
|
||||||
|
selector: selector,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *indexNode) filter(node ast.Node) (ast.Node, error) {
|
||||||
|
if node.Type() != ast.SequenceType {
|
||||||
|
return nil, fmt.Errorf("expected sequence type node. but got %s: %w", node.Type(), ErrInvalidQuery)
|
||||||
|
}
|
||||||
|
sequence, _ := node.(*ast.SequenceNode)
|
||||||
|
if n.selector >= uint(len(sequence.Values)) {
|
||||||
|
return nil, fmt.Errorf("expected index is %d. but got sequences has %d items: %w", n.selector, len(sequence.Values), ErrInvalidQuery)
|
||||||
|
}
|
||||||
|
value := sequence.Values[n.selector]
|
||||||
|
if n.child == nil {
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
filtered, err := n.child.filter(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return filtered, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *indexNode) replace(node ast.Node, target ast.Node) error {
|
||||||
|
if node.Type() != ast.SequenceType {
|
||||||
|
return fmt.Errorf("expected sequence type node. but got %s: %w", node.Type(), ErrInvalidQuery)
|
||||||
|
}
|
||||||
|
sequence, _ := node.(*ast.SequenceNode)
|
||||||
|
if n.selector >= uint(len(sequence.Values)) {
|
||||||
|
return fmt.Errorf("expected index is %d. but got sequences has %d items: %w", n.selector, len(sequence.Values), ErrInvalidQuery)
|
||||||
|
}
|
||||||
|
if n.child == nil {
|
||||||
|
if err := sequence.Replace(int(n.selector), target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if err := n.child.replace(sequence.Values[n.selector], target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *indexNode) String() string {
|
||||||
|
s := fmt.Sprintf("[%d]", n.selector)
|
||||||
|
if n.child != nil {
|
||||||
|
s += n.child.String()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
type indexAllNode struct {
|
||||||
|
*basePathNode
|
||||||
|
}
|
||||||
|
|
||||||
|
func newIndexAllNode() *indexAllNode {
|
||||||
|
return &indexAllNode{
|
||||||
|
basePathNode: &basePathNode{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *indexAllNode) String() string {
|
||||||
|
s := "[*]"
|
||||||
|
if n.child != nil {
|
||||||
|
s += n.child.String()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *indexAllNode) filter(node ast.Node) (ast.Node, error) {
|
||||||
|
if node.Type() != ast.SequenceType {
|
||||||
|
return nil, fmt.Errorf("expected sequence type node. but got %s: %w", node.Type(), ErrInvalidQuery)
|
||||||
|
}
|
||||||
|
sequence, _ := node.(*ast.SequenceNode)
|
||||||
|
if n.child == nil {
|
||||||
|
return sequence, nil
|
||||||
|
}
|
||||||
|
out := *sequence
|
||||||
|
out.Values = []ast.Node{}
|
||||||
|
for _, value := range sequence.Values {
|
||||||
|
filtered, err := n.child.filter(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out.Values = append(out.Values, filtered)
|
||||||
|
}
|
||||||
|
return &out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *indexAllNode) replace(node ast.Node, target ast.Node) error {
|
||||||
|
if node.Type() != ast.SequenceType {
|
||||||
|
return fmt.Errorf("expected sequence type node. but got %s: %w", node.Type(), ErrInvalidQuery)
|
||||||
|
}
|
||||||
|
sequence, _ := node.(*ast.SequenceNode)
|
||||||
|
if n.child == nil {
|
||||||
|
for idx := range sequence.Values {
|
||||||
|
if err := sequence.Replace(idx, target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
for _, value := range sequence.Values {
|
||||||
|
if err := n.child.replace(value, target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
type recursiveNode struct {
|
||||||
|
*basePathNode
|
||||||
|
selector string
|
||||||
|
}
|
||||||
|
|
||||||
|
func newRecursiveNode(selector string) *recursiveNode {
|
||||||
|
return &recursiveNode{
|
||||||
|
basePathNode: &basePathNode{},
|
||||||
|
selector: selector,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *recursiveNode) String() string {
|
||||||
|
s := fmt.Sprintf("..%s", n.selector)
|
||||||
|
if n.child != nil {
|
||||||
|
s += n.child.String()
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *recursiveNode) filterNode(node ast.Node) (*ast.SequenceNode, error) {
|
||||||
|
sequence := &ast.SequenceNode{BaseNode: &ast.BaseNode{}}
|
||||||
|
switch typedNode := node.(type) {
|
||||||
|
case *ast.MappingNode:
|
||||||
|
for _, value := range typedNode.Values {
|
||||||
|
seq, err := n.filterNode(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sequence.Values = append(sequence.Values, seq.Values...)
|
||||||
|
}
|
||||||
|
case *ast.MappingValueNode:
|
||||||
|
key := typedNode.Key.GetToken().Value
|
||||||
|
if n.selector == key {
|
||||||
|
sequence.Values = append(sequence.Values, typedNode.Value)
|
||||||
|
}
|
||||||
|
seq, err := n.filterNode(typedNode.Value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sequence.Values = append(sequence.Values, seq.Values...)
|
||||||
|
case *ast.SequenceNode:
|
||||||
|
for _, value := range typedNode.Values {
|
||||||
|
seq, err := n.filterNode(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sequence.Values = append(sequence.Values, seq.Values...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sequence, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *recursiveNode) filter(node ast.Node) (ast.Node, error) {
|
||||||
|
sequence, err := n.filterNode(node)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
sequence.Start = node.GetToken()
|
||||||
|
return sequence, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *recursiveNode) replaceNode(node ast.Node, target ast.Node) error {
|
||||||
|
switch typedNode := node.(type) {
|
||||||
|
case *ast.MappingNode:
|
||||||
|
for _, value := range typedNode.Values {
|
||||||
|
if err := n.replaceNode(value, target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case *ast.MappingValueNode:
|
||||||
|
key := typedNode.Key.GetToken().Value
|
||||||
|
if n.selector == key {
|
||||||
|
if err := typedNode.Replace(target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := n.replaceNode(typedNode.Value, target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
case *ast.SequenceNode:
|
||||||
|
for _, value := range typedNode.Values {
|
||||||
|
if err := n.replaceNode(value, target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *recursiveNode) replace(node ast.Node, target ast.Node) error {
|
||||||
|
if err := n.replaceNode(node, target); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
83
vendor/github.com/goccy/go-yaml/printer/color.go
generated
vendored
Normal file
83
vendor/github.com/goccy/go-yaml/printer/color.go
generated
vendored
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
// This source inspired by https://github.com/fatih/color.
|
||||||
|
package printer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ColorAttribute int
|
||||||
|
|
||||||
|
const (
|
||||||
|
ColorReset ColorAttribute = iota
|
||||||
|
ColorBold
|
||||||
|
ColorFaint
|
||||||
|
ColorItalic
|
||||||
|
ColorUnderline
|
||||||
|
ColorBlinkSlow
|
||||||
|
ColorBlinkRapid
|
||||||
|
ColorReverseVideo
|
||||||
|
ColorConcealed
|
||||||
|
ColorCrossedOut
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ColorFgHiBlack ColorAttribute = iota + 90
|
||||||
|
ColorFgHiRed
|
||||||
|
ColorFgHiGreen
|
||||||
|
ColorFgHiYellow
|
||||||
|
ColorFgHiBlue
|
||||||
|
ColorFgHiMagenta
|
||||||
|
ColorFgHiCyan
|
||||||
|
ColorFgHiWhite
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ColorResetBold ColorAttribute = iota + 22
|
||||||
|
ColorResetItalic
|
||||||
|
ColorResetUnderline
|
||||||
|
ColorResetBlinking
|
||||||
|
|
||||||
|
ColorResetReversed
|
||||||
|
ColorResetConcealed
|
||||||
|
ColorResetCrossedOut
|
||||||
|
)
|
||||||
|
|
||||||
|
const escape = "\x1b"
|
||||||
|
|
||||||
|
var colorResetMap = map[ColorAttribute]ColorAttribute{
|
||||||
|
ColorBold: ColorResetBold,
|
||||||
|
ColorFaint: ColorResetBold,
|
||||||
|
ColorItalic: ColorResetItalic,
|
||||||
|
ColorUnderline: ColorResetUnderline,
|
||||||
|
ColorBlinkSlow: ColorResetBlinking,
|
||||||
|
ColorBlinkRapid: ColorResetBlinking,
|
||||||
|
ColorReverseVideo: ColorResetReversed,
|
||||||
|
ColorConcealed: ColorResetConcealed,
|
||||||
|
ColorCrossedOut: ColorResetCrossedOut,
|
||||||
|
}
|
||||||
|
|
||||||
|
func format(attrs ...ColorAttribute) string {
|
||||||
|
format := make([]string, 0, len(attrs))
|
||||||
|
for _, attr := range attrs {
|
||||||
|
format = append(format, fmt.Sprint(attr))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func unformat(attrs ...ColorAttribute) string {
|
||||||
|
format := make([]string, len(attrs))
|
||||||
|
for _, attr := range attrs {
|
||||||
|
v := fmt.Sprint(ColorReset)
|
||||||
|
reset, exists := colorResetMap[attr]
|
||||||
|
if exists {
|
||||||
|
v = fmt.Sprint(reset)
|
||||||
|
}
|
||||||
|
format = append(format, v)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func colorize(msg string, attrs ...ColorAttribute) string {
|
||||||
|
return format(attrs...) + msg + unformat(attrs...)
|
||||||
|
}
|
||||||
353
vendor/github.com/goccy/go-yaml/printer/printer.go
generated
vendored
Normal file
353
vendor/github.com/goccy/go-yaml/printer/printer.go
generated
vendored
Normal file
@@ -0,0 +1,353 @@
|
|||||||
|
package printer
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml/ast"
|
||||||
|
"github.com/goccy/go-yaml/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Property additional property set for each the token
|
||||||
|
type Property struct {
|
||||||
|
Prefix string
|
||||||
|
Suffix string
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintFunc returns property instance
|
||||||
|
type PrintFunc func() *Property
|
||||||
|
|
||||||
|
// Printer create text from token collection or ast
|
||||||
|
type Printer struct {
|
||||||
|
LineNumber bool
|
||||||
|
LineNumberFormat func(num int) string
|
||||||
|
MapKey PrintFunc
|
||||||
|
Anchor PrintFunc
|
||||||
|
Alias PrintFunc
|
||||||
|
Bool PrintFunc
|
||||||
|
String PrintFunc
|
||||||
|
Number PrintFunc
|
||||||
|
Comment PrintFunc
|
||||||
|
}
|
||||||
|
|
||||||
|
func defaultLineNumberFormat(num int) string {
|
||||||
|
return fmt.Sprintf("%2d | ", num)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Printer) property(tk *token.Token) *Property {
|
||||||
|
prop := &Property{}
|
||||||
|
switch tk.PreviousType() {
|
||||||
|
case token.AnchorType:
|
||||||
|
if p.Anchor != nil {
|
||||||
|
return p.Anchor()
|
||||||
|
}
|
||||||
|
return prop
|
||||||
|
case token.AliasType:
|
||||||
|
if p.Alias != nil {
|
||||||
|
return p.Alias()
|
||||||
|
}
|
||||||
|
return prop
|
||||||
|
}
|
||||||
|
switch tk.NextType() {
|
||||||
|
case token.MappingValueType:
|
||||||
|
if p.MapKey != nil {
|
||||||
|
return p.MapKey()
|
||||||
|
}
|
||||||
|
return prop
|
||||||
|
}
|
||||||
|
switch tk.Type {
|
||||||
|
case token.BoolType:
|
||||||
|
if p.Bool != nil {
|
||||||
|
return p.Bool()
|
||||||
|
}
|
||||||
|
return prop
|
||||||
|
case token.AnchorType:
|
||||||
|
if p.Anchor != nil {
|
||||||
|
return p.Anchor()
|
||||||
|
}
|
||||||
|
return prop
|
||||||
|
case token.AliasType:
|
||||||
|
if p.Anchor != nil {
|
||||||
|
return p.Alias()
|
||||||
|
}
|
||||||
|
return prop
|
||||||
|
case token.StringType, token.SingleQuoteType, token.DoubleQuoteType:
|
||||||
|
if p.String != nil {
|
||||||
|
return p.String()
|
||||||
|
}
|
||||||
|
return prop
|
||||||
|
case token.IntegerType, token.FloatType:
|
||||||
|
if p.Number != nil {
|
||||||
|
return p.Number()
|
||||||
|
}
|
||||||
|
return prop
|
||||||
|
case token.CommentType:
|
||||||
|
if p.Comment != nil {
|
||||||
|
return p.Comment()
|
||||||
|
}
|
||||||
|
return prop
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
return prop
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintTokens create text from token collection
|
||||||
|
func (p *Printer) PrintTokens(tokens token.Tokens) string {
|
||||||
|
if len(tokens) == 0 {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
if p.LineNumber {
|
||||||
|
if p.LineNumberFormat == nil {
|
||||||
|
p.LineNumberFormat = defaultLineNumberFormat
|
||||||
|
}
|
||||||
|
}
|
||||||
|
texts := []string{}
|
||||||
|
lineNumber := tokens[0].Position.Line
|
||||||
|
for _, tk := range tokens {
|
||||||
|
lines := strings.Split(tk.Origin, "\n")
|
||||||
|
prop := p.property(tk)
|
||||||
|
header := ""
|
||||||
|
if p.LineNumber {
|
||||||
|
header = p.LineNumberFormat(lineNumber)
|
||||||
|
}
|
||||||
|
if len(lines) == 1 {
|
||||||
|
line := prop.Prefix + lines[0] + prop.Suffix
|
||||||
|
if len(texts) == 0 {
|
||||||
|
texts = append(texts, header+line)
|
||||||
|
lineNumber++
|
||||||
|
} else {
|
||||||
|
text := texts[len(texts)-1]
|
||||||
|
texts[len(texts)-1] = text + line
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for idx, src := range lines {
|
||||||
|
if p.LineNumber {
|
||||||
|
header = p.LineNumberFormat(lineNumber)
|
||||||
|
}
|
||||||
|
line := prop.Prefix + src + prop.Suffix
|
||||||
|
if idx == 0 {
|
||||||
|
if len(texts) == 0 {
|
||||||
|
texts = append(texts, header+line)
|
||||||
|
lineNumber++
|
||||||
|
} else {
|
||||||
|
text := texts[len(texts)-1]
|
||||||
|
texts[len(texts)-1] = text + line
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
texts = append(texts, fmt.Sprintf("%s%s", header, line))
|
||||||
|
lineNumber++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strings.Join(texts, "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintNode create text from ast.Node
|
||||||
|
func (p *Printer) PrintNode(node ast.Node) []byte {
|
||||||
|
return []byte(fmt.Sprintf("%+v\n", node))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Printer) setDefaultColorSet() {
|
||||||
|
p.Bool = func() *Property {
|
||||||
|
return &Property{
|
||||||
|
Prefix: format(ColorFgHiMagenta),
|
||||||
|
Suffix: format(ColorReset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Number = func() *Property {
|
||||||
|
return &Property{
|
||||||
|
Prefix: format(ColorFgHiMagenta),
|
||||||
|
Suffix: format(ColorReset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.MapKey = func() *Property {
|
||||||
|
return &Property{
|
||||||
|
Prefix: format(ColorFgHiCyan),
|
||||||
|
Suffix: format(ColorReset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Anchor = func() *Property {
|
||||||
|
return &Property{
|
||||||
|
Prefix: format(ColorFgHiYellow),
|
||||||
|
Suffix: format(ColorReset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Alias = func() *Property {
|
||||||
|
return &Property{
|
||||||
|
Prefix: format(ColorFgHiYellow),
|
||||||
|
Suffix: format(ColorReset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.String = func() *Property {
|
||||||
|
return &Property{
|
||||||
|
Prefix: format(ColorFgHiGreen),
|
||||||
|
Suffix: format(ColorReset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
p.Comment = func() *Property {
|
||||||
|
return &Property{
|
||||||
|
Prefix: format(ColorFgHiBlack),
|
||||||
|
Suffix: format(ColorReset),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Printer) PrintErrorMessage(msg string, isColored bool) string {
|
||||||
|
if isColored {
|
||||||
|
return fmt.Sprintf("%s%s%s",
|
||||||
|
format(ColorFgHiRed),
|
||||||
|
msg,
|
||||||
|
format(ColorReset),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
return msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Printer) removeLeftSideNewLineChar(src string) string {
|
||||||
|
return strings.TrimLeft(strings.TrimLeft(strings.TrimLeft(src, "\r"), "\n"), "\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Printer) removeRightSideNewLineChar(src string) string {
|
||||||
|
return strings.TrimRight(strings.TrimRight(strings.TrimRight(src, "\r"), "\n"), "\r\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Printer) removeRightSideWhiteSpaceChar(src string) string {
|
||||||
|
return p.removeRightSideNewLineChar(strings.TrimRight(src, " "))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Printer) newLineCount(s string) int {
|
||||||
|
src := []rune(s)
|
||||||
|
size := len(src)
|
||||||
|
cnt := 0
|
||||||
|
for i := 0; i < size; i++ {
|
||||||
|
c := src[i]
|
||||||
|
switch c {
|
||||||
|
case '\r':
|
||||||
|
if i+1 < size && src[i+1] == '\n' {
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
cnt++
|
||||||
|
case '\n':
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cnt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Printer) isNewLineLastChar(s string) bool {
|
||||||
|
for i := len(s) - 1; i > 0; i-- {
|
||||||
|
c := s[i]
|
||||||
|
switch c {
|
||||||
|
case ' ':
|
||||||
|
continue
|
||||||
|
case '\n', '\r':
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Printer) printBeforeTokens(tk *token.Token, minLine, extLine int) token.Tokens {
|
||||||
|
for tk.Prev != nil {
|
||||||
|
if tk.Prev.Position.Line < minLine {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
tk = tk.Prev
|
||||||
|
}
|
||||||
|
minTk := tk.Clone()
|
||||||
|
if minTk.Prev != nil {
|
||||||
|
// add white spaces to minTk by prev token
|
||||||
|
prev := minTk.Prev
|
||||||
|
whiteSpaceLen := len(prev.Origin) - len(strings.TrimRight(prev.Origin, " "))
|
||||||
|
minTk.Origin = strings.Repeat(" ", whiteSpaceLen) + minTk.Origin
|
||||||
|
}
|
||||||
|
minTk.Origin = p.removeLeftSideNewLineChar(minTk.Origin)
|
||||||
|
tokens := token.Tokens{minTk}
|
||||||
|
tk = minTk.Next
|
||||||
|
for tk != nil && tk.Position.Line <= extLine {
|
||||||
|
clonedTk := tk.Clone()
|
||||||
|
tokens.Add(clonedTk)
|
||||||
|
tk = clonedTk.Next
|
||||||
|
}
|
||||||
|
lastTk := tokens[len(tokens)-1]
|
||||||
|
trimmedOrigin := p.removeRightSideWhiteSpaceChar(lastTk.Origin)
|
||||||
|
suffix := lastTk.Origin[len(trimmedOrigin):]
|
||||||
|
lastTk.Origin = trimmedOrigin
|
||||||
|
|
||||||
|
if lastTk.Next != nil && len(suffix) > 1 {
|
||||||
|
next := lastTk.Next.Clone()
|
||||||
|
// add suffix to header of next token
|
||||||
|
if suffix[0] == '\n' || suffix[0] == '\r' {
|
||||||
|
suffix = suffix[1:]
|
||||||
|
}
|
||||||
|
next.Origin = suffix + next.Origin
|
||||||
|
lastTk.Next = next
|
||||||
|
}
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Printer) printAfterTokens(tk *token.Token, maxLine int) token.Tokens {
|
||||||
|
tokens := token.Tokens{}
|
||||||
|
if tk == nil {
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
if tk.Position.Line > maxLine {
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
minTk := tk.Clone()
|
||||||
|
minTk.Origin = p.removeLeftSideNewLineChar(minTk.Origin)
|
||||||
|
tokens.Add(minTk)
|
||||||
|
tk = minTk.Next
|
||||||
|
for tk != nil && tk.Position.Line <= maxLine {
|
||||||
|
clonedTk := tk.Clone()
|
||||||
|
tokens.Add(clonedTk)
|
||||||
|
tk = clonedTk.Next
|
||||||
|
}
|
||||||
|
return tokens
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Printer) setupErrorTokenFormat(annotateLine int, isColored bool) {
|
||||||
|
prefix := func(annotateLine, num int) string {
|
||||||
|
if annotateLine == num {
|
||||||
|
return fmt.Sprintf("> %2d | ", num)
|
||||||
|
}
|
||||||
|
return fmt.Sprintf(" %2d | ", num)
|
||||||
|
}
|
||||||
|
p.LineNumber = true
|
||||||
|
p.LineNumberFormat = func(num int) string {
|
||||||
|
if isColored {
|
||||||
|
return colorize(prefix(annotateLine, num), ColorBold, ColorFgHiWhite)
|
||||||
|
}
|
||||||
|
return prefix(annotateLine, num)
|
||||||
|
}
|
||||||
|
if isColored {
|
||||||
|
p.setDefaultColorSet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *Printer) PrintErrorToken(tk *token.Token, isColored bool) string {
|
||||||
|
errToken := tk
|
||||||
|
curLine := tk.Position.Line
|
||||||
|
curExtLine := curLine + p.newLineCount(p.removeLeftSideNewLineChar(tk.Origin))
|
||||||
|
if p.isNewLineLastChar(tk.Origin) {
|
||||||
|
// if last character ( exclude white space ) is new line character, ignore it.
|
||||||
|
curExtLine--
|
||||||
|
}
|
||||||
|
|
||||||
|
minLine := int(math.Max(float64(curLine-3), 1))
|
||||||
|
maxLine := curExtLine + 3
|
||||||
|
p.setupErrorTokenFormat(curLine, isColored)
|
||||||
|
|
||||||
|
beforeTokens := p.printBeforeTokens(tk, minLine, curExtLine)
|
||||||
|
lastTk := beforeTokens[len(beforeTokens)-1]
|
||||||
|
afterTokens := p.printAfterTokens(lastTk.Next, maxLine)
|
||||||
|
|
||||||
|
beforeSource := p.PrintTokens(beforeTokens)
|
||||||
|
prefixSpaceNum := len(fmt.Sprintf(" %2d | ", curLine))
|
||||||
|
annotateLine := strings.Repeat(" ", prefixSpaceNum+errToken.Position.Column-1) + "^"
|
||||||
|
afterSource := p.PrintTokens(afterTokens)
|
||||||
|
return fmt.Sprintf("%s\n%s\n%s", beforeSource, annotateLine, afterSource)
|
||||||
|
}
|
||||||
452
vendor/github.com/goccy/go-yaml/scanner/context.go
generated
vendored
Normal file
452
vendor/github.com/goccy/go-yaml/scanner/context.go
generated
vendored
Normal file
@@ -0,0 +1,452 @@
|
|||||||
|
package scanner
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml/token"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Context context at scanning
|
||||||
|
type Context struct {
|
||||||
|
idx int
|
||||||
|
size int
|
||||||
|
notSpaceCharPos int
|
||||||
|
notSpaceOrgCharPos int
|
||||||
|
src []rune
|
||||||
|
buf []rune
|
||||||
|
obuf []rune
|
||||||
|
tokens token.Tokens
|
||||||
|
mstate *MultiLineState
|
||||||
|
}
|
||||||
|
|
||||||
|
type MultiLineState struct {
|
||||||
|
opt string
|
||||||
|
firstLineIndentColumn int
|
||||||
|
prevLineIndentColumn int
|
||||||
|
lineIndentColumn int
|
||||||
|
lastNotSpaceOnlyLineIndentColumn int
|
||||||
|
spaceOnlyIndentColumn int
|
||||||
|
foldedNewLine bool
|
||||||
|
isRawFolded bool
|
||||||
|
isLiteral bool
|
||||||
|
isFolded bool
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
ctxPool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return createContext()
|
||||||
|
},
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func createContext() *Context {
|
||||||
|
return &Context{
|
||||||
|
idx: 0,
|
||||||
|
tokens: token.Tokens{},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func newContext(src []rune) *Context {
|
||||||
|
ctx, _ := ctxPool.Get().(*Context)
|
||||||
|
ctx.reset(src)
|
||||||
|
return ctx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) release() {
|
||||||
|
ctxPool.Put(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) clear() {
|
||||||
|
c.resetBuffer()
|
||||||
|
c.mstate = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) reset(src []rune) {
|
||||||
|
c.idx = 0
|
||||||
|
c.size = len(src)
|
||||||
|
c.src = src
|
||||||
|
c.tokens = c.tokens[:0]
|
||||||
|
c.resetBuffer()
|
||||||
|
c.mstate = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) resetBuffer() {
|
||||||
|
c.buf = c.buf[:0]
|
||||||
|
c.obuf = c.obuf[:0]
|
||||||
|
c.notSpaceCharPos = 0
|
||||||
|
c.notSpaceOrgCharPos = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) breakMultiLine() {
|
||||||
|
c.mstate = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) getMultiLineState() *MultiLineState {
|
||||||
|
return c.mstate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) setLiteral(lastDelimColumn int, opt string) {
|
||||||
|
mstate := &MultiLineState{
|
||||||
|
isLiteral: true,
|
||||||
|
opt: opt,
|
||||||
|
}
|
||||||
|
indent := firstLineIndentColumnByOpt(opt)
|
||||||
|
if indent > 0 {
|
||||||
|
mstate.firstLineIndentColumn = lastDelimColumn + indent
|
||||||
|
}
|
||||||
|
c.mstate = mstate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) setFolded(lastDelimColumn int, opt string) {
|
||||||
|
mstate := &MultiLineState{
|
||||||
|
isFolded: true,
|
||||||
|
opt: opt,
|
||||||
|
}
|
||||||
|
indent := firstLineIndentColumnByOpt(opt)
|
||||||
|
if indent > 0 {
|
||||||
|
mstate.firstLineIndentColumn = lastDelimColumn + indent
|
||||||
|
}
|
||||||
|
c.mstate = mstate
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) setRawFolded(column int) {
|
||||||
|
mstate := &MultiLineState{
|
||||||
|
isRawFolded: true,
|
||||||
|
}
|
||||||
|
mstate.updateIndentColumn(column)
|
||||||
|
c.mstate = mstate
|
||||||
|
}
|
||||||
|
|
||||||
|
func firstLineIndentColumnByOpt(opt string) int {
|
||||||
|
opt = strings.TrimPrefix(opt, "-")
|
||||||
|
opt = strings.TrimPrefix(opt, "+")
|
||||||
|
opt = strings.TrimSuffix(opt, "-")
|
||||||
|
opt = strings.TrimSuffix(opt, "+")
|
||||||
|
i, _ := strconv.ParseInt(opt, 10, 64)
|
||||||
|
return int(i)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiLineState) lastDelimColumn() int {
|
||||||
|
if s.firstLineIndentColumn == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return s.firstLineIndentColumn - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiLineState) updateIndentColumn(column int) {
|
||||||
|
if s.firstLineIndentColumn == 0 {
|
||||||
|
s.firstLineIndentColumn = column
|
||||||
|
}
|
||||||
|
if s.lineIndentColumn == 0 {
|
||||||
|
s.lineIndentColumn = column
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiLineState) updateSpaceOnlyIndentColumn(column int) {
|
||||||
|
if s.firstLineIndentColumn != 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
s.spaceOnlyIndentColumn = column
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiLineState) validateIndentAfterSpaceOnly(column int) error {
|
||||||
|
if s.firstLineIndentColumn != 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if s.spaceOnlyIndentColumn > column {
|
||||||
|
return errors.New("invalid number of indent is specified after space only")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiLineState) validateIndentColumn() error {
|
||||||
|
if firstLineIndentColumnByOpt(s.opt) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if s.firstLineIndentColumn > s.lineIndentColumn {
|
||||||
|
return errors.New("invalid number of indent is specified in the multi-line header")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiLineState) updateNewLineState() {
|
||||||
|
s.prevLineIndentColumn = s.lineIndentColumn
|
||||||
|
if s.lineIndentColumn != 0 {
|
||||||
|
s.lastNotSpaceOnlyLineIndentColumn = s.lineIndentColumn
|
||||||
|
}
|
||||||
|
s.foldedNewLine = true
|
||||||
|
s.lineIndentColumn = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiLineState) isIndentColumn(column int) bool {
|
||||||
|
if s.firstLineIndentColumn == 0 {
|
||||||
|
return column == 1
|
||||||
|
}
|
||||||
|
return s.firstLineIndentColumn > column
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiLineState) addIndent(ctx *Context, column int) {
|
||||||
|
if s.firstLineIndentColumn == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the first line of the document has already been evaluated, the number is treated as the threshold, since the `firstLineIndentColumn` is a positive number.
|
||||||
|
if column < s.firstLineIndentColumn {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// `c.foldedNewLine` is a variable that is set to true for every newline.
|
||||||
|
if !s.isLiteral && s.foldedNewLine {
|
||||||
|
s.foldedNewLine = false
|
||||||
|
}
|
||||||
|
// Since addBuf ignore space character, add to the buffer directly.
|
||||||
|
ctx.buf = append(ctx.buf, ' ')
|
||||||
|
ctx.notSpaceCharPos = len(ctx.buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
// updateNewLineInFolded if Folded or RawFolded context and the content on the current line starts at the same column as the previous line,
|
||||||
|
// treat the new-line-char as a space.
|
||||||
|
func (s *MultiLineState) updateNewLineInFolded(ctx *Context, column int) {
|
||||||
|
if s.isLiteral {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Folded or RawFolded.
|
||||||
|
|
||||||
|
if !s.foldedNewLine {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var (
|
||||||
|
lastChar rune
|
||||||
|
prevLastChar rune
|
||||||
|
)
|
||||||
|
if len(ctx.buf) != 0 {
|
||||||
|
lastChar = ctx.buf[len(ctx.buf)-1]
|
||||||
|
}
|
||||||
|
if len(ctx.buf) > 1 {
|
||||||
|
prevLastChar = ctx.buf[len(ctx.buf)-2]
|
||||||
|
}
|
||||||
|
if s.lineIndentColumn == s.prevLineIndentColumn {
|
||||||
|
// ---
|
||||||
|
// >
|
||||||
|
// a
|
||||||
|
// b
|
||||||
|
if lastChar == '\n' {
|
||||||
|
ctx.buf[len(ctx.buf)-1] = ' '
|
||||||
|
}
|
||||||
|
} else if s.prevLineIndentColumn == 0 && s.lastNotSpaceOnlyLineIndentColumn == column {
|
||||||
|
// if previous line is indent-space and new-line-char only, prevLineIndentColumn is zero.
|
||||||
|
// In this case, last new-line-char is removed.
|
||||||
|
// ---
|
||||||
|
// >
|
||||||
|
// a
|
||||||
|
//
|
||||||
|
// b
|
||||||
|
if lastChar == '\n' && prevLastChar == '\n' {
|
||||||
|
ctx.buf = ctx.buf[:len(ctx.buf)-1]
|
||||||
|
ctx.notSpaceCharPos = len(ctx.buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.foldedNewLine = false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiLineState) hasTrimAllEndNewlineOpt() bool {
|
||||||
|
return strings.HasPrefix(s.opt, "-") || strings.HasSuffix(s.opt, "-") || s.isRawFolded
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MultiLineState) hasKeepAllEndNewlineOpt() bool {
|
||||||
|
return strings.HasPrefix(s.opt, "+") || strings.HasSuffix(s.opt, "+")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) addToken(tk *token.Token) {
|
||||||
|
if tk == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.tokens = append(c.tokens, tk)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) addBuf(r rune) {
|
||||||
|
if len(c.buf) == 0 && (r == ' ' || r == '\t') {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.buf = append(c.buf, r)
|
||||||
|
if r != ' ' && r != '\t' {
|
||||||
|
c.notSpaceCharPos = len(c.buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) addBufWithTab(r rune) {
|
||||||
|
if len(c.buf) == 0 && r == ' ' {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.buf = append(c.buf, r)
|
||||||
|
if r != ' ' {
|
||||||
|
c.notSpaceCharPos = len(c.buf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) addOriginBuf(r rune) {
|
||||||
|
c.obuf = append(c.obuf, r)
|
||||||
|
if r != ' ' && r != '\t' {
|
||||||
|
c.notSpaceOrgCharPos = len(c.obuf)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) removeRightSpaceFromBuf() {
|
||||||
|
trimmedBuf := c.obuf[:c.notSpaceOrgCharPos]
|
||||||
|
buflen := len(trimmedBuf)
|
||||||
|
diff := len(c.obuf) - buflen
|
||||||
|
if diff > 0 {
|
||||||
|
c.obuf = c.obuf[:buflen]
|
||||||
|
c.buf = c.bufferedSrc()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) isEOS() bool {
|
||||||
|
return len(c.src)-1 <= c.idx
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) isNextEOS() bool {
|
||||||
|
return len(c.src) <= c.idx+1
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) next() bool {
|
||||||
|
return c.idx < c.size
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) source(s, e int) string {
|
||||||
|
return string(c.src[s:e])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) previousChar() rune {
|
||||||
|
if c.idx > 0 {
|
||||||
|
return c.src[c.idx-1]
|
||||||
|
}
|
||||||
|
return rune(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) currentChar() rune {
|
||||||
|
if c.size > c.idx {
|
||||||
|
return c.src[c.idx]
|
||||||
|
}
|
||||||
|
return rune(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) nextChar() rune {
|
||||||
|
if c.size > c.idx+1 {
|
||||||
|
return c.src[c.idx+1]
|
||||||
|
}
|
||||||
|
return rune(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) repeatNum(r rune) int {
|
||||||
|
cnt := 0
|
||||||
|
for i := c.idx; i < c.size; i++ {
|
||||||
|
if c.src[i] == r {
|
||||||
|
cnt++
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cnt
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) progress(num int) {
|
||||||
|
c.idx += num
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) existsBuffer() bool {
|
||||||
|
return len(c.bufferedSrc()) != 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) isMultiLine() bool {
|
||||||
|
return c.mstate != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) bufferedSrc() []rune {
|
||||||
|
src := c.buf[:c.notSpaceCharPos]
|
||||||
|
if c.isMultiLine() {
|
||||||
|
mstate := c.getMultiLineState()
|
||||||
|
// remove end '\n' character and trailing empty lines.
|
||||||
|
// https://yaml.org/spec/1.2.2/#8112-block-chomping-indicator
|
||||||
|
if mstate.hasTrimAllEndNewlineOpt() {
|
||||||
|
// If the '-' flag is specified, all trailing newline characters will be removed.
|
||||||
|
src = []rune(strings.TrimRight(string(src), "\n"))
|
||||||
|
} else if !mstate.hasKeepAllEndNewlineOpt() {
|
||||||
|
// Normally, all but one of the trailing newline characters are removed.
|
||||||
|
var newLineCharCount int
|
||||||
|
for i := len(src) - 1; i >= 0; i-- {
|
||||||
|
if src[i] == '\n' {
|
||||||
|
newLineCharCount++
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
removedNewLineCharCount := newLineCharCount - 1
|
||||||
|
for removedNewLineCharCount > 0 {
|
||||||
|
src = []rune(strings.TrimSuffix(string(src), "\n"))
|
||||||
|
removedNewLineCharCount--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the text ends with a space character, remove all of them.
|
||||||
|
if mstate.hasTrimAllEndNewlineOpt() {
|
||||||
|
src = []rune(strings.TrimRight(string(src), " "))
|
||||||
|
}
|
||||||
|
if string(src) == "\n" {
|
||||||
|
// If the content consists only of a newline,
|
||||||
|
// it can be considered as the document ending without any specified value,
|
||||||
|
// so it is treated as an empty string.
|
||||||
|
src = []rune{}
|
||||||
|
}
|
||||||
|
if mstate.hasKeepAllEndNewlineOpt() && len(src) == 0 {
|
||||||
|
src = []rune{'\n'}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return src
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) bufferedToken(pos *token.Position) *token.Token {
|
||||||
|
if c.idx == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
source := c.bufferedSrc()
|
||||||
|
if len(source) == 0 {
|
||||||
|
c.buf = c.buf[:0] // clear value's buffer only.
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
var tk *token.Token
|
||||||
|
if c.isMultiLine() {
|
||||||
|
tk = token.String(string(source), string(c.obuf), pos)
|
||||||
|
} else {
|
||||||
|
tk = token.New(string(source), string(c.obuf), pos)
|
||||||
|
}
|
||||||
|
c.setTokenTypeByPrevTag(tk)
|
||||||
|
c.resetBuffer()
|
||||||
|
return tk
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) setTokenTypeByPrevTag(tk *token.Token) {
|
||||||
|
lastTk := c.lastToken()
|
||||||
|
if lastTk == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if lastTk.Type != token.TagType {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
tag := token.ReservedTagKeyword(lastTk.Value)
|
||||||
|
if _, exists := token.ReservedTagKeywordMap[tag]; !exists {
|
||||||
|
tk.Type = token.StringType
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Context) lastToken() *token.Token {
|
||||||
|
if len(c.tokens) != 0 {
|
||||||
|
return c.tokens[len(c.tokens)-1]
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
17
vendor/github.com/goccy/go-yaml/scanner/error.go
generated
vendored
Normal file
17
vendor/github.com/goccy/go-yaml/scanner/error.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
package scanner
|
||||||
|
|
||||||
|
import "github.com/goccy/go-yaml/token"
|
||||||
|
|
||||||
|
type InvalidTokenError struct {
|
||||||
|
Token *token.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *InvalidTokenError) Error() string {
|
||||||
|
return e.Token.Error
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrInvalidToken(tk *token.Token) *InvalidTokenError {
|
||||||
|
return &InvalidTokenError{
|
||||||
|
Token: tk,
|
||||||
|
}
|
||||||
|
}
|
||||||
1536
vendor/github.com/goccy/go-yaml/scanner/scanner.go
generated
vendored
Normal file
1536
vendor/github.com/goccy/go-yaml/scanner/scanner.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
113
vendor/github.com/goccy/go-yaml/stdlib_quote.go
generated
vendored
Normal file
113
vendor/github.com/goccy/go-yaml/stdlib_quote.go
generated
vendored
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// Copied and trimmed down from https://github.com/golang/go/blob/e3769299cd3484e018e0e2a6e1b95c2b18ce4f41/src/strconv/quote.go
|
||||||
|
// We want to use the standard library's private "quoteWith" function rather than write our own so that we get robust unicode support.
|
||||||
|
// Every private function called by quoteWith was copied.
|
||||||
|
// There are 2 modifications to simplify the code:
|
||||||
|
// 1. The unicode.IsPrint function was substituted for the custom implementation of IsPrint
|
||||||
|
// 2. All code paths reachable only when ASCIIonly or grphicOnly are set to true were removed.
|
||||||
|
|
||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"unicode"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
lowerhex = "0123456789abcdef"
|
||||||
|
)
|
||||||
|
|
||||||
|
func quoteWith(s string, quote byte) string {
|
||||||
|
return string(appendQuotedWith(make([]byte, 0, 3*len(s)/2), s, quote))
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendQuotedWith(buf []byte, s string, quote byte) []byte {
|
||||||
|
// Often called with big strings, so preallocate. If there's quoting,
|
||||||
|
// this is conservative but still helps a lot.
|
||||||
|
if cap(buf)-len(buf) < len(s) {
|
||||||
|
nBuf := make([]byte, len(buf), len(buf)+1+len(s)+1)
|
||||||
|
copy(nBuf, buf)
|
||||||
|
buf = nBuf
|
||||||
|
}
|
||||||
|
buf = append(buf, quote)
|
||||||
|
for width := 0; len(s) > 0; s = s[width:] {
|
||||||
|
r := rune(s[0])
|
||||||
|
width = 1
|
||||||
|
if r >= utf8.RuneSelf {
|
||||||
|
r, width = utf8.DecodeRuneInString(s)
|
||||||
|
}
|
||||||
|
if width == 1 && r == utf8.RuneError {
|
||||||
|
buf = append(buf, `\x`...)
|
||||||
|
buf = append(buf, lowerhex[s[0]>>4])
|
||||||
|
buf = append(buf, lowerhex[s[0]&0xF])
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buf = appendEscapedRune(buf, r, quote)
|
||||||
|
}
|
||||||
|
buf = append(buf, quote)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
|
||||||
|
func appendEscapedRune(buf []byte, r rune, quote byte) []byte {
|
||||||
|
var runeTmp [utf8.UTFMax]byte
|
||||||
|
// goccy/go-yaml patch on top of the standard library's appendEscapedRune function.
|
||||||
|
//
|
||||||
|
// We use this to implement the YAML single-quoted string, where the only escape sequence is '', which represents a single quote.
|
||||||
|
// The below snippet from the standard library is for escaping e.g. \ with \\, which is not what we want for the single-quoted string.
|
||||||
|
//
|
||||||
|
// if r == rune(quote) || r == '\\' { // always backslashed
|
||||||
|
// buf = append(buf, '\\')
|
||||||
|
// buf = append(buf, byte(r))
|
||||||
|
// return buf
|
||||||
|
// }
|
||||||
|
if r == rune(quote) {
|
||||||
|
buf = append(buf, byte(r))
|
||||||
|
buf = append(buf, byte(r))
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
if unicode.IsPrint(r) {
|
||||||
|
n := utf8.EncodeRune(runeTmp[:], r)
|
||||||
|
buf = append(buf, runeTmp[:n]...)
|
||||||
|
return buf
|
||||||
|
}
|
||||||
|
switch r {
|
||||||
|
case '\a':
|
||||||
|
buf = append(buf, `\a`...)
|
||||||
|
case '\b':
|
||||||
|
buf = append(buf, `\b`...)
|
||||||
|
case '\f':
|
||||||
|
buf = append(buf, `\f`...)
|
||||||
|
case '\n':
|
||||||
|
buf = append(buf, `\n`...)
|
||||||
|
case '\r':
|
||||||
|
buf = append(buf, `\r`...)
|
||||||
|
case '\t':
|
||||||
|
buf = append(buf, `\t`...)
|
||||||
|
case '\v':
|
||||||
|
buf = append(buf, `\v`...)
|
||||||
|
default:
|
||||||
|
switch {
|
||||||
|
case r < ' ':
|
||||||
|
buf = append(buf, `\x`...)
|
||||||
|
buf = append(buf, lowerhex[byte(r)>>4])
|
||||||
|
buf = append(buf, lowerhex[byte(r)&0xF])
|
||||||
|
case r > utf8.MaxRune:
|
||||||
|
r = 0xFFFD
|
||||||
|
fallthrough
|
||||||
|
case r < 0x10000:
|
||||||
|
buf = append(buf, `\u`...)
|
||||||
|
for s := 12; s >= 0; s -= 4 {
|
||||||
|
buf = append(buf, lowerhex[r>>uint(s)&0xF])
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
buf = append(buf, `\U`...)
|
||||||
|
for s := 28; s >= 0; s -= 4 {
|
||||||
|
buf = append(buf, lowerhex[r>>uint(s)&0xF])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return buf
|
||||||
|
}
|
||||||
128
vendor/github.com/goccy/go-yaml/struct.go
generated
vendored
Normal file
128
vendor/github.com/goccy/go-yaml/struct.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// StructTagName tag keyword for Marshal/Unmarshal
|
||||||
|
StructTagName = "yaml"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StructField information for each the field in structure
|
||||||
|
type StructField struct {
|
||||||
|
FieldName string
|
||||||
|
RenderName string
|
||||||
|
AnchorName string
|
||||||
|
AliasName string
|
||||||
|
IsAutoAnchor bool
|
||||||
|
IsAutoAlias bool
|
||||||
|
IsOmitEmpty bool
|
||||||
|
IsOmitZero bool
|
||||||
|
IsFlow bool
|
||||||
|
IsInline bool
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTag(field reflect.StructField) string {
|
||||||
|
// If struct tag `yaml` exist, use that. If no `yaml`
|
||||||
|
// exists, but `json` does, use that and try the best to
|
||||||
|
// adhere to its rules
|
||||||
|
tag := field.Tag.Get(StructTagName)
|
||||||
|
if tag == "" {
|
||||||
|
tag = field.Tag.Get(`json`)
|
||||||
|
}
|
||||||
|
return tag
|
||||||
|
}
|
||||||
|
|
||||||
|
func structField(field reflect.StructField) *StructField {
|
||||||
|
tag := getTag(field)
|
||||||
|
fieldName := strings.ToLower(field.Name)
|
||||||
|
options := strings.Split(tag, ",")
|
||||||
|
if len(options) > 0 {
|
||||||
|
if options[0] != "" {
|
||||||
|
fieldName = options[0]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sf := &StructField{
|
||||||
|
FieldName: field.Name,
|
||||||
|
RenderName: fieldName,
|
||||||
|
}
|
||||||
|
if len(options) > 1 {
|
||||||
|
for _, opt := range options[1:] {
|
||||||
|
switch {
|
||||||
|
case opt == "omitempty":
|
||||||
|
sf.IsOmitEmpty = true
|
||||||
|
case opt == "omitzero":
|
||||||
|
sf.IsOmitZero = true
|
||||||
|
case opt == "flow":
|
||||||
|
sf.IsFlow = true
|
||||||
|
case opt == "inline":
|
||||||
|
sf.IsInline = true
|
||||||
|
case strings.HasPrefix(opt, "anchor"):
|
||||||
|
anchor := strings.Split(opt, "=")
|
||||||
|
if len(anchor) > 1 {
|
||||||
|
sf.AnchorName = anchor[1]
|
||||||
|
} else {
|
||||||
|
sf.IsAutoAnchor = true
|
||||||
|
}
|
||||||
|
case strings.HasPrefix(opt, "alias"):
|
||||||
|
alias := strings.Split(opt, "=")
|
||||||
|
if len(alias) > 1 {
|
||||||
|
sf.AliasName = alias[1]
|
||||||
|
} else {
|
||||||
|
sf.IsAutoAlias = true
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sf
|
||||||
|
}
|
||||||
|
|
||||||
|
func isIgnoredStructField(field reflect.StructField) bool {
|
||||||
|
if field.PkgPath != "" && !field.Anonymous {
|
||||||
|
// private field
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return getTag(field) == "-"
|
||||||
|
}
|
||||||
|
|
||||||
|
type StructFieldMap map[string]*StructField
|
||||||
|
|
||||||
|
func (m StructFieldMap) isIncludedRenderName(name string) bool {
|
||||||
|
for _, v := range m {
|
||||||
|
if !v.IsInline && v.RenderName == name {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m StructFieldMap) hasMergeProperty() bool {
|
||||||
|
for _, v := range m {
|
||||||
|
if v.IsOmitEmpty && v.IsInline && v.IsAutoAlias {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func structFieldMap(structType reflect.Type) (StructFieldMap, error) {
|
||||||
|
fieldMap := StructFieldMap{}
|
||||||
|
renderNameMap := map[string]struct{}{}
|
||||||
|
for i := 0; i < structType.NumField(); i++ {
|
||||||
|
field := structType.Field(i)
|
||||||
|
if isIgnoredStructField(field) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
sf := structField(field)
|
||||||
|
if _, exists := renderNameMap[sf.RenderName]; exists {
|
||||||
|
return nil, fmt.Errorf("duplicated struct field name %s", sf.RenderName)
|
||||||
|
}
|
||||||
|
fieldMap[sf.FieldName] = sf
|
||||||
|
renderNameMap[sf.RenderName] = struct{}{}
|
||||||
|
}
|
||||||
|
return fieldMap, nil
|
||||||
|
}
|
||||||
1177
vendor/github.com/goccy/go-yaml/token/token.go
generated
vendored
Normal file
1177
vendor/github.com/goccy/go-yaml/token/token.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
13
vendor/github.com/goccy/go-yaml/validate.go
generated
vendored
Normal file
13
vendor/github.com/goccy/go-yaml/validate.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
// StructValidator need to implement Struct method only
|
||||||
|
// ( see https://pkg.go.dev/github.com/go-playground/validator/v10#Validate.Struct )
|
||||||
|
type StructValidator interface {
|
||||||
|
Struct(interface{}) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldError need to implement StructField method only
|
||||||
|
// ( see https://pkg.go.dev/github.com/go-playground/validator/v10#FieldError )
|
||||||
|
type FieldError interface {
|
||||||
|
StructField() string
|
||||||
|
}
|
||||||
357
vendor/github.com/goccy/go-yaml/yaml.go
generated
vendored
Normal file
357
vendor/github.com/goccy/go-yaml/yaml.go
generated
vendored
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
package yaml
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/goccy/go-yaml/ast"
|
||||||
|
"github.com/goccy/go-yaml/internal/errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
// BytesMarshaler interface may be implemented by types to customize their
|
||||||
|
// behavior when being marshaled into a YAML document. The returned value
|
||||||
|
// is marshaled in place of the original value implementing Marshaler.
|
||||||
|
//
|
||||||
|
// If an error is returned by MarshalYAML, the marshaling procedure stops
|
||||||
|
// and returns with the provided error.
|
||||||
|
type BytesMarshaler interface {
|
||||||
|
MarshalYAML() ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesMarshalerContext interface use BytesMarshaler with context.Context.
|
||||||
|
type BytesMarshalerContext interface {
|
||||||
|
MarshalYAML(context.Context) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceMarshaler interface has MarshalYAML compatible with github.com/go-yaml/yaml package.
|
||||||
|
type InterfaceMarshaler interface {
|
||||||
|
MarshalYAML() (interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceMarshalerContext interface use InterfaceMarshaler with context.Context.
|
||||||
|
type InterfaceMarshalerContext interface {
|
||||||
|
MarshalYAML(context.Context) (interface{}, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesUnmarshaler interface may be implemented by types to customize their
|
||||||
|
// behavior when being unmarshaled from a YAML document.
|
||||||
|
type BytesUnmarshaler interface {
|
||||||
|
UnmarshalYAML([]byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// BytesUnmarshalerContext interface use BytesUnmarshaler with context.Context.
|
||||||
|
type BytesUnmarshalerContext interface {
|
||||||
|
UnmarshalYAML(context.Context, []byte) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceUnmarshaler interface has UnmarshalYAML compatible with github.com/go-yaml/yaml package.
|
||||||
|
type InterfaceUnmarshaler interface {
|
||||||
|
UnmarshalYAML(func(interface{}) error) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// InterfaceUnmarshalerContext interface use InterfaceUnmarshaler with context.Context.
|
||||||
|
type InterfaceUnmarshalerContext interface {
|
||||||
|
UnmarshalYAML(context.Context, func(interface{}) error) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeUnmarshaler interface is similar to BytesUnmarshaler but provide related AST node instead of raw YAML source.
|
||||||
|
type NodeUnmarshaler interface {
|
||||||
|
UnmarshalYAML(ast.Node) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeUnmarshalerContext interface is similar to BytesUnmarshaler but provide related AST node instead of raw YAML source.
|
||||||
|
type NodeUnmarshalerContext interface {
|
||||||
|
UnmarshalYAML(context.Context, ast.Node) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapItem is an item in a MapSlice.
|
||||||
|
type MapItem struct {
|
||||||
|
Key, Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MapSlice encodes and decodes as a YAML map.
|
||||||
|
// The order of keys is preserved when encoding and decoding.
|
||||||
|
type MapSlice []MapItem
|
||||||
|
|
||||||
|
// ToMap convert to map[interface{}]interface{}.
|
||||||
|
func (s MapSlice) ToMap() map[interface{}]interface{} {
|
||||||
|
v := map[interface{}]interface{}{}
|
||||||
|
for _, item := range s {
|
||||||
|
v[item.Key] = item.Value
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
// Marshal serializes the value provided into a YAML document. The structure
|
||||||
|
// of the generated document will reflect the structure of the value itself.
|
||||||
|
// Maps and pointers (to struct, string, int, etc) are accepted as the in value.
|
||||||
|
//
|
||||||
|
// Struct fields are only marshaled if they are exported (have an upper case
|
||||||
|
// first letter), and are marshaled using the field name lowercased as the
|
||||||
|
// default key. Custom keys may be defined via the "yaml" name in the field
|
||||||
|
// tag: the content preceding the first comma is used as the key, and the
|
||||||
|
// following comma-separated options are used to tweak the marshaling process.
|
||||||
|
// Conflicting names result in a runtime error.
|
||||||
|
//
|
||||||
|
// The field tag format accepted is:
|
||||||
|
//
|
||||||
|
// `(...) yaml:"[<key>][,<flag1>[,<flag2>]]" (...)`
|
||||||
|
//
|
||||||
|
// The following flags are currently supported:
|
||||||
|
//
|
||||||
|
// omitempty Only include the field if it's not set to the zero
|
||||||
|
// value for the type or to empty slices or maps.
|
||||||
|
// Zero valued structs will be omitted if all their public
|
||||||
|
// fields are zero, unless they implement an IsZero
|
||||||
|
// method (see the IsZeroer interface type), in which
|
||||||
|
// case the field will be included if that method returns true.
|
||||||
|
// Note that this definition is slightly different from the Go's
|
||||||
|
// encoding/json 'omitempty' definition. It combines some elements
|
||||||
|
// of 'omitempty' and 'omitzero'. See https://github.com/goccy/go-yaml/issues/695.
|
||||||
|
//
|
||||||
|
// omitzero The omitzero tag behaves in the same way as the interpretation of the omitzero tag in the encoding/json library.
|
||||||
|
// 1) If the field type has an "IsZero() bool" method, that will be used to determine whether the value is zero.
|
||||||
|
// 2) Otherwise, the value is zero if it is the zero value for its type.
|
||||||
|
//
|
||||||
|
// flow Marshal using a flow style (useful for structs,
|
||||||
|
// sequences and maps).
|
||||||
|
//
|
||||||
|
// inline Inline the field, which must be a struct or a map,
|
||||||
|
// causing all of its fields or keys to be processed as if
|
||||||
|
// they were part of the outer struct. For maps, keys must
|
||||||
|
// not conflict with the yaml keys of other struct fields.
|
||||||
|
//
|
||||||
|
// anchor Marshal with anchor. If want to define anchor name explicitly, use anchor=name style.
|
||||||
|
// Otherwise, if used 'anchor' name only, used the field name lowercased as the anchor name
|
||||||
|
//
|
||||||
|
// alias Marshal with alias. If want to define alias name explicitly, use alias=name style.
|
||||||
|
// Otherwise, If omitted alias name and the field type is pointer type,
|
||||||
|
// assigned anchor name automatically from same pointer address.
|
||||||
|
//
|
||||||
|
// In addition, if the key is "-", the field is ignored.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// type T struct {
|
||||||
|
// F int `yaml:"a,omitempty"`
|
||||||
|
// B int
|
||||||
|
// }
|
||||||
|
// yaml.Marshal(&T{B: 2}) // Returns "b: 2\n"
|
||||||
|
// yaml.Marshal(&T{F: 1}) // Returns "a: 1\nb: 0\n"
|
||||||
|
func Marshal(v interface{}) ([]byte, error) {
|
||||||
|
return MarshalWithOptions(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalWithOptions serializes the value provided into a YAML document with EncodeOptions.
|
||||||
|
func MarshalWithOptions(v interface{}, opts ...EncodeOption) ([]byte, error) {
|
||||||
|
return MarshalContext(context.Background(), v, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// MarshalContext serializes the value provided into a YAML document with context.Context and EncodeOptions.
|
||||||
|
func MarshalContext(ctx context.Context, v interface{}, opts ...EncodeOption) ([]byte, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := NewEncoder(&buf, opts...).EncodeContext(ctx, v); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return buf.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValueToNode convert from value to ast.Node.
|
||||||
|
func ValueToNode(v interface{}, opts ...EncodeOption) (ast.Node, error) {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
node, err := NewEncoder(&buf, opts...).EncodeToNode(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return node, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unmarshal decodes the first document found within the in byte slice
|
||||||
|
// and assigns decoded values into the out value.
|
||||||
|
//
|
||||||
|
// Struct fields are only unmarshalled if they are exported (have an
|
||||||
|
// upper case first letter), and are unmarshalled using the field name
|
||||||
|
// lowercased as the default key. Custom keys may be defined via the
|
||||||
|
// "yaml" name in the field tag: the content preceding the first comma
|
||||||
|
// is used as the key, and the following comma-separated options are
|
||||||
|
// used to tweak the marshaling process (see Marshal).
|
||||||
|
// Conflicting names result in a runtime error.
|
||||||
|
//
|
||||||
|
// For example:
|
||||||
|
//
|
||||||
|
// type T struct {
|
||||||
|
// F int `yaml:"a,omitempty"`
|
||||||
|
// B int
|
||||||
|
// }
|
||||||
|
// var t T
|
||||||
|
// yaml.Unmarshal([]byte("a: 1\nb: 2"), &t)
|
||||||
|
//
|
||||||
|
// See the documentation of Marshal for the format of tags and a list of
|
||||||
|
// supported tag options.
|
||||||
|
func Unmarshal(data []byte, v interface{}) error {
|
||||||
|
return UnmarshalWithOptions(data, v)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalWithOptions decodes with DecodeOptions the first document found within the in byte slice
|
||||||
|
// and assigns decoded values into the out value.
|
||||||
|
func UnmarshalWithOptions(data []byte, v interface{}, opts ...DecodeOption) error {
|
||||||
|
return UnmarshalContext(context.Background(), data, v, opts...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalContext decodes with context.Context and DecodeOptions.
|
||||||
|
func UnmarshalContext(ctx context.Context, data []byte, v interface{}, opts ...DecodeOption) error {
|
||||||
|
dec := NewDecoder(bytes.NewBuffer(data), opts...)
|
||||||
|
if err := dec.DecodeContext(ctx, v); err != nil {
|
||||||
|
if err == io.EOF {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NodeToValue converts node to the value pointed to by v.
|
||||||
|
func NodeToValue(node ast.Node, v interface{}, opts ...DecodeOption) error {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if err := NewDecoder(&buf, opts...).DecodeFromNode(node, v); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// FormatError is a utility function that takes advantage of the metadata
|
||||||
|
// stored in the errors returned by this package's parser.
|
||||||
|
//
|
||||||
|
// If the second argument `colored` is true, the error message is colorized.
|
||||||
|
// If the third argument `inclSource` is true, the error message will
|
||||||
|
// contain snippets of the YAML source that was used.
|
||||||
|
func FormatError(e error, colored, inclSource bool) string {
|
||||||
|
var yamlErr Error
|
||||||
|
if errors.As(e, &yamlErr) {
|
||||||
|
return yamlErr.FormatError(colored, inclSource)
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.Error()
|
||||||
|
}
|
||||||
|
|
||||||
|
// YAMLToJSON convert YAML bytes to JSON.
|
||||||
|
func YAMLToJSON(bytes []byte) ([]byte, error) {
|
||||||
|
var v interface{}
|
||||||
|
if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out, err := MarshalWithOptions(v, JSON())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSONToYAML convert JSON bytes to YAML.
|
||||||
|
func JSONToYAML(bytes []byte) ([]byte, error) {
|
||||||
|
var v interface{}
|
||||||
|
if err := UnmarshalWithOptions(bytes, &v, UseOrderedMap()); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
out, err := Marshal(v)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return out, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
globalCustomMarshalerMu sync.Mutex
|
||||||
|
globalCustomUnmarshalerMu sync.Mutex
|
||||||
|
globalCustomMarshalerMap = map[reflect.Type]func(context.Context, interface{}) ([]byte, error){}
|
||||||
|
globalCustomUnmarshalerMap = map[reflect.Type]func(context.Context, interface{}, []byte) error{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// RegisterCustomMarshaler overrides any encoding process for the type specified in generics.
|
||||||
|
// If you want to switch the behavior for each encoder, use `CustomMarshaler` defined as EncodeOption.
|
||||||
|
//
|
||||||
|
// NOTE: If type T implements MarshalYAML for pointer receiver, the type specified in RegisterCustomMarshaler must be *T.
|
||||||
|
// If RegisterCustomMarshaler and CustomMarshaler of EncodeOption are specified for the same type,
|
||||||
|
// the CustomMarshaler specified in EncodeOption takes precedence.
|
||||||
|
func RegisterCustomMarshaler[T any](marshaler func(T) ([]byte, error)) {
|
||||||
|
globalCustomMarshalerMu.Lock()
|
||||||
|
defer globalCustomMarshalerMu.Unlock()
|
||||||
|
|
||||||
|
var typ T
|
||||||
|
globalCustomMarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}) ([]byte, error) {
|
||||||
|
return marshaler(v.(T))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterCustomMarshalerContext overrides any encoding process for the type specified in generics.
|
||||||
|
// Similar to RegisterCustomMarshalerContext, but allows passing a context to the unmarshaler function.
|
||||||
|
func RegisterCustomMarshalerContext[T any](marshaler func(context.Context, T) ([]byte, error)) {
|
||||||
|
globalCustomMarshalerMu.Lock()
|
||||||
|
defer globalCustomMarshalerMu.Unlock()
|
||||||
|
|
||||||
|
var typ T
|
||||||
|
globalCustomMarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}) ([]byte, error) {
|
||||||
|
return marshaler(ctx, v.(T))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterCustomUnmarshaler overrides any decoding process for the type specified in generics.
|
||||||
|
// If you want to switch the behavior for each decoder, use `CustomUnmarshaler` defined as DecodeOption.
|
||||||
|
//
|
||||||
|
// NOTE: If RegisterCustomUnmarshaler and CustomUnmarshaler of DecodeOption are specified for the same type,
|
||||||
|
// the CustomUnmarshaler specified in DecodeOption takes precedence.
|
||||||
|
func RegisterCustomUnmarshaler[T any](unmarshaler func(*T, []byte) error) {
|
||||||
|
globalCustomUnmarshalerMu.Lock()
|
||||||
|
defer globalCustomUnmarshalerMu.Unlock()
|
||||||
|
|
||||||
|
var typ *T
|
||||||
|
globalCustomUnmarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}, b []byte) error {
|
||||||
|
return unmarshaler(v.(*T), b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RegisterCustomUnmarshalerContext overrides any decoding process for the type specified in generics.
|
||||||
|
// Similar to RegisterCustomUnmarshalerContext, but allows passing a context to the unmarshaler function.
|
||||||
|
func RegisterCustomUnmarshalerContext[T any](unmarshaler func(context.Context, *T, []byte) error) {
|
||||||
|
globalCustomUnmarshalerMu.Lock()
|
||||||
|
defer globalCustomUnmarshalerMu.Unlock()
|
||||||
|
|
||||||
|
var typ *T
|
||||||
|
globalCustomUnmarshalerMap[reflect.TypeOf(typ)] = func(ctx context.Context, v interface{}, b []byte) error {
|
||||||
|
return unmarshaler(ctx, v.(*T), b)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// RawMessage is a raw encoded YAML value. It implements [BytesMarshaler] and
|
||||||
|
// [BytesUnmarshaler] and can be used to delay YAML decoding or precompute a YAML
|
||||||
|
// encoding.
|
||||||
|
// It also implements [json.Marshaler] and [json.Unmarshaler].
|
||||||
|
//
|
||||||
|
// This is similar to [json.RawMessage] in the stdlib.
|
||||||
|
type RawMessage []byte
|
||||||
|
|
||||||
|
func (m RawMessage) MarshalYAML() ([]byte, error) {
|
||||||
|
if m == nil {
|
||||||
|
return []byte("null"), nil
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RawMessage) UnmarshalYAML(dt []byte) error {
|
||||||
|
if m == nil {
|
||||||
|
return errors.New("yaml.RawMessage: UnmarshalYAML on nil pointer")
|
||||||
|
}
|
||||||
|
*m = append((*m)[0:0], dt...)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RawMessage) UnmarshalJSON(b []byte) error {
|
||||||
|
return m.UnmarshalYAML(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m RawMessage) MarshalJSON() ([]byte, error) {
|
||||||
|
return YAMLToJSON(m)
|
||||||
|
}
|
||||||
4
vendor/github.com/sirupsen/logrus/.gitignore
generated
vendored
Normal file
4
vendor/github.com/sirupsen/logrus/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
logrus
|
||||||
|
vendor
|
||||||
|
|
||||||
|
.idea/
|
||||||
67
vendor/github.com/sirupsen/logrus/.golangci.yml
generated
vendored
Normal file
67
vendor/github.com/sirupsen/logrus/.golangci.yml
generated
vendored
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
version: "2"
|
||||||
|
run:
|
||||||
|
tests: false
|
||||||
|
linters:
|
||||||
|
enable:
|
||||||
|
- asasalint
|
||||||
|
- asciicheck
|
||||||
|
- bidichk
|
||||||
|
- bodyclose
|
||||||
|
- contextcheck
|
||||||
|
- durationcheck
|
||||||
|
- errchkjson
|
||||||
|
- errorlint
|
||||||
|
- exhaustive
|
||||||
|
- gocheckcompilerdirectives
|
||||||
|
- gochecksumtype
|
||||||
|
- gosec
|
||||||
|
- gosmopolitan
|
||||||
|
- loggercheck
|
||||||
|
- makezero
|
||||||
|
- musttag
|
||||||
|
- nilerr
|
||||||
|
- nilnesserr
|
||||||
|
- noctx
|
||||||
|
- protogetter
|
||||||
|
- reassign
|
||||||
|
- recvcheck
|
||||||
|
- rowserrcheck
|
||||||
|
- spancheck
|
||||||
|
- sqlclosecheck
|
||||||
|
- testifylint
|
||||||
|
- unparam
|
||||||
|
- zerologlint
|
||||||
|
disable:
|
||||||
|
- prealloc
|
||||||
|
settings:
|
||||||
|
errcheck:
|
||||||
|
check-type-assertions: false
|
||||||
|
check-blank: false
|
||||||
|
lll:
|
||||||
|
line-length: 100
|
||||||
|
tab-width: 4
|
||||||
|
prealloc:
|
||||||
|
simple: false
|
||||||
|
range-loops: false
|
||||||
|
for-loops: false
|
||||||
|
whitespace:
|
||||||
|
multi-if: false
|
||||||
|
multi-func: false
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
presets:
|
||||||
|
- comments
|
||||||
|
- common-false-positives
|
||||||
|
- legacy
|
||||||
|
- std-error-handling
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
|
formatters:
|
||||||
|
exclusions:
|
||||||
|
generated: lax
|
||||||
|
paths:
|
||||||
|
- third_party$
|
||||||
|
- builtin$
|
||||||
|
- examples$
|
||||||
15
vendor/github.com/sirupsen/logrus/.travis.yml
generated
vendored
Normal file
15
vendor/github.com/sirupsen/logrus/.travis.yml
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
language: go
|
||||||
|
go_import_path: github.com/sirupsen/logrus
|
||||||
|
git:
|
||||||
|
depth: 1
|
||||||
|
env:
|
||||||
|
- GO111MODULE=on
|
||||||
|
go: 1.15.x
|
||||||
|
os: linux
|
||||||
|
install:
|
||||||
|
- ./travis/install.sh
|
||||||
|
script:
|
||||||
|
- cd ci
|
||||||
|
- go run mage.go -v -w ../ crossBuild
|
||||||
|
- go run mage.go -v -w ../ lint
|
||||||
|
- go run mage.go -v -w ../ test
|
||||||
259
vendor/github.com/sirupsen/logrus/CHANGELOG.md
generated
vendored
Normal file
259
vendor/github.com/sirupsen/logrus/CHANGELOG.md
generated
vendored
Normal file
@@ -0,0 +1,259 @@
|
|||||||
|
# 1.8.1
|
||||||
|
Code quality:
|
||||||
|
* move magefile in its own subdir/submodule to remove magefile dependency on logrus consumer
|
||||||
|
* improve timestamp format documentation
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* fix race condition on logger hooks
|
||||||
|
|
||||||
|
|
||||||
|
# 1.8.0
|
||||||
|
|
||||||
|
Correct versioning number replacing v1.7.1.
|
||||||
|
|
||||||
|
# 1.7.1
|
||||||
|
|
||||||
|
Beware this release has introduced a new public API and its semver is therefore incorrect.
|
||||||
|
|
||||||
|
Code quality:
|
||||||
|
* use go 1.15 in travis
|
||||||
|
* use magefile as task runner
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* small fixes about new go 1.13 error formatting system
|
||||||
|
* Fix for long time race condiction with mutating data hooks
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* build support for zos
|
||||||
|
|
||||||
|
# 1.7.0
|
||||||
|
Fixes:
|
||||||
|
* the dependency toward a windows terminal library has been removed
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* a new buffer pool management API has been added
|
||||||
|
* a set of `<LogLevel>Fn()` functions have been added
|
||||||
|
|
||||||
|
# 1.6.0
|
||||||
|
Fixes:
|
||||||
|
* end of line cleanup
|
||||||
|
* revert the entry concurrency bug fix which leads to deadlock under some circumstances
|
||||||
|
* update dependency on go-windows-terminal-sequences to fix a crash with go 1.14
|
||||||
|
|
||||||
|
Features:
|
||||||
|
* add an option to the `TextFormatter` to completely disable fields quoting
|
||||||
|
|
||||||
|
# 1.5.0
|
||||||
|
Code quality:
|
||||||
|
* add golangci linter run on travis
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* add mutex for hooks concurrent access on `Entry` data
|
||||||
|
* caller function field for go1.14
|
||||||
|
* fix build issue for gopherjs target
|
||||||
|
|
||||||
|
Feature:
|
||||||
|
* add an hooks/writer sub-package whose goal is to split output on different stream depending on the trace level
|
||||||
|
* add a `DisableHTMLEscape` option in the `JSONFormatter`
|
||||||
|
* add `ForceQuote` and `PadLevelText` options in the `TextFormatter`
|
||||||
|
|
||||||
|
# 1.4.2
|
||||||
|
* Fixes build break for plan9, nacl, solaris
|
||||||
|
# 1.4.1
|
||||||
|
This new release introduces:
|
||||||
|
* Enhance TextFormatter to not print caller information when they are empty (#944)
|
||||||
|
* Remove dependency on golang.org/x/crypto (#932, #943)
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* Fix Entry.WithContext method to return a copy of the initial entry (#941)
|
||||||
|
|
||||||
|
# 1.4.0
|
||||||
|
This new release introduces:
|
||||||
|
* Add `DeferExitHandler`, similar to `RegisterExitHandler` but prepending the handler to the list of handlers (semantically like `defer`) (#848).
|
||||||
|
* Add `CallerPrettyfier` to `JSONFormatter` and `TextFormatter` (#909, #911)
|
||||||
|
* Add `Entry.WithContext()` and `Entry.Context`, to set a context on entries to be used e.g. in hooks (#919).
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* Fix wrong method calls `Logger.Print` and `Logger.Warningln` (#893).
|
||||||
|
* Update `Entry.Logf` to not do string formatting unless the log level is enabled (#903)
|
||||||
|
* Fix infinite recursion on unknown `Level.String()` (#907)
|
||||||
|
* Fix race condition in `getCaller` (#916).
|
||||||
|
|
||||||
|
|
||||||
|
# 1.3.0
|
||||||
|
This new release introduces:
|
||||||
|
* Log, Logf, Logln functions for Logger and Entry that take a Level
|
||||||
|
|
||||||
|
Fixes:
|
||||||
|
* Building prometheus node_exporter on AIX (#840)
|
||||||
|
* Race condition in TextFormatter (#468)
|
||||||
|
* Travis CI import path (#868)
|
||||||
|
* Remove coloured output on Windows (#862)
|
||||||
|
* Pointer to func as field in JSONFormatter (#870)
|
||||||
|
* Properly marshal Levels (#873)
|
||||||
|
|
||||||
|
# 1.2.0
|
||||||
|
This new release introduces:
|
||||||
|
* A new method `SetReportCaller` in the `Logger` to enable the file, line and calling function from which the trace has been issued
|
||||||
|
* A new trace level named `Trace` whose level is below `Debug`
|
||||||
|
* A configurable exit function to be called upon a Fatal trace
|
||||||
|
* The `Level` object now implements `encoding.TextUnmarshaler` interface
|
||||||
|
|
||||||
|
# 1.1.1
|
||||||
|
This is a bug fix release.
|
||||||
|
* fix the build break on Solaris
|
||||||
|
* don't drop a whole trace in JSONFormatter when a field param is a function pointer which can not be serialized
|
||||||
|
|
||||||
|
# 1.1.0
|
||||||
|
This new release introduces:
|
||||||
|
* several fixes:
|
||||||
|
* a fix for a race condition on entry formatting
|
||||||
|
* proper cleanup of previously used entries before putting them back in the pool
|
||||||
|
* the extra new line at the end of message in text formatter has been removed
|
||||||
|
* a new global public API to check if a level is activated: IsLevelEnabled
|
||||||
|
* the following methods have been added to the Logger object
|
||||||
|
* IsLevelEnabled
|
||||||
|
* SetFormatter
|
||||||
|
* SetOutput
|
||||||
|
* ReplaceHooks
|
||||||
|
* introduction of go module
|
||||||
|
* an indent configuration for the json formatter
|
||||||
|
* output colour support for windows
|
||||||
|
* the field sort function is now configurable for text formatter
|
||||||
|
* the CLICOLOR and CLICOLOR\_FORCE environment variable support in text formater
|
||||||
|
|
||||||
|
# 1.0.6
|
||||||
|
|
||||||
|
This new release introduces:
|
||||||
|
* a new api WithTime which allows to easily force the time of the log entry
|
||||||
|
which is mostly useful for logger wrapper
|
||||||
|
* a fix reverting the immutability of the entry given as parameter to the hooks
|
||||||
|
a new configuration field of the json formatter in order to put all the fields
|
||||||
|
in a nested dictionary
|
||||||
|
* a new SetOutput method in the Logger
|
||||||
|
* a new configuration of the textformatter to configure the name of the default keys
|
||||||
|
* a new configuration of the text formatter to disable the level truncation
|
||||||
|
|
||||||
|
# 1.0.5
|
||||||
|
|
||||||
|
* Fix hooks race (#707)
|
||||||
|
* Fix panic deadlock (#695)
|
||||||
|
|
||||||
|
# 1.0.4
|
||||||
|
|
||||||
|
* Fix race when adding hooks (#612)
|
||||||
|
* Fix terminal check in AppEngine (#635)
|
||||||
|
|
||||||
|
# 1.0.3
|
||||||
|
|
||||||
|
* Replace example files with testable examples
|
||||||
|
|
||||||
|
# 1.0.2
|
||||||
|
|
||||||
|
* bug: quote non-string values in text formatter (#583)
|
||||||
|
* Make (*Logger) SetLevel a public method
|
||||||
|
|
||||||
|
# 1.0.1
|
||||||
|
|
||||||
|
* bug: fix escaping in text formatter (#575)
|
||||||
|
|
||||||
|
# 1.0.0
|
||||||
|
|
||||||
|
* Officially changed name to lower-case
|
||||||
|
* bug: colors on Windows 10 (#541)
|
||||||
|
* bug: fix race in accessing level (#512)
|
||||||
|
|
||||||
|
# 0.11.5
|
||||||
|
|
||||||
|
* feature: add writer and writerlevel to entry (#372)
|
||||||
|
|
||||||
|
# 0.11.4
|
||||||
|
|
||||||
|
* bug: fix undefined variable on solaris (#493)
|
||||||
|
|
||||||
|
# 0.11.3
|
||||||
|
|
||||||
|
* formatter: configure quoting of empty values (#484)
|
||||||
|
* formatter: configure quoting character (default is `"`) (#484)
|
||||||
|
* bug: fix not importing io correctly in non-linux environments (#481)
|
||||||
|
|
||||||
|
# 0.11.2
|
||||||
|
|
||||||
|
* bug: fix windows terminal detection (#476)
|
||||||
|
|
||||||
|
# 0.11.1
|
||||||
|
|
||||||
|
* bug: fix tty detection with custom out (#471)
|
||||||
|
|
||||||
|
# 0.11.0
|
||||||
|
|
||||||
|
* performance: Use bufferpool to allocate (#370)
|
||||||
|
* terminal: terminal detection for app-engine (#343)
|
||||||
|
* feature: exit handler (#375)
|
||||||
|
|
||||||
|
# 0.10.0
|
||||||
|
|
||||||
|
* feature: Add a test hook (#180)
|
||||||
|
* feature: `ParseLevel` is now case-insensitive (#326)
|
||||||
|
* feature: `FieldLogger` interface that generalizes `Logger` and `Entry` (#308)
|
||||||
|
* performance: avoid re-allocations on `WithFields` (#335)
|
||||||
|
|
||||||
|
# 0.9.0
|
||||||
|
|
||||||
|
* logrus/text_formatter: don't emit empty msg
|
||||||
|
* logrus/hooks/airbrake: move out of main repository
|
||||||
|
* logrus/hooks/sentry: move out of main repository
|
||||||
|
* logrus/hooks/papertrail: move out of main repository
|
||||||
|
* logrus/hooks/bugsnag: move out of main repository
|
||||||
|
* logrus/core: run tests with `-race`
|
||||||
|
* logrus/core: detect TTY based on `stderr`
|
||||||
|
* logrus/core: support `WithError` on logger
|
||||||
|
* logrus/core: Solaris support
|
||||||
|
|
||||||
|
# 0.8.7
|
||||||
|
|
||||||
|
* logrus/core: fix possible race (#216)
|
||||||
|
* logrus/doc: small typo fixes and doc improvements
|
||||||
|
|
||||||
|
|
||||||
|
# 0.8.6
|
||||||
|
|
||||||
|
* hooks/raven: allow passing an initialized client
|
||||||
|
|
||||||
|
# 0.8.5
|
||||||
|
|
||||||
|
* logrus/core: revert #208
|
||||||
|
|
||||||
|
# 0.8.4
|
||||||
|
|
||||||
|
* formatter/text: fix data race (#218)
|
||||||
|
|
||||||
|
# 0.8.3
|
||||||
|
|
||||||
|
* logrus/core: fix entry log level (#208)
|
||||||
|
* logrus/core: improve performance of text formatter by 40%
|
||||||
|
* logrus/core: expose `LevelHooks` type
|
||||||
|
* logrus/core: add support for DragonflyBSD and NetBSD
|
||||||
|
* formatter/text: print structs more verbosely
|
||||||
|
|
||||||
|
# 0.8.2
|
||||||
|
|
||||||
|
* logrus: fix more Fatal family functions
|
||||||
|
|
||||||
|
# 0.8.1
|
||||||
|
|
||||||
|
* logrus: fix not exiting on `Fatalf` and `Fatalln`
|
||||||
|
|
||||||
|
# 0.8.0
|
||||||
|
|
||||||
|
* logrus: defaults to stderr instead of stdout
|
||||||
|
* hooks/sentry: add special field for `*http.Request`
|
||||||
|
* formatter/text: ignore Windows for colors
|
||||||
|
|
||||||
|
# 0.7.3
|
||||||
|
|
||||||
|
* formatter/\*: allow configuration of timestamp layout
|
||||||
|
|
||||||
|
# 0.7.2
|
||||||
|
|
||||||
|
* formatter/text: Add configuration option for time format (#158)
|
||||||
21
vendor/github.com/sirupsen/logrus/LICENSE
generated
vendored
Normal file
21
vendor/github.com/sirupsen/logrus/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2014 Simon Eskildsen
|
||||||
|
|
||||||
|
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.
|
||||||
519
vendor/github.com/sirupsen/logrus/README.md
generated
vendored
Normal file
519
vendor/github.com/sirupsen/logrus/README.md
generated
vendored
Normal file
@@ -0,0 +1,519 @@
|
|||||||
|
# Logrus <img src="http://i.imgur.com/hTeVwmJ.png" width="40" height="40" alt=":walrus:" class="emoji" title=":walrus:"/> [](https://github.com/sirupsen/logrus/actions?query=workflow%3ACI) [](https://pkg.go.dev/github.com/sirupsen/logrus)
|
||||||
|
|
||||||
|
Logrus is a structured logger for Go (golang), completely API compatible with
|
||||||
|
the standard library logger.
|
||||||
|
|
||||||
|
**Logrus is in maintenance-mode.** We will not be introducing new features. It's
|
||||||
|
simply too hard to do in a way that won't break many people's projects, which is
|
||||||
|
the last thing you want from your Logging library (again...).
|
||||||
|
|
||||||
|
This does not mean Logrus is dead. Logrus will continue to be maintained for
|
||||||
|
security, (backwards compatible) bug fixes, and performance (where we are
|
||||||
|
limited by the interface).
|
||||||
|
|
||||||
|
I believe Logrus' biggest contribution is to have played a part in today's
|
||||||
|
widespread use of structured logging in Golang. There doesn't seem to be a
|
||||||
|
reason to do a major, breaking iteration into Logrus V2, since the fantastic Go
|
||||||
|
community has built those independently. Many fantastic alternatives have sprung
|
||||||
|
up. Logrus would look like those, had it been re-designed with what we know
|
||||||
|
about structured logging in Go today. Check out, for example,
|
||||||
|
[Zerolog][zerolog], [Zap][zap], and [Apex][apex].
|
||||||
|
|
||||||
|
[zerolog]: https://github.com/rs/zerolog
|
||||||
|
[zap]: https://github.com/uber-go/zap
|
||||||
|
[apex]: https://github.com/apex/log
|
||||||
|
|
||||||
|
**Seeing weird case-sensitive problems?** It's in the past been possible to
|
||||||
|
import Logrus as both upper- and lower-case. Due to the Go package environment,
|
||||||
|
this caused issues in the community and we needed a standard. Some environments
|
||||||
|
experienced problems with the upper-case variant, so the lower-case was decided.
|
||||||
|
Everything using `logrus` will need to use the lower-case:
|
||||||
|
`github.com/sirupsen/logrus`. Any package that isn't, should be changed.
|
||||||
|
|
||||||
|
To fix Glide, see [these
|
||||||
|
comments](https://github.com/sirupsen/logrus/issues/553#issuecomment-306591437).
|
||||||
|
For an in-depth explanation of the casing issue, see [this
|
||||||
|
comment](https://github.com/sirupsen/logrus/issues/570#issuecomment-313933276).
|
||||||
|
|
||||||
|
Nicely color-coded in development (when a TTY is attached, otherwise just
|
||||||
|
plain text):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
With `logrus.SetFormatter(&logrus.JSONFormatter{})`, for easy parsing by logstash
|
||||||
|
or Splunk:
|
||||||
|
|
||||||
|
```text
|
||||||
|
{"animal":"walrus","level":"info","msg":"A group of walrus emerges from the
|
||||||
|
ocean","size":10,"time":"2014-03-10 19:57:38.562264131 -0400 EDT"}
|
||||||
|
|
||||||
|
{"level":"warning","msg":"The group's number increased tremendously!",
|
||||||
|
"number":122,"omg":true,"time":"2014-03-10 19:57:38.562471297 -0400 EDT"}
|
||||||
|
|
||||||
|
{"animal":"walrus","level":"info","msg":"A giant walrus appears!",
|
||||||
|
"size":10,"time":"2014-03-10 19:57:38.562500591 -0400 EDT"}
|
||||||
|
|
||||||
|
{"animal":"walrus","level":"info","msg":"Tremendously sized cow enters the ocean.",
|
||||||
|
"size":9,"time":"2014-03-10 19:57:38.562527896 -0400 EDT"}
|
||||||
|
|
||||||
|
{"level":"fatal","msg":"The ice breaks!","number":100,"omg":true,
|
||||||
|
"time":"2014-03-10 19:57:38.562543128 -0400 EDT"}
|
||||||
|
```
|
||||||
|
|
||||||
|
With the default `logrus.SetFormatter(&logrus.TextFormatter{})` when a TTY is not
|
||||||
|
attached, the output is compatible with the
|
||||||
|
[logfmt](https://pkg.go.dev/github.com/kr/logfmt) format:
|
||||||
|
|
||||||
|
```text
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=debug msg="Started observing beach" animal=walrus number=8
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=info msg="A group of walrus emerges from the ocean" animal=walrus size=10
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=warning msg="The group's number increased tremendously!" number=122 omg=true
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=debug msg="Temperature changes" temperature=-4
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=panic msg="It's over 9000!" animal=orca size=9009
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=fatal msg="The ice breaks!" err=&{0x2082280c0 map[animal:orca size:9009] 2015-03-26 01:27:38.441574009 -0400 EDT panic It's over 9000!} number=100 omg=true
|
||||||
|
```
|
||||||
|
To ensure this behaviour even if a TTY is attached, set your formatter as follows:
|
||||||
|
|
||||||
|
```go
|
||||||
|
logrus.SetFormatter(&logrus.TextFormatter{
|
||||||
|
DisableColors: true,
|
||||||
|
FullTimestamp: true,
|
||||||
|
})
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Logging Method Name
|
||||||
|
|
||||||
|
If you wish to add the calling method as a field, instruct the logger via:
|
||||||
|
|
||||||
|
```go
|
||||||
|
logrus.SetReportCaller(true)
|
||||||
|
```
|
||||||
|
This adds the caller as 'method' like so:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{"animal":"penguin","level":"fatal","method":"github.com/sirupsen/arcticcreatures.migrate","msg":"a penguin swims by",
|
||||||
|
"time":"2014-03-10 19:57:38.562543129 -0400 EDT"}
|
||||||
|
```
|
||||||
|
|
||||||
|
```text
|
||||||
|
time="2015-03-26T01:27:38-04:00" level=fatal method=github.com/sirupsen/arcticcreatures.migrate msg="a penguin swims by" animal=penguin
|
||||||
|
```
|
||||||
|
Note that this does add measurable overhead - the cost will depend on the version of Go, but is
|
||||||
|
between 20 and 40% in recent tests with 1.6 and 1.7. You can validate this in your
|
||||||
|
environment via benchmarks:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
go test -bench=.*CallerTracing
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Case-sensitivity
|
||||||
|
|
||||||
|
The organization's name was changed to lower-case--and this will not be changed
|
||||||
|
back. If you are getting import conflicts due to case sensitivity, please use
|
||||||
|
the lower-case import: `github.com/sirupsen/logrus`.
|
||||||
|
|
||||||
|
#### Example
|
||||||
|
|
||||||
|
The simplest way to use Logrus is simply the package-level exported logger:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
}).Info("A walrus appears")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that it's completely api-compatible with the stdlib logger, so you can
|
||||||
|
replace your `log` imports everywhere with `log "github.com/sirupsen/logrus"`
|
||||||
|
and you'll now have the flexibility of Logrus. You can customize it all you
|
||||||
|
want:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// Log as JSON instead of the default ASCII formatter.
|
||||||
|
log.SetFormatter(&log.JSONFormatter{})
|
||||||
|
|
||||||
|
// Output to stdout instead of the default stderr
|
||||||
|
// Can be any io.Writer, see below for File example
|
||||||
|
log.SetOutput(os.Stdout)
|
||||||
|
|
||||||
|
// Only log the warning severity or above.
|
||||||
|
log.SetLevel(log.WarnLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A group of walrus emerges from the ocean")
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"omg": true,
|
||||||
|
"number": 122,
|
||||||
|
}).Warn("The group's number increased tremendously!")
|
||||||
|
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"omg": true,
|
||||||
|
"number": 100,
|
||||||
|
}).Fatal("The ice breaks!")
|
||||||
|
|
||||||
|
// A common pattern is to re-use fields between logging statements by re-using
|
||||||
|
// the logrus.Entry returned from WithFields()
|
||||||
|
contextLogger := log.WithFields(log.Fields{
|
||||||
|
"common": "this is a common field",
|
||||||
|
"other": "I also should be logged always",
|
||||||
|
})
|
||||||
|
|
||||||
|
contextLogger.Info("I'll be logged with common and other field")
|
||||||
|
contextLogger.Info("Me too")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For more advanced usage such as logging to multiple locations from the same
|
||||||
|
application, you can also create an instance of the `logrus` Logger:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Create a new instance of the logger. You can have any number of instances.
|
||||||
|
var logger = logrus.New()
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// The API for setting attributes is a little different than the package level
|
||||||
|
// exported logger. See Godoc.
|
||||||
|
logger.Out = os.Stdout
|
||||||
|
|
||||||
|
// You could set this to any `io.Writer` such as a file
|
||||||
|
// file, err := os.OpenFile("logrus.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
|
||||||
|
// if err == nil {
|
||||||
|
// logger.Out = file
|
||||||
|
// } else {
|
||||||
|
// logger.Info("Failed to log to file, using default stderr")
|
||||||
|
// }
|
||||||
|
|
||||||
|
logger.WithFields(logrus.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A group of walrus emerges from the ocean")
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fields
|
||||||
|
|
||||||
|
Logrus encourages careful, structured logging through logging fields instead of
|
||||||
|
long, unparseable error messages. For example, instead of: `logrus.Fatalf("Failed
|
||||||
|
to send event %s to topic %s with key %d")`, you should log the much more
|
||||||
|
discoverable:
|
||||||
|
|
||||||
|
```go
|
||||||
|
logrus.WithFields(logrus.Fields{
|
||||||
|
"event": event,
|
||||||
|
"topic": topic,
|
||||||
|
"key": key,
|
||||||
|
}).Fatal("Failed to send event")
|
||||||
|
```
|
||||||
|
|
||||||
|
We've found this API forces you to think about logging in a way that produces
|
||||||
|
much more useful logging messages. We've been in countless situations where just
|
||||||
|
a single added field to a log statement that was already there would've saved us
|
||||||
|
hours. The `WithFields` call is optional.
|
||||||
|
|
||||||
|
In general, with Logrus using any of the `printf`-family functions should be
|
||||||
|
seen as a hint you should add a field, however, you can still use the
|
||||||
|
`printf`-family functions with Logrus.
|
||||||
|
|
||||||
|
#### Default Fields
|
||||||
|
|
||||||
|
Often it's helpful to have fields _always_ attached to log statements in an
|
||||||
|
application or parts of one. For example, you may want to always log the
|
||||||
|
`request_id` and `user_ip` in the context of a request. Instead of writing
|
||||||
|
`logger.WithFields(logrus.Fields{"request_id": request_id, "user_ip": user_ip})` on
|
||||||
|
every line, you can create a `logrus.Entry` to pass around instead:
|
||||||
|
|
||||||
|
```go
|
||||||
|
requestLogger := logger.WithFields(logrus.Fields{"request_id": request_id, "user_ip": user_ip})
|
||||||
|
requestLogger.Info("something happened on that request") // will log request_id and user_ip
|
||||||
|
requestLogger.Warn("something not great happened")
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Hooks
|
||||||
|
|
||||||
|
You can add hooks for logging levels. For example to send errors to an exception
|
||||||
|
tracking service on `Error`, `Fatal` and `Panic`, info to StatsD or log to
|
||||||
|
multiple places simultaneously, e.g. syslog.
|
||||||
|
|
||||||
|
Logrus comes with [built-in hooks](hooks/). Add those, or your custom hook, in
|
||||||
|
`init`:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/syslog"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
airbrake "gopkg.in/gemnasium/logrus-airbrake-hook.v2"
|
||||||
|
logrus_syslog "github.com/sirupsen/logrus/hooks/syslog"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
|
||||||
|
// Use the Airbrake hook to report errors that have Error severity or above to
|
||||||
|
// an exception tracker. You can create custom hooks, see the Hooks section.
|
||||||
|
logrus.AddHook(airbrake.NewHook(123, "xyz", "production"))
|
||||||
|
|
||||||
|
hook, err := logrus_syslog.NewSyslogHook("udp", "localhost:514", syslog.LOG_INFO, "")
|
||||||
|
if err != nil {
|
||||||
|
logrus.Error("Unable to connect to local syslog daemon")
|
||||||
|
} else {
|
||||||
|
logrus.AddHook(hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
Note: Syslog hooks also support connecting to local syslog (Ex. "/dev/log" or "/var/run/syslog" or "/var/run/log"). For the detail, please check the [syslog hook README](hooks/syslog/README.md).
|
||||||
|
|
||||||
|
A list of currently known service hooks can be found in this wiki [page](https://github.com/sirupsen/logrus/wiki/Hooks)
|
||||||
|
|
||||||
|
|
||||||
|
#### Level logging
|
||||||
|
|
||||||
|
Logrus has seven logging levels: Trace, Debug, Info, Warning, Error, Fatal and Panic.
|
||||||
|
|
||||||
|
```go
|
||||||
|
logrus.Trace("Something very low level.")
|
||||||
|
logrus.Debug("Useful debugging information.")
|
||||||
|
logrus.Info("Something noteworthy happened!")
|
||||||
|
logrus.Warn("You should probably take a look at this.")
|
||||||
|
logrus.Error("Something failed but I'm not quitting.")
|
||||||
|
// Calls os.Exit(1) after logging
|
||||||
|
logrus.Fatal("Bye.")
|
||||||
|
// Calls panic() after logging
|
||||||
|
logrus.Panic("I'm bailing.")
|
||||||
|
```
|
||||||
|
|
||||||
|
You can set the logging level on a `Logger`, then it will only log entries with
|
||||||
|
that severity or anything above it:
|
||||||
|
|
||||||
|
```go
|
||||||
|
// Will log anything that is info or above (warn, error, fatal, panic). Default.
|
||||||
|
logrus.SetLevel(logrus.InfoLevel)
|
||||||
|
```
|
||||||
|
|
||||||
|
It may be useful to set `logrus.Level = logrus.DebugLevel` in a debug or verbose
|
||||||
|
environment if your application has that.
|
||||||
|
|
||||||
|
Note: If you want different log levels for global (`logrus.SetLevel(...)`) and syslog logging, please check the [syslog hook README](hooks/syslog/README.md#different-log-levels-for-local-and-remote-logging).
|
||||||
|
|
||||||
|
#### Entries
|
||||||
|
|
||||||
|
Besides the fields added with `WithField` or `WithFields` some fields are
|
||||||
|
automatically added to all logging events:
|
||||||
|
|
||||||
|
1. `time`. The timestamp when the entry was created.
|
||||||
|
2. `msg`. The logging message passed to `{Info,Warn,Error,Fatal,Panic}` after
|
||||||
|
the `AddFields` call. E.g. `Failed to send event.`
|
||||||
|
3. `level`. The logging level. E.g. `info`.
|
||||||
|
|
||||||
|
#### Environments
|
||||||
|
|
||||||
|
Logrus has no notion of environment.
|
||||||
|
|
||||||
|
If you wish for hooks and formatters to only be used in specific environments,
|
||||||
|
you should handle that yourself. For example, if your application has a global
|
||||||
|
variable `Environment`, which is a string representation of the environment you
|
||||||
|
could do:
|
||||||
|
|
||||||
|
```go
|
||||||
|
import (
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// do something here to set environment depending on an environment variable
|
||||||
|
// or command-line flag
|
||||||
|
if Environment == "production" {
|
||||||
|
logrus.SetFormatter(&logrus.JSONFormatter{})
|
||||||
|
} else {
|
||||||
|
// The TextFormatter is default, you don't actually have to do this.
|
||||||
|
logrus.SetFormatter(&logrus.TextFormatter{})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This configuration is how `logrus` was intended to be used, but JSON in
|
||||||
|
production is mostly only useful if you do log aggregation with tools like
|
||||||
|
Splunk or Logstash.
|
||||||
|
|
||||||
|
#### Formatters
|
||||||
|
|
||||||
|
The built-in logging formatters are:
|
||||||
|
|
||||||
|
* `logrus.TextFormatter`. Logs the event in colors if stdout is a tty, otherwise
|
||||||
|
without colors.
|
||||||
|
* *Note:* to force colored output when there is no TTY, set the `ForceColors`
|
||||||
|
field to `true`. To force no colored output even if there is a TTY set the
|
||||||
|
`DisableColors` field to `true`. For Windows, see
|
||||||
|
[github.com/mattn/go-colorable](https://github.com/mattn/go-colorable).
|
||||||
|
* When colors are enabled, levels are truncated to 4 characters by default. To disable
|
||||||
|
truncation set the `DisableLevelTruncation` field to `true`.
|
||||||
|
* When outputting to a TTY, it's often helpful to visually scan down a column where all the levels are the same width. Setting the `PadLevelText` field to `true` enables this behavior, by adding padding to the level text.
|
||||||
|
* All options are listed in the [generated docs](https://pkg.go.dev/github.com/sirupsen/logrus#TextFormatter).
|
||||||
|
* `logrus.JSONFormatter`. Logs fields as JSON.
|
||||||
|
* All options are listed in the [generated docs](https://pkg.go.dev/github.com/sirupsen/logrus#JSONFormatter).
|
||||||
|
|
||||||
|
Third-party logging formatters:
|
||||||
|
|
||||||
|
* [`FluentdFormatter`](https://github.com/joonix/log). Formats entries that can be parsed by Kubernetes and Google Container Engine.
|
||||||
|
* [`GELF`](https://github.com/fabienm/go-logrus-formatters). Formats entries so they comply to Graylog's [GELF 1.1 specification](http://docs.graylog.org/en/2.4/pages/gelf.html).
|
||||||
|
* [`logstash`](https://github.com/bshuster-repo/logrus-logstash-hook). Logs fields as [Logstash](http://logstash.net) Events.
|
||||||
|
* [`prefixed`](https://github.com/x-cray/logrus-prefixed-formatter). Displays log entry source along with alternative layout.
|
||||||
|
* [`zalgo`](https://github.com/aybabtme/logzalgo). Invoking the Power of Zalgo.
|
||||||
|
* [`nested-logrus-formatter`](https://github.com/antonfisher/nested-logrus-formatter). Converts logrus fields to a nested structure.
|
||||||
|
* [`powerful-logrus-formatter`](https://github.com/zput/zxcTool). get fileName, log's line number and the latest function's name when print log; Save log to files.
|
||||||
|
* [`caption-json-formatter`](https://github.com/nolleh/caption_json_formatter). logrus's message json formatter with human-readable caption added.
|
||||||
|
|
||||||
|
You can define your formatter by implementing the `Formatter` interface,
|
||||||
|
requiring a `Format` method. `Format` takes an `*Entry`. `entry.Data` is a
|
||||||
|
`Fields` type (`map[string]interface{}`) with all your fields as well as the
|
||||||
|
default ones (see Entries section above):
|
||||||
|
|
||||||
|
```go
|
||||||
|
type MyJSONFormatter struct{}
|
||||||
|
|
||||||
|
logrus.SetFormatter(new(MyJSONFormatter))
|
||||||
|
|
||||||
|
func (f *MyJSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
// Note this doesn't include Time, Level and Message which are available on
|
||||||
|
// the Entry. Consult `godoc` on information about those fields or read the
|
||||||
|
// source of the official loggers.
|
||||||
|
serialized, err := json.Marshal(entry.Data)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("Failed to marshal fields to JSON, %w", err)
|
||||||
|
}
|
||||||
|
return append(serialized, '\n'), nil
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Logger as an `io.Writer`
|
||||||
|
|
||||||
|
Logrus can be transformed into an `io.Writer`. That writer is the end of an `io.Pipe` and it is your responsibility to close it.
|
||||||
|
|
||||||
|
```go
|
||||||
|
w := logger.Writer()
|
||||||
|
defer w.Close()
|
||||||
|
|
||||||
|
srv := http.Server{
|
||||||
|
// create a stdlib log.Logger that writes to
|
||||||
|
// logrus.Logger.
|
||||||
|
ErrorLog: log.New(w, "", 0),
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Each line written to that writer will be printed the usual way, using formatters
|
||||||
|
and hooks. The level for those entries is `info`.
|
||||||
|
|
||||||
|
This means that we can override the standard library logger easily:
|
||||||
|
|
||||||
|
```go
|
||||||
|
logger := logrus.New()
|
||||||
|
logger.Formatter = &logrus.JSONFormatter{}
|
||||||
|
|
||||||
|
// Use logrus for standard log output
|
||||||
|
// Note that `log` here references stdlib's log
|
||||||
|
// Not logrus imported under the name `log`.
|
||||||
|
log.SetOutput(logger.Writer())
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Rotation
|
||||||
|
|
||||||
|
Log rotation is not provided with Logrus. Log rotation should be done by an
|
||||||
|
external program (like `logrotate(8)`) that can compress and delete old log
|
||||||
|
entries. It should not be a feature of the application-level logger.
|
||||||
|
|
||||||
|
#### Tools
|
||||||
|
|
||||||
|
| Tool | Description |
|
||||||
|
| ---- | ----------- |
|
||||||
|
|[Logrus Mate](https://github.com/gogap/logrus_mate)|Logrus mate is a tool for Logrus to manage loggers, you can initial logger's level, hook and formatter by config file, the logger will be generated with different configs in different environments.|
|
||||||
|
|[Logrus Viper Helper](https://github.com/heirko/go-contrib/tree/master/logrusHelper)|An Helper around Logrus to wrap with spf13/Viper to load configuration with fangs! And to simplify Logrus configuration use some behavior of [Logrus Mate](https://github.com/gogap/logrus_mate). [sample](https://github.com/heirko/iris-contrib/blob/master/middleware/logrus-logger/example) |
|
||||||
|
|
||||||
|
#### Testing
|
||||||
|
|
||||||
|
Logrus has a built-in facility for asserting the presence of log messages. This is implemented through the `test` hook and provides:
|
||||||
|
|
||||||
|
* decorators for existing logger (`test.NewLocal` and `test.NewGlobal`) which basically just adds the `test` hook
|
||||||
|
* a test logger (`test.NewNullLogger`) that just records log messages (and does not output any):
|
||||||
|
|
||||||
|
```go
|
||||||
|
import(
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/sirupsen/logrus/hooks/test"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestSomething(t*testing.T){
|
||||||
|
logger, hook := test.NewNullLogger()
|
||||||
|
logger.Error("Helloerror")
|
||||||
|
|
||||||
|
assert.Equal(t, 1, len(hook.Entries))
|
||||||
|
assert.Equal(t, logrus.ErrorLevel, hook.LastEntry().Level)
|
||||||
|
assert.Equal(t, "Helloerror", hook.LastEntry().Message)
|
||||||
|
|
||||||
|
hook.Reset()
|
||||||
|
assert.Nil(t, hook.LastEntry())
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Fatal handlers
|
||||||
|
|
||||||
|
Logrus can register one or more functions that will be called when any `fatal`
|
||||||
|
level message is logged. The registered handlers will be executed before
|
||||||
|
logrus performs an `os.Exit(1)`. This behavior may be helpful if callers need
|
||||||
|
to gracefully shut down. Unlike a `panic("Something went wrong...")` call which can be intercepted with a deferred `recover` a call to `os.Exit(1)` can not be intercepted.
|
||||||
|
|
||||||
|
```go
|
||||||
|
// ...
|
||||||
|
handler := func() {
|
||||||
|
// gracefully shut down something...
|
||||||
|
}
|
||||||
|
logrus.RegisterExitHandler(handler)
|
||||||
|
// ...
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Thread safety
|
||||||
|
|
||||||
|
By default, Logger is protected by a mutex for concurrent writes. The mutex is held when calling hooks and writing logs.
|
||||||
|
If you are sure such locking is not needed, you can call logger.SetNoLock() to disable the locking.
|
||||||
|
|
||||||
|
Situations when locking is not needed include:
|
||||||
|
|
||||||
|
* You have no hooks registered, or hooks calling is already thread-safe.
|
||||||
|
|
||||||
|
* Writing to logger.Out is already thread-safe, for example:
|
||||||
|
|
||||||
|
1) logger.Out is protected by locks.
|
||||||
|
|
||||||
|
2) logger.Out is an os.File handler opened with `O_APPEND` flag, and every write is smaller than 4k. (This allows multi-thread/multi-process writing)
|
||||||
|
|
||||||
|
(Refer to http://www.notthewizard.com/2014/06/17/are-files-appends-really-atomic/)
|
||||||
76
vendor/github.com/sirupsen/logrus/alt_exit.go
generated
vendored
Normal file
76
vendor/github.com/sirupsen/logrus/alt_exit.go
generated
vendored
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
// The following code was sourced and modified from the
|
||||||
|
// https://github.com/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 appends a Logrus Exit handler to the list of handlers,
|
||||||
|
// 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)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeferExitHandler prepends a Logrus Exit handler to the list of handlers,
|
||||||
|
// 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 DeferExitHandler(handler func()) {
|
||||||
|
handlers = append([]func(){handler}, handlers...)
|
||||||
|
}
|
||||||
12
vendor/github.com/sirupsen/logrus/appveyor.yml
generated
vendored
Normal file
12
vendor/github.com/sirupsen/logrus/appveyor.yml
generated
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# Minimal stub to satisfy AppVeyor CI
|
||||||
|
version: 1.0.{build}
|
||||||
|
platform: x64
|
||||||
|
shallow_clone: true
|
||||||
|
|
||||||
|
branches:
|
||||||
|
only:
|
||||||
|
- master
|
||||||
|
- main
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- echo "No-op build to satisfy AppVeyor CI"
|
||||||
43
vendor/github.com/sirupsen/logrus/buffer_pool.go
generated
vendored
Normal file
43
vendor/github.com/sirupsen/logrus/buffer_pool.go
generated
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
bufferPool BufferPool
|
||||||
|
)
|
||||||
|
|
||||||
|
type BufferPool interface {
|
||||||
|
Put(*bytes.Buffer)
|
||||||
|
Get() *bytes.Buffer
|
||||||
|
}
|
||||||
|
|
||||||
|
type defaultPool struct {
|
||||||
|
pool *sync.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *defaultPool) Put(buf *bytes.Buffer) {
|
||||||
|
p.pool.Put(buf)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *defaultPool) Get() *bytes.Buffer {
|
||||||
|
return p.pool.Get().(*bytes.Buffer)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBufferPool allows to replace the default logrus buffer pool
|
||||||
|
// to better meets the specific needs of an application.
|
||||||
|
func SetBufferPool(bp BufferPool) {
|
||||||
|
bufferPool = bp
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
SetBufferPool(&defaultPool{
|
||||||
|
pool: &sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
return new(bytes.Buffer)
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
26
vendor/github.com/sirupsen/logrus/doc.go
generated
vendored
Normal file
26
vendor/github.com/sirupsen/logrus/doc.go
generated
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
/*
|
||||||
|
Package logrus is a structured logger for Go, completely API compatible with the standard library logger.
|
||||||
|
|
||||||
|
|
||||||
|
The simplest way to use Logrus is simply the package-level exported logger:
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
log "github.com/sirupsen/logrus"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
log.WithFields(log.Fields{
|
||||||
|
"animal": "walrus",
|
||||||
|
"number": 1,
|
||||||
|
"size": 10,
|
||||||
|
}).Info("A walrus appears")
|
||||||
|
}
|
||||||
|
|
||||||
|
Output:
|
||||||
|
time="2015-09-07T08:48:33Z" level=info msg="A walrus appears" animal=walrus number=1 size=10
|
||||||
|
|
||||||
|
For a full guide visit https://github.com/sirupsen/logrus
|
||||||
|
*/
|
||||||
|
package logrus
|
||||||
445
vendor/github.com/sirupsen/logrus/entry.go
generated
vendored
Normal file
445
vendor/github.com/sirupsen/logrus/entry.go
generated
vendored
Normal file
@@ -0,0 +1,445 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"reflect"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
|
||||||
|
// qualified package name, cached at first use
|
||||||
|
logrusPackage string
|
||||||
|
|
||||||
|
// Positions in the call stack when tracing to report the calling method
|
||||||
|
minimumCallerDepth int
|
||||||
|
|
||||||
|
// Used for caller information initialisation
|
||||||
|
callerInitOnce sync.Once
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
maximumCallerDepth int = 25
|
||||||
|
knownLogrusFrames int = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// start at the bottom of the stack before the package-name cache is primed
|
||||||
|
minimumCallerDepth = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorKey defines the key when adding errors using [WithError], [Logger.WithError].
|
||||||
|
var ErrorKey = "error"
|
||||||
|
|
||||||
|
// Entry is the final or intermediate Logrus logging entry. It contains all
|
||||||
|
// the fields passed with WithField{,s}. It's finally logged when Trace, Debug,
|
||||||
|
// Info, Warn, Error, Fatal or Panic is called on it. These objects can be
|
||||||
|
// reused and passed around as much as you wish to avoid field duplication.
|
||||||
|
//
|
||||||
|
//nolint:recvcheck // the methods of "Entry" use pointer receiver and non-pointer receiver.
|
||||||
|
type Entry struct {
|
||||||
|
Logger *Logger
|
||||||
|
|
||||||
|
// Contains all the fields set by the user.
|
||||||
|
Data Fields
|
||||||
|
|
||||||
|
// Time at which the log entry was created
|
||||||
|
Time time.Time
|
||||||
|
|
||||||
|
// Level the log entry was logged at: Trace, Debug, Info, Warn, Error, Fatal or Panic
|
||||||
|
// This field will be set on entry firing and the value will be equal to the one in Logger struct field.
|
||||||
|
Level Level
|
||||||
|
|
||||||
|
// Calling method, with package name
|
||||||
|
Caller *runtime.Frame
|
||||||
|
|
||||||
|
// Message passed to Trace, Debug, Info, Warn, Error, Fatal or Panic
|
||||||
|
Message string
|
||||||
|
|
||||||
|
// When formatter is called in entry.log(), a Buffer may be set to entry
|
||||||
|
Buffer *bytes.Buffer
|
||||||
|
|
||||||
|
// Contains the context set by the user. Useful for hook processing etc.
|
||||||
|
Context context.Context
|
||||||
|
|
||||||
|
// err may contain a field formatting error
|
||||||
|
err string
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewEntry(logger *Logger) *Entry {
|
||||||
|
return &Entry{
|
||||||
|
Logger: logger,
|
||||||
|
// Default is three fields, plus one optional. Give a little extra room.
|
||||||
|
Data: make(Fields, 6),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Dup() *Entry {
|
||||||
|
data := make(Fields, len(entry.Data))
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, Context: entry.Context, err: entry.err}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bytes returns the bytes representation of this entry from the formatter.
|
||||||
|
func (entry *Entry) Bytes() ([]byte, error) {
|
||||||
|
return entry.Logger.Formatter.Format(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation from the reader and ultimately the
|
||||||
|
// formatter.
|
||||||
|
func (entry *Entry) String() (string, error) {
|
||||||
|
serialized, err := entry.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
str := string(serialized)
|
||||||
|
return str, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithError adds an error as single field (using the key defined in [ErrorKey])
|
||||||
|
// to the Entry.
|
||||||
|
func (entry *Entry) WithError(err error) *Entry {
|
||||||
|
return entry.WithField(ErrorKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContext adds a context to the Entry.
|
||||||
|
func (entry *Entry) WithContext(ctx context.Context) *Entry {
|
||||||
|
dataCopy := make(Fields, len(entry.Data))
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
dataCopy[k] = v
|
||||||
|
}
|
||||||
|
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: entry.Time, err: entry.err, Context: ctx}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithField adds a single field to the Entry.
|
||||||
|
func (entry *Entry) WithField(key string, value interface{}) *Entry {
|
||||||
|
return entry.WithFields(Fields{key: value})
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFields adds a map of fields to the Entry.
|
||||||
|
func (entry *Entry) WithFields(fields Fields) *Entry {
|
||||||
|
data := make(Fields, len(entry.Data)+len(fields))
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
fieldErr := entry.err
|
||||||
|
for k, v := range fields {
|
||||||
|
isErrField := false
|
||||||
|
if t := reflect.TypeOf(v); t != nil {
|
||||||
|
switch {
|
||||||
|
case t.Kind() == reflect.Func, t.Kind() == reflect.Ptr && t.Elem().Kind() == reflect.Func:
|
||||||
|
isErrField = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if isErrField {
|
||||||
|
tmp := fmt.Sprintf("can not add field %q", k)
|
||||||
|
if fieldErr != "" {
|
||||||
|
fieldErr = entry.err + ", " + tmp
|
||||||
|
} else {
|
||||||
|
fieldErr = tmp
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &Entry{Logger: entry.Logger, Data: data, Time: entry.Time, err: fieldErr, Context: entry.Context}
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTime overrides the time of the Entry.
|
||||||
|
func (entry *Entry) WithTime(t time.Time) *Entry {
|
||||||
|
dataCopy := make(Fields, len(entry.Data))
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
dataCopy[k] = v
|
||||||
|
}
|
||||||
|
return &Entry{Logger: entry.Logger, Data: dataCopy, Time: t, err: entry.err, Context: entry.Context}
|
||||||
|
}
|
||||||
|
|
||||||
|
// getPackageName reduces a fully qualified function name to the package name
|
||||||
|
// There really ought to be to be a better way...
|
||||||
|
func getPackageName(f string) string {
|
||||||
|
for {
|
||||||
|
lastPeriod := strings.LastIndex(f, ".")
|
||||||
|
lastSlash := strings.LastIndex(f, "/")
|
||||||
|
if lastPeriod > lastSlash {
|
||||||
|
f = f[:lastPeriod]
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return f
|
||||||
|
}
|
||||||
|
|
||||||
|
// getCaller retrieves the name of the first non-logrus calling function
|
||||||
|
func getCaller() *runtime.Frame {
|
||||||
|
// cache this package's fully-qualified name
|
||||||
|
callerInitOnce.Do(func() {
|
||||||
|
pcs := make([]uintptr, maximumCallerDepth)
|
||||||
|
_ = runtime.Callers(0, pcs)
|
||||||
|
|
||||||
|
// dynamic get the package name and the minimum caller depth
|
||||||
|
for i := 0; i < maximumCallerDepth; i++ {
|
||||||
|
funcName := runtime.FuncForPC(pcs[i]).Name()
|
||||||
|
if strings.Contains(funcName, "getCaller") {
|
||||||
|
logrusPackage = getPackageName(funcName)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
minimumCallerDepth = knownLogrusFrames
|
||||||
|
})
|
||||||
|
|
||||||
|
// Restrict the lookback frames to avoid runaway lookups
|
||||||
|
pcs := make([]uintptr, maximumCallerDepth)
|
||||||
|
depth := runtime.Callers(minimumCallerDepth, pcs)
|
||||||
|
frames := runtime.CallersFrames(pcs[:depth])
|
||||||
|
|
||||||
|
for f, again := frames.Next(); again; f, again = frames.Next() {
|
||||||
|
pkg := getPackageName(f.Function)
|
||||||
|
|
||||||
|
// If the caller isn't part of this package, we're done
|
||||||
|
if pkg != logrusPackage {
|
||||||
|
return &f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if we got here, we failed to find the caller's context
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry Entry) HasCaller() (has bool) {
|
||||||
|
return entry.Logger != nil &&
|
||||||
|
entry.Logger.ReportCaller &&
|
||||||
|
entry.Caller != nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) log(level Level, msg string) {
|
||||||
|
var buffer *bytes.Buffer
|
||||||
|
|
||||||
|
newEntry := entry.Dup()
|
||||||
|
|
||||||
|
if newEntry.Time.IsZero() {
|
||||||
|
newEntry.Time = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
newEntry.Level = level
|
||||||
|
newEntry.Message = msg
|
||||||
|
|
||||||
|
newEntry.Logger.mu.Lock()
|
||||||
|
reportCaller := newEntry.Logger.ReportCaller
|
||||||
|
bufPool := newEntry.getBufferPool()
|
||||||
|
newEntry.Logger.mu.Unlock()
|
||||||
|
|
||||||
|
if reportCaller {
|
||||||
|
newEntry.Caller = getCaller()
|
||||||
|
}
|
||||||
|
|
||||||
|
newEntry.fireHooks()
|
||||||
|
buffer = bufPool.Get()
|
||||||
|
defer func() {
|
||||||
|
newEntry.Buffer = nil
|
||||||
|
buffer.Reset()
|
||||||
|
bufPool.Put(buffer)
|
||||||
|
}()
|
||||||
|
buffer.Reset()
|
||||||
|
newEntry.Buffer = buffer
|
||||||
|
|
||||||
|
newEntry.write()
|
||||||
|
|
||||||
|
newEntry.Buffer = nil
|
||||||
|
|
||||||
|
// To avoid Entry#log() returning a value that only would make sense for
|
||||||
|
// panic() to use in Entry#Panic(), we avoid the allocation by checking
|
||||||
|
// directly here.
|
||||||
|
if level <= PanicLevel {
|
||||||
|
panic(newEntry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) getBufferPool() (pool BufferPool) {
|
||||||
|
if entry.Logger.BufferPool != nil {
|
||||||
|
return entry.Logger.BufferPool
|
||||||
|
}
|
||||||
|
return bufferPool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) fireHooks() {
|
||||||
|
var tmpHooks LevelHooks
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
tmpHooks = make(LevelHooks, len(entry.Logger.Hooks))
|
||||||
|
for k, v := range entry.Logger.Hooks {
|
||||||
|
tmpHooks[k] = v
|
||||||
|
}
|
||||||
|
entry.Logger.mu.Unlock()
|
||||||
|
|
||||||
|
err := tmpHooks.Fire(entry.Level, entry)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to fire hook: %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) write() {
|
||||||
|
entry.Logger.mu.Lock()
|
||||||
|
defer entry.Logger.mu.Unlock()
|
||||||
|
serialized, err := entry.Logger.Formatter.Format(entry)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to obtain reader, %v\n", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if _, err := entry.Logger.Out.Write(serialized); err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "Failed to write to log, %v\n", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log will log a message at the level given as parameter.
|
||||||
|
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
|
||||||
|
// For this behaviour Entry.Panic or Entry.Fatal should be used instead.
|
||||||
|
func (entry *Entry) Log(level Level, args ...interface{}) {
|
||||||
|
if entry.Logger.IsLevelEnabled(level) {
|
||||||
|
entry.log(level, fmt.Sprint(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Trace(args ...interface{}) {
|
||||||
|
entry.Log(TraceLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Debug(args ...interface{}) {
|
||||||
|
entry.Log(DebugLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Print(args ...interface{}) {
|
||||||
|
entry.Info(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Info(args ...interface{}) {
|
||||||
|
entry.Log(InfoLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warn(args ...interface{}) {
|
||||||
|
entry.Log(WarnLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warning(args ...interface{}) {
|
||||||
|
entry.Warn(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Error(args ...interface{}) {
|
||||||
|
entry.Log(ErrorLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Fatal(args ...interface{}) {
|
||||||
|
entry.Log(FatalLevel, args...)
|
||||||
|
entry.Logger.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Panic(args ...interface{}) {
|
||||||
|
entry.Log(PanicLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry Printf family functions
|
||||||
|
|
||||||
|
func (entry *Entry) Logf(level Level, format string, args ...interface{}) {
|
||||||
|
if entry.Logger.IsLevelEnabled(level) {
|
||||||
|
entry.Log(level, fmt.Sprintf(format, args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Tracef(format string, args ...interface{}) {
|
||||||
|
entry.Logf(TraceLevel, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Debugf(format string, args ...interface{}) {
|
||||||
|
entry.Logf(DebugLevel, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Infof(format string, args ...interface{}) {
|
||||||
|
entry.Logf(InfoLevel, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Printf(format string, args ...interface{}) {
|
||||||
|
entry.Infof(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warnf(format string, args ...interface{}) {
|
||||||
|
entry.Logf(WarnLevel, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warningf(format string, args ...interface{}) {
|
||||||
|
entry.Warnf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Errorf(format string, args ...interface{}) {
|
||||||
|
entry.Logf(ErrorLevel, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Fatalf(format string, args ...interface{}) {
|
||||||
|
entry.Logf(FatalLevel, format, args...)
|
||||||
|
entry.Logger.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Panicf(format string, args ...interface{}) {
|
||||||
|
entry.Logf(PanicLevel, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Entry Println family functions
|
||||||
|
|
||||||
|
func (entry *Entry) Logln(level Level, args ...interface{}) {
|
||||||
|
if entry.Logger.IsLevelEnabled(level) {
|
||||||
|
entry.Log(level, entry.sprintlnn(args...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Traceln(args ...interface{}) {
|
||||||
|
entry.Logln(TraceLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Debugln(args ...interface{}) {
|
||||||
|
entry.Logln(DebugLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Infoln(args ...interface{}) {
|
||||||
|
entry.Logln(InfoLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Println(args ...interface{}) {
|
||||||
|
entry.Infoln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warnln(args ...interface{}) {
|
||||||
|
entry.Logln(WarnLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Warningln(args ...interface{}) {
|
||||||
|
entry.Warnln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Errorln(args ...interface{}) {
|
||||||
|
entry.Logln(ErrorLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Fatalln(args ...interface{}) {
|
||||||
|
entry.Logln(FatalLevel, args...)
|
||||||
|
entry.Logger.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (entry *Entry) Panicln(args ...interface{}) {
|
||||||
|
entry.Logln(PanicLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sprintlnn => Sprint no newline. This is to get the behavior of how
|
||||||
|
// fmt.Sprintln where spaces are always added between operands, regardless of
|
||||||
|
// their type. Instead of vendoring the Sprintln implementation to spare a
|
||||||
|
// string allocation, we do the simplest thing.
|
||||||
|
func (entry *Entry) sprintlnn(args ...interface{}) string {
|
||||||
|
msg := fmt.Sprintln(args...)
|
||||||
|
return msg[:len(msg)-1]
|
||||||
|
}
|
||||||
270
vendor/github.com/sirupsen/logrus/exported.go
generated
vendored
Normal file
270
vendor/github.com/sirupsen/logrus/exported.go
generated
vendored
Normal file
@@ -0,0 +1,270 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
// std is the name of the standard logger in stdlib `log`
|
||||||
|
std = New()
|
||||||
|
)
|
||||||
|
|
||||||
|
func StandardLogger() *Logger {
|
||||||
|
return std
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOutput sets the standard logger output.
|
||||||
|
func SetOutput(out io.Writer) {
|
||||||
|
std.SetOutput(out)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFormatter sets the standard logger formatter.
|
||||||
|
func SetFormatter(formatter Formatter) {
|
||||||
|
std.SetFormatter(formatter)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetReportCaller sets whether the standard logger will include the calling
|
||||||
|
// method as a field.
|
||||||
|
func SetReportCaller(include bool) {
|
||||||
|
std.SetReportCaller(include)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLevel sets the standard logger level.
|
||||||
|
func SetLevel(level Level) {
|
||||||
|
std.SetLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLevel returns the standard logger level.
|
||||||
|
func GetLevel() Level {
|
||||||
|
return std.GetLevel()
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLevelEnabled checks if the log level of the standard logger is greater than the level param
|
||||||
|
func IsLevelEnabled(level Level) bool {
|
||||||
|
return std.IsLevelEnabled(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHook adds a hook to the standard logger hooks.
|
||||||
|
func AddHook(hook Hook) {
|
||||||
|
std.AddHook(hook)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithError creates an entry from the standard logger and adds an error to it, using the value defined in ErrorKey as key.
|
||||||
|
func WithError(err error) *Entry {
|
||||||
|
return std.WithField(ErrorKey, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContext creates an entry from the standard logger and adds a context to it.
|
||||||
|
func WithContext(ctx context.Context) *Entry {
|
||||||
|
return std.WithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithField creates an entry from the standard logger and adds a field to
|
||||||
|
// it. If you want multiple fields, use `WithFields`.
|
||||||
|
//
|
||||||
|
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||||
|
// or Panic on the Entry it returns.
|
||||||
|
func WithField(key string, value interface{}) *Entry {
|
||||||
|
return std.WithField(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFields creates an entry from the standard logger and adds multiple
|
||||||
|
// fields to it. This is simply a helper for `WithField`, invoking it
|
||||||
|
// once for each field.
|
||||||
|
//
|
||||||
|
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||||
|
// or Panic on the Entry it returns.
|
||||||
|
func WithFields(fields Fields) *Entry {
|
||||||
|
return std.WithFields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTime creates an entry from the standard logger and overrides the time of
|
||||||
|
// logs generated with it.
|
||||||
|
//
|
||||||
|
// Note that it doesn't log until you call Debug, Print, Info, Warn, Fatal
|
||||||
|
// or Panic on the Entry it returns.
|
||||||
|
func WithTime(t time.Time) *Entry {
|
||||||
|
return std.WithTime(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trace logs a message at level Trace on the standard logger.
|
||||||
|
func Trace(args ...interface{}) {
|
||||||
|
std.Trace(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug logs a message at level Debug on the standard logger.
|
||||||
|
func Debug(args ...interface{}) {
|
||||||
|
std.Debug(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Print logs a message at level Info on the standard logger.
|
||||||
|
func Print(args ...interface{}) {
|
||||||
|
std.Print(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Info logs a message at level Info on the standard logger.
|
||||||
|
func Info(args ...interface{}) {
|
||||||
|
std.Info(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warn logs a message at level Warn on the standard logger.
|
||||||
|
func Warn(args ...interface{}) {
|
||||||
|
std.Warn(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warning logs a message at level Warn on the standard logger.
|
||||||
|
func Warning(args ...interface{}) {
|
||||||
|
std.Warning(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Error logs a message at level Error on the standard logger.
|
||||||
|
func Error(args ...interface{}) {
|
||||||
|
std.Error(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panic logs a message at level Panic on the standard logger.
|
||||||
|
func Panic(args ...interface{}) {
|
||||||
|
std.Panic(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatal logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||||
|
func Fatal(args ...interface{}) {
|
||||||
|
std.Fatal(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// TraceFn logs a message from a func at level Trace on the standard logger.
|
||||||
|
func TraceFn(fn LogFunction) {
|
||||||
|
std.TraceFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// DebugFn logs a message from a func at level Debug on the standard logger.
|
||||||
|
func DebugFn(fn LogFunction) {
|
||||||
|
std.DebugFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrintFn logs a message from a func at level Info on the standard logger.
|
||||||
|
func PrintFn(fn LogFunction) {
|
||||||
|
std.PrintFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// InfoFn logs a message from a func at level Info on the standard logger.
|
||||||
|
func InfoFn(fn LogFunction) {
|
||||||
|
std.InfoFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WarnFn logs a message from a func at level Warn on the standard logger.
|
||||||
|
func WarnFn(fn LogFunction) {
|
||||||
|
std.WarnFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WarningFn logs a message from a func at level Warn on the standard logger.
|
||||||
|
func WarningFn(fn LogFunction) {
|
||||||
|
std.WarningFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// ErrorFn logs a message from a func at level Error on the standard logger.
|
||||||
|
func ErrorFn(fn LogFunction) {
|
||||||
|
std.ErrorFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// PanicFn logs a message from a func at level Panic on the standard logger.
|
||||||
|
func PanicFn(fn LogFunction) {
|
||||||
|
std.PanicFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// FatalFn logs a message from a func at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||||
|
func FatalFn(fn LogFunction) {
|
||||||
|
std.FatalFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tracef logs a message at level Trace on the standard logger.
|
||||||
|
func Tracef(format string, args ...interface{}) {
|
||||||
|
std.Tracef(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugf logs a message at level Debug on the standard logger.
|
||||||
|
func Debugf(format string, args ...interface{}) {
|
||||||
|
std.Debugf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Printf logs a message at level Info on the standard logger.
|
||||||
|
func Printf(format string, args ...interface{}) {
|
||||||
|
std.Printf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infof logs a message at level Info on the standard logger.
|
||||||
|
func Infof(format string, args ...interface{}) {
|
||||||
|
std.Infof(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnf logs a message at level Warn on the standard logger.
|
||||||
|
func Warnf(format string, args ...interface{}) {
|
||||||
|
std.Warnf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningf logs a message at level Warn on the standard logger.
|
||||||
|
func Warningf(format string, args ...interface{}) {
|
||||||
|
std.Warningf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorf logs a message at level Error on the standard logger.
|
||||||
|
func Errorf(format string, args ...interface{}) {
|
||||||
|
std.Errorf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panicf logs a message at level Panic on the standard logger.
|
||||||
|
func Panicf(format string, args ...interface{}) {
|
||||||
|
std.Panicf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalf logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||||
|
func Fatalf(format string, args ...interface{}) {
|
||||||
|
std.Fatalf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traceln logs a message at level Trace on the standard logger.
|
||||||
|
func Traceln(args ...interface{}) {
|
||||||
|
std.Traceln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debugln logs a message at level Debug on the standard logger.
|
||||||
|
func Debugln(args ...interface{}) {
|
||||||
|
std.Debugln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Println logs a message at level Info on the standard logger.
|
||||||
|
func Println(args ...interface{}) {
|
||||||
|
std.Println(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Infoln logs a message at level Info on the standard logger.
|
||||||
|
func Infoln(args ...interface{}) {
|
||||||
|
std.Infoln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warnln logs a message at level Warn on the standard logger.
|
||||||
|
func Warnln(args ...interface{}) {
|
||||||
|
std.Warnln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Warningln logs a message at level Warn on the standard logger.
|
||||||
|
func Warningln(args ...interface{}) {
|
||||||
|
std.Warningln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Errorln logs a message at level Error on the standard logger.
|
||||||
|
func Errorln(args ...interface{}) {
|
||||||
|
std.Errorln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Panicln logs a message at level Panic on the standard logger.
|
||||||
|
func Panicln(args ...interface{}) {
|
||||||
|
std.Panicln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fatalln logs a message at level Fatal on the standard logger then the process will exit with status set to 1.
|
||||||
|
func Fatalln(args ...interface{}) {
|
||||||
|
std.Fatalln(args...)
|
||||||
|
}
|
||||||
78
vendor/github.com/sirupsen/logrus/formatter.go
generated
vendored
Normal file
78
vendor/github.com/sirupsen/logrus/formatter.go
generated
vendored
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
// Default key names for the default fields
|
||||||
|
const (
|
||||||
|
defaultTimestampFormat = time.RFC3339
|
||||||
|
FieldKeyMsg = "msg"
|
||||||
|
FieldKeyLevel = "level"
|
||||||
|
FieldKeyTime = "time"
|
||||||
|
FieldKeyLogrusError = "logrus_error"
|
||||||
|
FieldKeyFunc = "func"
|
||||||
|
FieldKeyFile = "file"
|
||||||
|
)
|
||||||
|
|
||||||
|
// The Formatter interface is used to implement a custom Formatter. It takes an
|
||||||
|
// `Entry`. It exposes all the fields, including the default ones:
|
||||||
|
//
|
||||||
|
// * `entry.Data["msg"]`. The message passed from Info, Warn, Error ..
|
||||||
|
// * `entry.Data["time"]`. The timestamp.
|
||||||
|
// * `entry.Data["level"]. The level the entry was logged at.
|
||||||
|
//
|
||||||
|
// Any additional fields added with `WithField` or `WithFields` are also in
|
||||||
|
// `entry.Data`. Format is expected to return an array of bytes which are then
|
||||||
|
// logged to `logger.Out`.
|
||||||
|
type Formatter interface {
|
||||||
|
Format(*Entry) ([]byte, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is to not silently overwrite `time`, `msg`, `func` and `level` fields when
|
||||||
|
// dumping it. If this code wasn't there doing:
|
||||||
|
//
|
||||||
|
// logrus.WithField("level", 1).Info("hello")
|
||||||
|
//
|
||||||
|
// Would just silently drop the user provided level. Instead with this code
|
||||||
|
// it'll logged as:
|
||||||
|
//
|
||||||
|
// {"level": "info", "fields.level": 1, "msg": "hello", "time": "..."}
|
||||||
|
//
|
||||||
|
// 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, fieldMap FieldMap, reportCaller bool) {
|
||||||
|
timeKey := fieldMap.resolve(FieldKeyTime)
|
||||||
|
if t, ok := data[timeKey]; ok {
|
||||||
|
data["fields."+timeKey] = t
|
||||||
|
delete(data, timeKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
msgKey := fieldMap.resolve(FieldKeyMsg)
|
||||||
|
if m, ok := data[msgKey]; ok {
|
||||||
|
data["fields."+msgKey] = m
|
||||||
|
delete(data, msgKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
levelKey := fieldMap.resolve(FieldKeyLevel)
|
||||||
|
if l, ok := data[levelKey]; ok {
|
||||||
|
data["fields."+levelKey] = l
|
||||||
|
delete(data, levelKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
logrusErrKey := fieldMap.resolve(FieldKeyLogrusError)
|
||||||
|
if l, ok := data[logrusErrKey]; ok {
|
||||||
|
data["fields."+logrusErrKey] = l
|
||||||
|
delete(data, logrusErrKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If reportCaller is not set, 'func' will not conflict.
|
||||||
|
if reportCaller {
|
||||||
|
funcKey := fieldMap.resolve(FieldKeyFunc)
|
||||||
|
if l, ok := data[funcKey]; ok {
|
||||||
|
data["fields."+funcKey] = l
|
||||||
|
}
|
||||||
|
fileKey := fieldMap.resolve(FieldKeyFile)
|
||||||
|
if l, ok := data[fileKey]; ok {
|
||||||
|
data["fields."+fileKey] = l
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
34
vendor/github.com/sirupsen/logrus/hooks.go
generated
vendored
Normal file
34
vendor/github.com/sirupsen/logrus/hooks.go
generated
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
// Hook describes hooks to be fired when logging on the logging levels returned from
|
||||||
|
// [Hook.Levels] on your implementation of the interface. Note that this is not
|
||||||
|
// fired in a goroutine or a channel with workers, you should handle such
|
||||||
|
// functionality yourself if your call is non-blocking, and you don't wish for
|
||||||
|
// the logging calls for levels returned from `Levels()` to block.
|
||||||
|
type Hook interface {
|
||||||
|
Levels() []Level
|
||||||
|
Fire(*Entry) error
|
||||||
|
}
|
||||||
|
|
||||||
|
// LevelHooks is an internal type for storing the hooks on a logger instance.
|
||||||
|
type LevelHooks map[Level][]Hook
|
||||||
|
|
||||||
|
// Add a hook to an instance of logger. This is called with
|
||||||
|
// `log.Hooks.Add(new(MyHook))` where `MyHook` implements the `Hook` interface.
|
||||||
|
func (hooks LevelHooks) Add(hook Hook) {
|
||||||
|
for _, level := range hook.Levels() {
|
||||||
|
hooks[level] = append(hooks[level], hook)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fire all the hooks for the passed level. Used by `entry.log` to fire
|
||||||
|
// appropriate hooks for a log entry.
|
||||||
|
func (hooks LevelHooks) Fire(level Level, entry *Entry) error {
|
||||||
|
for _, hook := range hooks[level] {
|
||||||
|
if err := hook.Fire(entry); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
128
vendor/github.com/sirupsen/logrus/json_formatter.go
generated
vendored
Normal file
128
vendor/github.com/sirupsen/logrus/json_formatter.go
generated
vendored
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"runtime"
|
||||||
|
)
|
||||||
|
|
||||||
|
type fieldKey string
|
||||||
|
|
||||||
|
// FieldMap allows customization of the key names for default fields.
|
||||||
|
type FieldMap map[fieldKey]string
|
||||||
|
|
||||||
|
func (f FieldMap) resolve(key fieldKey) string {
|
||||||
|
if k, ok := f[key]; ok {
|
||||||
|
return k
|
||||||
|
}
|
||||||
|
|
||||||
|
return string(key)
|
||||||
|
}
|
||||||
|
|
||||||
|
// JSONFormatter formats logs into parsable json
|
||||||
|
type JSONFormatter struct {
|
||||||
|
// TimestampFormat sets the format used for marshaling timestamps.
|
||||||
|
// The format to use is the same than for time.Format or time.Parse from the standard
|
||||||
|
// library.
|
||||||
|
// The standard Library already provides a set of predefined format.
|
||||||
|
TimestampFormat string
|
||||||
|
|
||||||
|
// DisableTimestamp allows disabling automatic timestamps in output
|
||||||
|
DisableTimestamp bool
|
||||||
|
|
||||||
|
// DisableHTMLEscape allows disabling html escaping in output
|
||||||
|
DisableHTMLEscape bool
|
||||||
|
|
||||||
|
// DataKey allows users to put all the log entry parameters into a nested dictionary at a given key.
|
||||||
|
DataKey string
|
||||||
|
|
||||||
|
// FieldMap allows users to customize the names of keys for default fields.
|
||||||
|
// As an example:
|
||||||
|
// formatter := &JSONFormatter{
|
||||||
|
// FieldMap: FieldMap{
|
||||||
|
// FieldKeyTime: "@timestamp",
|
||||||
|
// FieldKeyLevel: "@level",
|
||||||
|
// FieldKeyMsg: "@message",
|
||||||
|
// FieldKeyFunc: "@caller",
|
||||||
|
// },
|
||||||
|
// }
|
||||||
|
FieldMap FieldMap
|
||||||
|
|
||||||
|
// CallerPrettyfier can be set by the user to modify the content
|
||||||
|
// of the function and file keys in the json data when ReportCaller is
|
||||||
|
// activated. If any of the returned value is the empty string the
|
||||||
|
// corresponding key will be removed from json fields.
|
||||||
|
CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
||||||
|
|
||||||
|
// PrettyPrint will indent all json logs
|
||||||
|
PrettyPrint bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format renders a single log entry
|
||||||
|
func (f *JSONFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
data := make(Fields, len(entry.Data)+4)
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
switch v := v.(type) {
|
||||||
|
case error:
|
||||||
|
// Otherwise errors are ignored by `encoding/json`
|
||||||
|
// https://github.com/sirupsen/logrus/issues/137
|
||||||
|
data[k] = v.Error()
|
||||||
|
default:
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if f.DataKey != "" {
|
||||||
|
newData := make(Fields, 4)
|
||||||
|
newData[f.DataKey] = data
|
||||||
|
data = newData
|
||||||
|
}
|
||||||
|
|
||||||
|
prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
|
||||||
|
|
||||||
|
timestampFormat := f.TimestampFormat
|
||||||
|
if timestampFormat == "" {
|
||||||
|
timestampFormat = defaultTimestampFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
if entry.err != "" {
|
||||||
|
data[f.FieldMap.resolve(FieldKeyLogrusError)] = entry.err
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
if entry.HasCaller() {
|
||||||
|
funcVal := entry.Caller.Function
|
||||||
|
fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
||||||
|
if f.CallerPrettyfier != nil {
|
||||||
|
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
||||||
|
}
|
||||||
|
if funcVal != "" {
|
||||||
|
data[f.FieldMap.resolve(FieldKeyFunc)] = funcVal
|
||||||
|
}
|
||||||
|
if fileVal != "" {
|
||||||
|
data[f.FieldMap.resolve(FieldKeyFile)] = fileVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var b *bytes.Buffer
|
||||||
|
if entry.Buffer != nil {
|
||||||
|
b = entry.Buffer
|
||||||
|
} else {
|
||||||
|
b = &bytes.Buffer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
encoder := json.NewEncoder(b)
|
||||||
|
encoder.SetEscapeHTML(!f.DisableHTMLEscape)
|
||||||
|
if f.PrettyPrint {
|
||||||
|
encoder.SetIndent("", " ")
|
||||||
|
}
|
||||||
|
if err := encoder.Encode(data); err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to marshal fields to JSON, %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
417
vendor/github.com/sirupsen/logrus/logger.go
generated
vendored
Normal file
417
vendor/github.com/sirupsen/logrus/logger.go
generated
vendored
Normal file
@@ -0,0 +1,417 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
"sync"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// LogFunction For big messages, it can be more efficient to pass a function
|
||||||
|
// and only call it if the log level is actually enables rather than
|
||||||
|
// generating the log message and then checking if the level is enabled
|
||||||
|
type LogFunction func() []interface{}
|
||||||
|
|
||||||
|
type Logger struct {
|
||||||
|
// The logs are `io.Copy`'d to this in a mutex. It's common to set this to a
|
||||||
|
// file, or leave it default which is `os.Stderr`. You can also set this to
|
||||||
|
// something more adventurous, such as logging to Kafka.
|
||||||
|
Out io.Writer
|
||||||
|
// Hooks for the logger instance. These allow firing events based on logging
|
||||||
|
// levels and log entries. For example, to send errors to an error tracking
|
||||||
|
// service, log to StatsD or dump the core on fatal errors.
|
||||||
|
Hooks LevelHooks
|
||||||
|
// All log entries pass through the formatter before logged to Out. The
|
||||||
|
// included formatters are `TextFormatter` and `JSONFormatter` for which
|
||||||
|
// TextFormatter is the default. In development (when a TTY is attached) it
|
||||||
|
// logs with colors, but to a file it wouldn't. You can easily implement your
|
||||||
|
// own that implements the `Formatter` interface, see the `README` or included
|
||||||
|
// formatters for examples.
|
||||||
|
Formatter Formatter
|
||||||
|
|
||||||
|
// Flag for whether to log caller info (off by default)
|
||||||
|
ReportCaller bool
|
||||||
|
|
||||||
|
// The logging level the logger should log at. This is typically (and defaults
|
||||||
|
// to) `logrus.Info`, which allows Info(), Warn(), Error() and Fatal() to be
|
||||||
|
// logged.
|
||||||
|
Level Level
|
||||||
|
// Used to sync writing to the log. Locking is enabled by Default
|
||||||
|
mu MutexWrap
|
||||||
|
// Reusable empty entry
|
||||||
|
entryPool sync.Pool
|
||||||
|
// Function to exit the application, defaults to `os.Exit()`
|
||||||
|
ExitFunc exitFunc
|
||||||
|
// The buffer pool used to format the log. If it is nil, the default global
|
||||||
|
// buffer pool will be used.
|
||||||
|
BufferPool BufferPool
|
||||||
|
}
|
||||||
|
|
||||||
|
type exitFunc func(int)
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// New Creates a new logger. Configuration should be set by changing [Formatter],
|
||||||
|
// Out and Hooks directly on the default Logger instance. You can also just
|
||||||
|
// instantiate your own:
|
||||||
|
//
|
||||||
|
// var log = &logrus.Logger{
|
||||||
|
// Out: os.Stderr,
|
||||||
|
// Formatter: new(logrus.TextFormatter),
|
||||||
|
// Hooks: make(logrus.LevelHooks),
|
||||||
|
// Level: logrus.DebugLevel,
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// It's recommended to make this a global instance called `log`.
|
||||||
|
func New() *Logger {
|
||||||
|
return &Logger{
|
||||||
|
Out: os.Stderr,
|
||||||
|
Formatter: new(TextFormatter),
|
||||||
|
Hooks: make(LevelHooks),
|
||||||
|
Level: InfoLevel,
|
||||||
|
ExitFunc: os.Exit,
|
||||||
|
ReportCaller: false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) newEntry() *Entry {
|
||||||
|
entry, ok := logger.entryPool.Get().(*Entry)
|
||||||
|
if ok {
|
||||||
|
return entry
|
||||||
|
}
|
||||||
|
return NewEntry(logger)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) releaseEntry(entry *Entry) {
|
||||||
|
entry.Data = map[string]interface{}{}
|
||||||
|
logger.entryPool.Put(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithField allocates a new entry and adds a field to it.
|
||||||
|
// Debug, Print, Info, Warn, Error, Fatal or Panic must be then applied to
|
||||||
|
// this new returned entry.
|
||||||
|
// If you want multiple fields, use `WithFields`.
|
||||||
|
func (logger *Logger) WithField(key string, value interface{}) *Entry {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithField(key, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithFields adds a struct of fields to the log entry. It calls [Entry.WithField]
|
||||||
|
// for each Field.
|
||||||
|
func (logger *Logger) WithFields(fields Fields) *Entry {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithFields(fields)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithError adds an error as single field to the log entry. It calls
|
||||||
|
// [Entry.WithError] for the given error.
|
||||||
|
func (logger *Logger) WithError(err error) *Entry {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithContext add a context to the log entry.
|
||||||
|
func (logger *Logger) WithContext(ctx context.Context) *Entry {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithContext(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WithTime overrides the time of the log entry.
|
||||||
|
func (logger *Logger) WithTime(t time.Time) *Entry {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
defer logger.releaseEntry(entry)
|
||||||
|
return entry.WithTime(t)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Logf(level Level, format string, args ...interface{}) {
|
||||||
|
if logger.IsLevelEnabled(level) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Logf(level, format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Tracef(format string, args ...interface{}) {
|
||||||
|
logger.Logf(TraceLevel, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debugf(format string, args ...interface{}) {
|
||||||
|
logger.Logf(DebugLevel, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Infof(format string, args ...interface{}) {
|
||||||
|
logger.Logf(InfoLevel, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Printf(format string, args ...interface{}) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Printf(format, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warnf(format string, args ...interface{}) {
|
||||||
|
logger.Logf(WarnLevel, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warningf(format string, args ...interface{}) {
|
||||||
|
logger.Warnf(format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Errorf(format string, args ...interface{}) {
|
||||||
|
logger.Logf(ErrorLevel, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatalf(format string, args ...interface{}) {
|
||||||
|
logger.Logf(FatalLevel, format, args...)
|
||||||
|
logger.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panicf(format string, args ...interface{}) {
|
||||||
|
logger.Logf(PanicLevel, format, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Log will log a message at the level given as parameter.
|
||||||
|
// Warning: using Log at Panic or Fatal level will not respectively Panic nor Exit.
|
||||||
|
// For this behaviour Logger.Panic or Logger.Fatal should be used instead.
|
||||||
|
func (logger *Logger) Log(level Level, args ...interface{}) {
|
||||||
|
if logger.IsLevelEnabled(level) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Log(level, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) LogFn(level Level, fn LogFunction) {
|
||||||
|
if logger.IsLevelEnabled(level) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Log(level, fn()...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Trace(args ...interface{}) {
|
||||||
|
logger.Log(TraceLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debug(args ...interface{}) {
|
||||||
|
logger.Log(DebugLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Info(args ...interface{}) {
|
||||||
|
logger.Log(InfoLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Print(args ...interface{}) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Print(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warn(args ...interface{}) {
|
||||||
|
logger.Log(WarnLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warning(args ...interface{}) {
|
||||||
|
logger.Warn(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Error(args ...interface{}) {
|
||||||
|
logger.Log(ErrorLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatal(args ...interface{}) {
|
||||||
|
logger.Log(FatalLevel, args...)
|
||||||
|
logger.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panic(args ...interface{}) {
|
||||||
|
logger.Log(PanicLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) TraceFn(fn LogFunction) {
|
||||||
|
logger.LogFn(TraceLevel, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) DebugFn(fn LogFunction) {
|
||||||
|
logger.LogFn(DebugLevel, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) InfoFn(fn LogFunction) {
|
||||||
|
logger.LogFn(InfoLevel, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) PrintFn(fn LogFunction) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Print(fn()...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) WarnFn(fn LogFunction) {
|
||||||
|
logger.LogFn(WarnLevel, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) WarningFn(fn LogFunction) {
|
||||||
|
logger.WarnFn(fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) ErrorFn(fn LogFunction) {
|
||||||
|
logger.LogFn(ErrorLevel, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) FatalFn(fn LogFunction) {
|
||||||
|
logger.LogFn(FatalLevel, fn)
|
||||||
|
logger.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) PanicFn(fn LogFunction) {
|
||||||
|
logger.LogFn(PanicLevel, fn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Logln(level Level, args ...interface{}) {
|
||||||
|
if logger.IsLevelEnabled(level) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Logln(level, args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Traceln(args ...interface{}) {
|
||||||
|
logger.Logln(TraceLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Debugln(args ...interface{}) {
|
||||||
|
logger.Logln(DebugLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Infoln(args ...interface{}) {
|
||||||
|
logger.Logln(InfoLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Println(args ...interface{}) {
|
||||||
|
entry := logger.newEntry()
|
||||||
|
entry.Println(args...)
|
||||||
|
logger.releaseEntry(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warnln(args ...interface{}) {
|
||||||
|
logger.Logln(WarnLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Warningln(args ...interface{}) {
|
||||||
|
logger.Warnln(args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Errorln(args ...interface{}) {
|
||||||
|
logger.Logln(ErrorLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Fatalln(args ...interface{}) {
|
||||||
|
logger.Logln(FatalLevel, args...)
|
||||||
|
logger.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Panicln(args ...interface{}) {
|
||||||
|
logger.Logln(PanicLevel, args...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) Exit(code int) {
|
||||||
|
runHandlers()
|
||||||
|
if logger.ExitFunc == nil {
|
||||||
|
logger.ExitFunc = os.Exit
|
||||||
|
}
|
||||||
|
logger.ExitFunc(code)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetNoLock disables the lock for situations where a file is opened with
|
||||||
|
// appending mode, and safe for concurrent writes to the file (within 4k
|
||||||
|
// message on Linux). In these cases user can choose to disable the lock.
|
||||||
|
func (logger *Logger) SetNoLock() {
|
||||||
|
logger.mu.Disable()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) level() Level {
|
||||||
|
return Level(atomic.LoadUint32((*uint32)(&logger.Level)))
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetLevel sets the logger level.
|
||||||
|
func (logger *Logger) SetLevel(level Level) {
|
||||||
|
atomic.StoreUint32((*uint32)(&logger.Level), uint32(level))
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetLevel returns the logger level.
|
||||||
|
func (logger *Logger) GetLevel() Level {
|
||||||
|
return logger.level()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddHook adds a hook to the logger hooks.
|
||||||
|
func (logger *Logger) AddHook(hook Hook) {
|
||||||
|
logger.mu.Lock()
|
||||||
|
defer logger.mu.Unlock()
|
||||||
|
logger.Hooks.Add(hook)
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsLevelEnabled checks if the log level of the logger is greater than the level param
|
||||||
|
func (logger *Logger) IsLevelEnabled(level Level) bool {
|
||||||
|
return logger.level() >= level
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetFormatter sets the logger formatter.
|
||||||
|
func (logger *Logger) SetFormatter(formatter Formatter) {
|
||||||
|
logger.mu.Lock()
|
||||||
|
defer logger.mu.Unlock()
|
||||||
|
logger.Formatter = formatter
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetOutput sets the logger output.
|
||||||
|
func (logger *Logger) SetOutput(output io.Writer) {
|
||||||
|
logger.mu.Lock()
|
||||||
|
defer logger.mu.Unlock()
|
||||||
|
logger.Out = output
|
||||||
|
}
|
||||||
|
|
||||||
|
func (logger *Logger) SetReportCaller(reportCaller bool) {
|
||||||
|
logger.mu.Lock()
|
||||||
|
defer logger.mu.Unlock()
|
||||||
|
logger.ReportCaller = reportCaller
|
||||||
|
}
|
||||||
|
|
||||||
|
// ReplaceHooks replaces the logger hooks and returns the old ones
|
||||||
|
func (logger *Logger) ReplaceHooks(hooks LevelHooks) LevelHooks {
|
||||||
|
logger.mu.Lock()
|
||||||
|
oldHooks := logger.Hooks
|
||||||
|
logger.Hooks = hooks
|
||||||
|
logger.mu.Unlock()
|
||||||
|
return oldHooks
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetBufferPool sets the logger buffer pool.
|
||||||
|
func (logger *Logger) SetBufferPool(pool BufferPool) {
|
||||||
|
logger.mu.Lock()
|
||||||
|
defer logger.mu.Unlock()
|
||||||
|
logger.BufferPool = pool
|
||||||
|
}
|
||||||
190
vendor/github.com/sirupsen/logrus/logrus.go
generated
vendored
Normal file
190
vendor/github.com/sirupsen/logrus/logrus.go
generated
vendored
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Fields type, used to pass to [WithFields].
|
||||||
|
type Fields map[string]interface{}
|
||||||
|
|
||||||
|
// Level type
|
||||||
|
//
|
||||||
|
//nolint:recvcheck // the methods of "Entry" use pointer receiver and non-pointer receiver.
|
||||||
|
type Level uint32
|
||||||
|
|
||||||
|
// Convert the Level to a string. E.g. [PanicLevel] becomes "panic".
|
||||||
|
func (level Level) String() string {
|
||||||
|
if b, err := level.MarshalText(); err == nil {
|
||||||
|
return string(b)
|
||||||
|
} else {
|
||||||
|
return "unknown"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ParseLevel takes a string level and returns the Logrus log level constant.
|
||||||
|
func ParseLevel(lvl string) (Level, error) {
|
||||||
|
switch strings.ToLower(lvl) {
|
||||||
|
case "panic":
|
||||||
|
return PanicLevel, nil
|
||||||
|
case "fatal":
|
||||||
|
return FatalLevel, nil
|
||||||
|
case "error":
|
||||||
|
return ErrorLevel, nil
|
||||||
|
case "warn", "warning":
|
||||||
|
return WarnLevel, nil
|
||||||
|
case "info":
|
||||||
|
return InfoLevel, nil
|
||||||
|
case "debug":
|
||||||
|
return DebugLevel, nil
|
||||||
|
case "trace":
|
||||||
|
return TraceLevel, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var l Level
|
||||||
|
return l, fmt.Errorf("not a valid logrus Level: %q", lvl)
|
||||||
|
}
|
||||||
|
|
||||||
|
// UnmarshalText implements encoding.TextUnmarshaler.
|
||||||
|
func (level *Level) UnmarshalText(text []byte) error {
|
||||||
|
l, err := ParseLevel(string(text))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*level = l
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (level Level) MarshalText() ([]byte, error) {
|
||||||
|
switch level {
|
||||||
|
case TraceLevel:
|
||||||
|
return []byte("trace"), nil
|
||||||
|
case DebugLevel:
|
||||||
|
return []byte("debug"), nil
|
||||||
|
case InfoLevel:
|
||||||
|
return []byte("info"), nil
|
||||||
|
case WarnLevel:
|
||||||
|
return []byte("warning"), nil
|
||||||
|
case ErrorLevel:
|
||||||
|
return []byte("error"), nil
|
||||||
|
case FatalLevel:
|
||||||
|
return []byte("fatal"), nil
|
||||||
|
case PanicLevel:
|
||||||
|
return []byte("panic"), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil, fmt.Errorf("not a valid logrus level %d", level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// AllLevels exposing all logging levels.
|
||||||
|
var AllLevels = []Level{
|
||||||
|
PanicLevel,
|
||||||
|
FatalLevel,
|
||||||
|
ErrorLevel,
|
||||||
|
WarnLevel,
|
||||||
|
InfoLevel,
|
||||||
|
DebugLevel,
|
||||||
|
TraceLevel,
|
||||||
|
}
|
||||||
|
|
||||||
|
// These are the different logging levels. You can set the logging level to log
|
||||||
|
// on your instance of logger, obtained with `logrus.New()`.
|
||||||
|
const (
|
||||||
|
// PanicLevel level, highest level of severity. Logs and then calls panic with the
|
||||||
|
// message passed to Debug, Info, ...
|
||||||
|
PanicLevel Level = iota
|
||||||
|
// FatalLevel level. Logs and then calls `logger.Exit(1)`. It will exit even if the
|
||||||
|
// logging level is set to Panic.
|
||||||
|
FatalLevel
|
||||||
|
// ErrorLevel level. Logs. Used for errors that should definitely be noted.
|
||||||
|
// Commonly used for hooks to send errors to an error tracking service.
|
||||||
|
ErrorLevel
|
||||||
|
// WarnLevel level. Non-critical entries that deserve eyes.
|
||||||
|
WarnLevel
|
||||||
|
// InfoLevel level. General operational entries about what's going on inside the
|
||||||
|
// application.
|
||||||
|
InfoLevel
|
||||||
|
// DebugLevel level. Usually only enabled when debugging. Very verbose logging.
|
||||||
|
DebugLevel
|
||||||
|
// TraceLevel level. Designates finer-grained informational events than the Debug.
|
||||||
|
TraceLevel
|
||||||
|
)
|
||||||
|
|
||||||
|
// Won't compile if StdLogger can't be realized by a log.Logger
|
||||||
|
var (
|
||||||
|
_ StdLogger = &log.Logger{}
|
||||||
|
_ StdLogger = &Entry{}
|
||||||
|
_ StdLogger = &Logger{}
|
||||||
|
)
|
||||||
|
|
||||||
|
// StdLogger is what your logrus-enabled library should take, that way
|
||||||
|
// it'll accept a stdlib logger ([log.Logger]) and a logrus logger.
|
||||||
|
// There's no standard interface, so this is the closest we get, unfortunately.
|
||||||
|
type StdLogger interface {
|
||||||
|
Print(...interface{})
|
||||||
|
Printf(string, ...interface{})
|
||||||
|
Println(...interface{})
|
||||||
|
|
||||||
|
Fatal(...interface{})
|
||||||
|
Fatalf(string, ...interface{})
|
||||||
|
Fatalln(...interface{})
|
||||||
|
|
||||||
|
Panic(...interface{})
|
||||||
|
Panicf(string, ...interface{})
|
||||||
|
Panicln(...interface{})
|
||||||
|
}
|
||||||
|
|
||||||
|
// FieldLogger extends the [StdLogger] interface, generalizing
|
||||||
|
// the [Entry] and [Logger] types.
|
||||||
|
type FieldLogger interface {
|
||||||
|
WithField(key string, value interface{}) *Entry
|
||||||
|
WithFields(fields Fields) *Entry
|
||||||
|
WithError(err error) *Entry
|
||||||
|
|
||||||
|
Debugf(format string, args ...interface{})
|
||||||
|
Infof(format string, args ...interface{})
|
||||||
|
Printf(format string, args ...interface{})
|
||||||
|
Warnf(format string, args ...interface{})
|
||||||
|
Warningf(format string, args ...interface{})
|
||||||
|
Errorf(format string, args ...interface{})
|
||||||
|
Fatalf(format string, args ...interface{})
|
||||||
|
Panicf(format string, args ...interface{})
|
||||||
|
|
||||||
|
Debug(args ...interface{})
|
||||||
|
Info(args ...interface{})
|
||||||
|
Print(args ...interface{})
|
||||||
|
Warn(args ...interface{})
|
||||||
|
Warning(args ...interface{})
|
||||||
|
Error(args ...interface{})
|
||||||
|
Fatal(args ...interface{})
|
||||||
|
Panic(args ...interface{})
|
||||||
|
|
||||||
|
Debugln(args ...interface{})
|
||||||
|
Infoln(args ...interface{})
|
||||||
|
Println(args ...interface{})
|
||||||
|
Warnln(args ...interface{})
|
||||||
|
Warningln(args ...interface{})
|
||||||
|
Errorln(args ...interface{})
|
||||||
|
Fatalln(args ...interface{})
|
||||||
|
Panicln(args ...interface{})
|
||||||
|
|
||||||
|
// IsDebugEnabled() bool
|
||||||
|
// IsInfoEnabled() bool
|
||||||
|
// IsWarnEnabled() bool
|
||||||
|
// IsErrorEnabled() bool
|
||||||
|
// IsFatalEnabled() bool
|
||||||
|
// IsPanicEnabled() bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ext1FieldLogger (the first extension to [FieldLogger]) is superfluous, it is
|
||||||
|
// here for consistency. Do not use. Use [FieldLogger], [Logger] or [Entry]
|
||||||
|
// instead.
|
||||||
|
type Ext1FieldLogger interface {
|
||||||
|
FieldLogger
|
||||||
|
Tracef(format string, args ...interface{})
|
||||||
|
Trace(args ...interface{})
|
||||||
|
Traceln(args ...interface{})
|
||||||
|
}
|
||||||
11
vendor/github.com/sirupsen/logrus/terminal_check_appengine.go
generated
vendored
Normal file
11
vendor/github.com/sirupsen/logrus/terminal_check_appengine.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// +build appengine
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkIfTerminal(w io.Writer) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
13
vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
generated
vendored
Normal file
13
vendor/github.com/sirupsen/logrus/terminal_check_bsd.go
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
// +build darwin dragonfly freebsd netbsd openbsd hurd
|
||||||
|
// +build !js
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
const ioctlReadTermios = unix.TIOCGETA
|
||||||
|
|
||||||
|
func isTerminal(fd int) bool {
|
||||||
|
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
7
vendor/github.com/sirupsen/logrus/terminal_check_js.go
generated
vendored
Normal file
7
vendor/github.com/sirupsen/logrus/terminal_check_js.go
generated
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
// +build js
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
func isTerminal(fd int) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
11
vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go
generated
vendored
Normal file
11
vendor/github.com/sirupsen/logrus/terminal_check_no_terminal.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
// +build js nacl plan9
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkIfTerminal(w io.Writer) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
17
vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go
generated
vendored
Normal file
17
vendor/github.com/sirupsen/logrus/terminal_check_notappengine.go
generated
vendored
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
// +build !appengine,!js,!windows,!nacl,!plan9
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkIfTerminal(w io.Writer) bool {
|
||||||
|
switch v := w.(type) {
|
||||||
|
case *os.File:
|
||||||
|
return isTerminal(int(v.Fd()))
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
11
vendor/github.com/sirupsen/logrus/terminal_check_solaris.go
generated
vendored
Normal file
11
vendor/github.com/sirupsen/logrus/terminal_check_solaris.go
generated
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"golang.org/x/sys/unix"
|
||||||
|
)
|
||||||
|
|
||||||
|
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||||
|
func isTerminal(fd int) bool {
|
||||||
|
_, err := unix.IoctlGetTermio(fd, unix.TCGETA)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
15
vendor/github.com/sirupsen/logrus/terminal_check_unix.go
generated
vendored
Normal file
15
vendor/github.com/sirupsen/logrus/terminal_check_unix.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
//go:build (linux || aix || zos) && !js && !wasi
|
||||||
|
// +build linux aix zos
|
||||||
|
// +build !js
|
||||||
|
// +build !wasi
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import "golang.org/x/sys/unix"
|
||||||
|
|
||||||
|
const ioctlReadTermios = unix.TCGETS
|
||||||
|
|
||||||
|
func isTerminal(fd int) bool {
|
||||||
|
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
8
vendor/github.com/sirupsen/logrus/terminal_check_wasi.go
generated
vendored
Normal file
8
vendor/github.com/sirupsen/logrus/terminal_check_wasi.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
//go:build wasi
|
||||||
|
// +build wasi
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
func isTerminal(fd int) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
8
vendor/github.com/sirupsen/logrus/terminal_check_wasip1.go
generated
vendored
Normal file
8
vendor/github.com/sirupsen/logrus/terminal_check_wasip1.go
generated
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
//go:build wasip1
|
||||||
|
// +build wasip1
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
func isTerminal(fd int) bool {
|
||||||
|
return false
|
||||||
|
}
|
||||||
27
vendor/github.com/sirupsen/logrus/terminal_check_windows.go
generated
vendored
Normal file
27
vendor/github.com/sirupsen/logrus/terminal_check_windows.go
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
// +build !appengine,!js,windows
|
||||||
|
|
||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"golang.org/x/sys/windows"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkIfTerminal(w io.Writer) bool {
|
||||||
|
switch v := w.(type) {
|
||||||
|
case *os.File:
|
||||||
|
handle := windows.Handle(v.Fd())
|
||||||
|
var mode uint32
|
||||||
|
if err := windows.GetConsoleMode(handle, &mode); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
mode |= windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||||
|
if err := windows.SetConsoleMode(handle, mode); err != nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
340
vendor/github.com/sirupsen/logrus/text_formatter.go
generated
vendored
Normal file
340
vendor/github.com/sirupsen/logrus/text_formatter.go
generated
vendored
Normal file
@@ -0,0 +1,340 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
"unicode/utf8"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
red = 31
|
||||||
|
yellow = 33
|
||||||
|
blue = 36
|
||||||
|
gray = 37
|
||||||
|
)
|
||||||
|
|
||||||
|
var baseTimestamp time.Time
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
baseTimestamp = time.Now()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextFormatter formats logs into text
|
||||||
|
type TextFormatter struct {
|
||||||
|
// Set to true to bypass checking for a TTY before outputting colors.
|
||||||
|
ForceColors bool
|
||||||
|
|
||||||
|
// Force disabling colors.
|
||||||
|
DisableColors bool
|
||||||
|
|
||||||
|
// Force quoting of all values
|
||||||
|
ForceQuote bool
|
||||||
|
|
||||||
|
// DisableQuote disables quoting for all values.
|
||||||
|
// DisableQuote will have a lower priority than ForceQuote.
|
||||||
|
// If both of them are set to true, quote will be forced on all values.
|
||||||
|
DisableQuote bool
|
||||||
|
|
||||||
|
// Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
|
||||||
|
EnvironmentOverrideColors bool
|
||||||
|
|
||||||
|
// Disable timestamp logging. useful when output is redirected to logging
|
||||||
|
// system that already adds timestamps.
|
||||||
|
DisableTimestamp bool
|
||||||
|
|
||||||
|
// Enable logging the full timestamp when a TTY is attached instead of just
|
||||||
|
// the time passed since beginning of execution.
|
||||||
|
FullTimestamp bool
|
||||||
|
|
||||||
|
// TimestampFormat to use for display when a full timestamp is printed.
|
||||||
|
// The format to use is the same than for time.Format or time.Parse from the standard
|
||||||
|
// library.
|
||||||
|
// The standard Library already provides a set of predefined format.
|
||||||
|
TimestampFormat string
|
||||||
|
|
||||||
|
// The fields are sorted by default for a consistent output. For applications
|
||||||
|
// that log extremely frequently and don't use the JSON formatter this may not
|
||||||
|
// be desired.
|
||||||
|
DisableSorting bool
|
||||||
|
|
||||||
|
// The keys sorting function, when uninitialized it uses sort.Strings.
|
||||||
|
SortingFunc func([]string)
|
||||||
|
|
||||||
|
// Disables the truncation of the level text to 4 characters.
|
||||||
|
DisableLevelTruncation bool
|
||||||
|
|
||||||
|
// PadLevelText Adds padding the level text so that all the levels output at the same length
|
||||||
|
// PadLevelText is a superset of the DisableLevelTruncation option
|
||||||
|
PadLevelText bool
|
||||||
|
|
||||||
|
// QuoteEmptyFields will wrap empty fields in quotes if true
|
||||||
|
QuoteEmptyFields bool
|
||||||
|
|
||||||
|
// Whether the logger's out is to a terminal
|
||||||
|
isTerminal bool
|
||||||
|
|
||||||
|
// FieldMap allows users to customize the names of keys for default fields.
|
||||||
|
// As an example:
|
||||||
|
// formatter := &TextFormatter{
|
||||||
|
// FieldMap: FieldMap{
|
||||||
|
// FieldKeyTime: "@timestamp",
|
||||||
|
// FieldKeyLevel: "@level",
|
||||||
|
// FieldKeyMsg: "@message"}}
|
||||||
|
FieldMap FieldMap
|
||||||
|
|
||||||
|
// CallerPrettyfier can be set by the user to modify the content
|
||||||
|
// of the function and file keys in the data when ReportCaller is
|
||||||
|
// activated. If any of the returned value is the empty string the
|
||||||
|
// corresponding key will be removed from fields.
|
||||||
|
CallerPrettyfier func(*runtime.Frame) (function string, file string)
|
||||||
|
|
||||||
|
terminalInitOnce sync.Once
|
||||||
|
|
||||||
|
// The max length of the level text, generated dynamically on init
|
||||||
|
levelTextMaxLength int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) init(entry *Entry) {
|
||||||
|
if entry.Logger != nil {
|
||||||
|
f.isTerminal = checkIfTerminal(entry.Logger.Out)
|
||||||
|
}
|
||||||
|
// Get the max length of the level text
|
||||||
|
for _, level := range AllLevels {
|
||||||
|
levelTextLength := utf8.RuneCount([]byte(level.String()))
|
||||||
|
if levelTextLength > f.levelTextMaxLength {
|
||||||
|
f.levelTextMaxLength = levelTextLength
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) isColored() bool {
|
||||||
|
isColored := f.ForceColors || (f.isTerminal && (runtime.GOOS != "windows"))
|
||||||
|
|
||||||
|
if f.EnvironmentOverrideColors {
|
||||||
|
switch force, ok := os.LookupEnv("CLICOLOR_FORCE"); {
|
||||||
|
case ok && force != "0":
|
||||||
|
isColored = true
|
||||||
|
case ok && force == "0", os.Getenv("CLICOLOR") == "0":
|
||||||
|
isColored = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return isColored && !f.DisableColors
|
||||||
|
}
|
||||||
|
|
||||||
|
// Format renders a single log entry
|
||||||
|
func (f *TextFormatter) Format(entry *Entry) ([]byte, error) {
|
||||||
|
data := make(Fields)
|
||||||
|
for k, v := range entry.Data {
|
||||||
|
data[k] = v
|
||||||
|
}
|
||||||
|
prefixFieldClashes(data, f.FieldMap, entry.HasCaller())
|
||||||
|
keys := make([]string, 0, len(data))
|
||||||
|
for k := range data {
|
||||||
|
keys = append(keys, k)
|
||||||
|
}
|
||||||
|
|
||||||
|
var funcVal, fileVal string
|
||||||
|
|
||||||
|
fixedKeys := make([]string, 0, 4+len(data))
|
||||||
|
if !f.DisableTimestamp {
|
||||||
|
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyTime))
|
||||||
|
}
|
||||||
|
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLevel))
|
||||||
|
if entry.Message != "" {
|
||||||
|
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyMsg))
|
||||||
|
}
|
||||||
|
if entry.err != "" {
|
||||||
|
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyLogrusError))
|
||||||
|
}
|
||||||
|
if entry.HasCaller() {
|
||||||
|
if f.CallerPrettyfier != nil {
|
||||||
|
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
||||||
|
} else {
|
||||||
|
funcVal = entry.Caller.Function
|
||||||
|
fileVal = fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
||||||
|
}
|
||||||
|
|
||||||
|
if funcVal != "" {
|
||||||
|
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFunc))
|
||||||
|
}
|
||||||
|
if fileVal != "" {
|
||||||
|
fixedKeys = append(fixedKeys, f.FieldMap.resolve(FieldKeyFile))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.DisableSorting {
|
||||||
|
if f.SortingFunc == nil {
|
||||||
|
sort.Strings(keys)
|
||||||
|
fixedKeys = append(fixedKeys, keys...)
|
||||||
|
} else {
|
||||||
|
if !f.isColored() {
|
||||||
|
fixedKeys = append(fixedKeys, keys...)
|
||||||
|
f.SortingFunc(fixedKeys)
|
||||||
|
} else {
|
||||||
|
f.SortingFunc(keys)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fixedKeys = append(fixedKeys, keys...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var b *bytes.Buffer
|
||||||
|
if entry.Buffer != nil {
|
||||||
|
b = entry.Buffer
|
||||||
|
} else {
|
||||||
|
b = &bytes.Buffer{}
|
||||||
|
}
|
||||||
|
|
||||||
|
f.terminalInitOnce.Do(func() { f.init(entry) })
|
||||||
|
|
||||||
|
timestampFormat := f.TimestampFormat
|
||||||
|
if timestampFormat == "" {
|
||||||
|
timestampFormat = defaultTimestampFormat
|
||||||
|
}
|
||||||
|
if f.isColored() {
|
||||||
|
f.printColored(b, entry, keys, data, timestampFormat)
|
||||||
|
} else {
|
||||||
|
|
||||||
|
for _, key := range fixedKeys {
|
||||||
|
var value interface{}
|
||||||
|
switch {
|
||||||
|
case key == f.FieldMap.resolve(FieldKeyTime):
|
||||||
|
value = entry.Time.Format(timestampFormat)
|
||||||
|
case key == f.FieldMap.resolve(FieldKeyLevel):
|
||||||
|
value = entry.Level.String()
|
||||||
|
case key == f.FieldMap.resolve(FieldKeyMsg):
|
||||||
|
value = entry.Message
|
||||||
|
case key == f.FieldMap.resolve(FieldKeyLogrusError):
|
||||||
|
value = entry.err
|
||||||
|
case key == f.FieldMap.resolve(FieldKeyFunc) && entry.HasCaller():
|
||||||
|
value = funcVal
|
||||||
|
case key == f.FieldMap.resolve(FieldKeyFile) && entry.HasCaller():
|
||||||
|
value = fileVal
|
||||||
|
default:
|
||||||
|
value = data[key]
|
||||||
|
}
|
||||||
|
f.appendKeyValue(b, key, value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
b.WriteByte('\n')
|
||||||
|
return b.Bytes(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) printColored(b *bytes.Buffer, entry *Entry, keys []string, data Fields, timestampFormat string) {
|
||||||
|
var levelColor int
|
||||||
|
switch entry.Level {
|
||||||
|
case DebugLevel, TraceLevel:
|
||||||
|
levelColor = gray
|
||||||
|
case WarnLevel:
|
||||||
|
levelColor = yellow
|
||||||
|
case ErrorLevel, FatalLevel, PanicLevel:
|
||||||
|
levelColor = red
|
||||||
|
case InfoLevel:
|
||||||
|
levelColor = blue
|
||||||
|
default:
|
||||||
|
levelColor = blue
|
||||||
|
}
|
||||||
|
|
||||||
|
levelText := strings.ToUpper(entry.Level.String())
|
||||||
|
if !f.DisableLevelTruncation && !f.PadLevelText {
|
||||||
|
levelText = levelText[0:4]
|
||||||
|
}
|
||||||
|
if f.PadLevelText {
|
||||||
|
// Generates the format string used in the next line, for example "%-6s" or "%-7s".
|
||||||
|
// Based on the max level text length.
|
||||||
|
formatString := "%-" + strconv.Itoa(f.levelTextMaxLength) + "s"
|
||||||
|
// Formats the level text by appending spaces up to the max length, for example:
|
||||||
|
// - "INFO "
|
||||||
|
// - "WARNING"
|
||||||
|
levelText = fmt.Sprintf(formatString, levelText)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove a single newline if it already exists in the message to keep
|
||||||
|
// the behavior of logrus text_formatter the same as the stdlib log package
|
||||||
|
entry.Message = strings.TrimSuffix(entry.Message, "\n")
|
||||||
|
|
||||||
|
caller := ""
|
||||||
|
if entry.HasCaller() {
|
||||||
|
funcVal := fmt.Sprintf("%s()", entry.Caller.Function)
|
||||||
|
fileVal := fmt.Sprintf("%s:%d", entry.Caller.File, entry.Caller.Line)
|
||||||
|
|
||||||
|
if f.CallerPrettyfier != nil {
|
||||||
|
funcVal, fileVal = f.CallerPrettyfier(entry.Caller)
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileVal == "" {
|
||||||
|
caller = funcVal
|
||||||
|
} else if funcVal == "" {
|
||||||
|
caller = fileVal
|
||||||
|
} else {
|
||||||
|
caller = fileVal + " " + funcVal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case f.DisableTimestamp:
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m%s %-44s ", levelColor, levelText, caller, entry.Message)
|
||||||
|
case !f.FullTimestamp:
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%04d]%s %-44s ", levelColor, levelText, int(entry.Time.Sub(baseTimestamp)/time.Second), caller, entry.Message)
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(b, "\x1b[%dm%s\x1b[0m[%s]%s %-44s ", levelColor, levelText, entry.Time.Format(timestampFormat), caller, entry.Message)
|
||||||
|
}
|
||||||
|
for _, k := range keys {
|
||||||
|
v := data[k]
|
||||||
|
fmt.Fprintf(b, " \x1b[%dm%s\x1b[0m=", levelColor, k)
|
||||||
|
f.appendValue(b, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) needsQuoting(text string) bool {
|
||||||
|
if f.ForceQuote {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if f.QuoteEmptyFields && len(text) == 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
if f.DisableQuote {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
for _, ch := range text {
|
||||||
|
//nolint:staticcheck // QF1001: could apply De Morgan's law
|
||||||
|
if !((ch >= 'a' && ch <= 'z') ||
|
||||||
|
(ch >= 'A' && ch <= 'Z') ||
|
||||||
|
(ch >= '0' && ch <= '9') ||
|
||||||
|
ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) appendKeyValue(b *bytes.Buffer, key string, value interface{}) {
|
||||||
|
if b.Len() > 0 {
|
||||||
|
b.WriteByte(' ')
|
||||||
|
}
|
||||||
|
b.WriteString(key)
|
||||||
|
b.WriteByte('=')
|
||||||
|
f.appendValue(b, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *TextFormatter) appendValue(b *bytes.Buffer, value interface{}) {
|
||||||
|
stringVal, ok := value.(string)
|
||||||
|
if !ok {
|
||||||
|
stringVal = fmt.Sprint(value)
|
||||||
|
}
|
||||||
|
|
||||||
|
if !f.needsQuoting(stringVal) {
|
||||||
|
b.WriteString(stringVal)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(b, "%q", stringVal)
|
||||||
|
}
|
||||||
|
}
|
||||||
102
vendor/github.com/sirupsen/logrus/writer.go
generated
vendored
Normal file
102
vendor/github.com/sirupsen/logrus/writer.go
generated
vendored
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
package logrus
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"io"
|
||||||
|
"runtime"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Writer at INFO level. See WriterLevel for details.
|
||||||
|
func (logger *Logger) Writer() *io.PipeWriter {
|
||||||
|
return logger.WriterLevel(InfoLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriterLevel returns an io.Writer that can be used to write arbitrary text to
|
||||||
|
// the logger at the given log level. Each line written to the writer will be
|
||||||
|
// printed in the usual way using formatters and hooks. The writer is part of an
|
||||||
|
// io.Pipe and it is the callers responsibility to close the writer when done.
|
||||||
|
// This can be used to override the standard library logger easily.
|
||||||
|
func (logger *Logger) WriterLevel(level Level) *io.PipeWriter {
|
||||||
|
return NewEntry(logger).WriterLevel(level)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Writer returns an io.Writer that writes to the logger at the info log level
|
||||||
|
func (entry *Entry) Writer() *io.PipeWriter {
|
||||||
|
return entry.WriterLevel(InfoLevel)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriterLevel returns an io.Writer that writes to the logger at the given log level
|
||||||
|
func (entry *Entry) WriterLevel(level Level) *io.PipeWriter {
|
||||||
|
reader, writer := io.Pipe()
|
||||||
|
|
||||||
|
var printFunc func(args ...interface{})
|
||||||
|
|
||||||
|
// Determine which log function to use based on the specified log level
|
||||||
|
switch level {
|
||||||
|
case TraceLevel:
|
||||||
|
printFunc = entry.Trace
|
||||||
|
case DebugLevel:
|
||||||
|
printFunc = entry.Debug
|
||||||
|
case InfoLevel:
|
||||||
|
printFunc = entry.Info
|
||||||
|
case WarnLevel:
|
||||||
|
printFunc = entry.Warn
|
||||||
|
case ErrorLevel:
|
||||||
|
printFunc = entry.Error
|
||||||
|
case FatalLevel:
|
||||||
|
printFunc = entry.Fatal
|
||||||
|
case PanicLevel:
|
||||||
|
printFunc = entry.Panic
|
||||||
|
default:
|
||||||
|
printFunc = entry.Print
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start a new goroutine to scan the input and write it to the logger using the specified print function.
|
||||||
|
// It splits the input into chunks of up to 64KB to avoid buffer overflows.
|
||||||
|
go entry.writerScanner(reader, printFunc)
|
||||||
|
|
||||||
|
// Set a finalizer function to close the writer when it is garbage collected
|
||||||
|
runtime.SetFinalizer(writer, writerFinalizer)
|
||||||
|
|
||||||
|
return writer
|
||||||
|
}
|
||||||
|
|
||||||
|
// writerScanner scans the input from the reader and writes it to the logger
|
||||||
|
func (entry *Entry) writerScanner(reader *io.PipeReader, printFunc func(args ...interface{})) {
|
||||||
|
scanner := bufio.NewScanner(reader)
|
||||||
|
|
||||||
|
// Set the buffer size to the maximum token size to avoid buffer overflows
|
||||||
|
scanner.Buffer(make([]byte, bufio.MaxScanTokenSize), bufio.MaxScanTokenSize)
|
||||||
|
|
||||||
|
// Define a split function to split the input into chunks of up to 64KB
|
||||||
|
chunkSize := bufio.MaxScanTokenSize // 64KB
|
||||||
|
splitFunc := func(data []byte, atEOF bool) (int, []byte, error) {
|
||||||
|
if len(data) >= chunkSize {
|
||||||
|
return chunkSize, data[:chunkSize], nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return bufio.ScanLines(data, atEOF)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Use the custom split function to split the input
|
||||||
|
scanner.Split(splitFunc)
|
||||||
|
|
||||||
|
// Scan the input and write it to the logger using the specified print function
|
||||||
|
for scanner.Scan() {
|
||||||
|
printFunc(strings.TrimRight(scanner.Text(), "\r\n"))
|
||||||
|
}
|
||||||
|
|
||||||
|
// If there was an error while scanning the input, log an error
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
entry.Errorf("Error while reading from Writer: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close the reader when we are done
|
||||||
|
reader.Close()
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriterFinalizer is a finalizer function that closes then given writer when it is garbage collected
|
||||||
|
func writerFinalizer(writer *io.PipeWriter) {
|
||||||
|
writer.Close()
|
||||||
|
}
|
||||||
27
vendor/golang.org/x/sys/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/sys/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||||
|
|
||||||
|
Redistribution and use in source and binary forms, with or without
|
||||||
|
modification, are permitted provided that the following conditions are
|
||||||
|
met:
|
||||||
|
|
||||||
|
* Redistributions of source code must retain the above copyright
|
||||||
|
notice, this list of conditions and the following disclaimer.
|
||||||
|
* Redistributions in binary form must reproduce the above
|
||||||
|
copyright notice, this list of conditions and the following disclaimer
|
||||||
|
in the documentation and/or other materials provided with the
|
||||||
|
distribution.
|
||||||
|
* Neither the name of Google Inc. nor the names of its
|
||||||
|
contributors may be used to endorse or promote products derived from
|
||||||
|
this software without specific prior written permission.
|
||||||
|
|
||||||
|
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
22
vendor/golang.org/x/sys/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/sys/PATENTS
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
Additional IP Rights Grant (Patents)
|
||||||
|
|
||||||
|
"This implementation" means the copyrightable works distributed by
|
||||||
|
Google as part of the Go project.
|
||||||
|
|
||||||
|
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||||
|
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||||
|
patent license to make, have made, use, offer to sell, sell, import,
|
||||||
|
transfer and otherwise run, modify and propagate the contents of this
|
||||||
|
implementation of Go, where such license applies only to those patent
|
||||||
|
claims, both currently owned or controlled by Google and acquired in
|
||||||
|
the future, licensable by Google that are necessarily infringed by this
|
||||||
|
implementation of Go. This grant does not include claims that would be
|
||||||
|
infringed only as a consequence of further modification of this
|
||||||
|
implementation. If you or your agent or exclusive licensee institute or
|
||||||
|
order or agree to the institution of patent litigation against any
|
||||||
|
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||||
|
that this implementation of Go or any code incorporated within this
|
||||||
|
implementation of Go constitutes direct or contributory patent
|
||||||
|
infringement, or inducement of patent infringement, then any patent
|
||||||
|
rights granted to you under this License for this implementation of Go
|
||||||
|
shall terminate as of the date such litigation is filed.
|
||||||
2
vendor/golang.org/x/sys/unix/.gitignore
generated
vendored
Normal file
2
vendor/golang.org/x/sys/unix/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
_obj/
|
||||||
|
unix.test
|
||||||
184
vendor/golang.org/x/sys/unix/README.md
generated
vendored
Normal file
184
vendor/golang.org/x/sys/unix/README.md
generated
vendored
Normal file
@@ -0,0 +1,184 @@
|
|||||||
|
# Building `sys/unix`
|
||||||
|
|
||||||
|
The sys/unix package provides access to the raw system call interface of the
|
||||||
|
underlying operating system. See: https://godoc.org/golang.org/x/sys/unix
|
||||||
|
|
||||||
|
Porting Go to a new architecture/OS combination or adding syscalls, types, or
|
||||||
|
constants to an existing architecture/OS pair requires some manual effort;
|
||||||
|
however, there are tools that automate much of the process.
|
||||||
|
|
||||||
|
## Build Systems
|
||||||
|
|
||||||
|
There are currently two ways we generate the necessary files. We are currently
|
||||||
|
migrating the build system to use containers so the builds are reproducible.
|
||||||
|
This is being done on an OS-by-OS basis. Please update this documentation as
|
||||||
|
components of the build system change.
|
||||||
|
|
||||||
|
### Old Build System (currently for `GOOS != "linux"`)
|
||||||
|
|
||||||
|
The old build system generates the Go files based on the C header files
|
||||||
|
present on your system. This means that files
|
||||||
|
for a given GOOS/GOARCH pair must be generated on a system with that OS and
|
||||||
|
architecture. This also means that the generated code can differ from system
|
||||||
|
to system, based on differences in the header files.
|
||||||
|
|
||||||
|
To avoid this, if you are using the old build system, only generate the Go
|
||||||
|
files on an installation with unmodified header files. It is also important to
|
||||||
|
keep track of which version of the OS the files were generated from (ex.
|
||||||
|
Darwin 14 vs Darwin 15). This makes it easier to track the progress of changes
|
||||||
|
and have each OS upgrade correspond to a single change.
|
||||||
|
|
||||||
|
To build the files for your current OS and architecture, make sure GOOS and
|
||||||
|
GOARCH are set correctly and run `mkall.sh`. This will generate the files for
|
||||||
|
your specific system. Running `mkall.sh -n` shows the commands that will be run.
|
||||||
|
|
||||||
|
Requirements: bash, go
|
||||||
|
|
||||||
|
### New Build System (currently for `GOOS == "linux"`)
|
||||||
|
|
||||||
|
The new build system uses a Docker container to generate the go files directly
|
||||||
|
from source checkouts of the kernel and various system libraries. This means
|
||||||
|
that on any platform that supports Docker, all the files using the new build
|
||||||
|
system can be generated at once, and generated files will not change based on
|
||||||
|
what the person running the scripts has installed on their computer.
|
||||||
|
|
||||||
|
The OS specific files for the new build system are located in the `${GOOS}`
|
||||||
|
directory, and the build is coordinated by the `${GOOS}/mkall.go` program. When
|
||||||
|
the kernel or system library updates, modify the Dockerfile at
|
||||||
|
`${GOOS}/Dockerfile` to checkout the new release of the source.
|
||||||
|
|
||||||
|
To build all the files under the new build system, you must be on an amd64/Linux
|
||||||
|
system and have your GOOS and GOARCH set accordingly. Running `mkall.sh` will
|
||||||
|
then generate all of the files for all of the GOOS/GOARCH pairs in the new build
|
||||||
|
system. Running `mkall.sh -n` shows the commands that will be run.
|
||||||
|
|
||||||
|
Requirements: bash, go, docker
|
||||||
|
|
||||||
|
## Component files
|
||||||
|
|
||||||
|
This section describes the various files used in the code generation process.
|
||||||
|
It also contains instructions on how to modify these files to add a new
|
||||||
|
architecture/OS or to add additional syscalls, types, or constants. Note that
|
||||||
|
if you are using the new build system, the scripts/programs cannot be called normally.
|
||||||
|
They must be called from within the docker container.
|
||||||
|
|
||||||
|
### asm files
|
||||||
|
|
||||||
|
The hand-written assembly file at `asm_${GOOS}_${GOARCH}.s` implements system
|
||||||
|
call dispatch. There are three entry points:
|
||||||
|
```
|
||||||
|
func Syscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||||
|
func Syscall6(trap, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2, err uintptr)
|
||||||
|
func RawSyscall(trap, a1, a2, a3 uintptr) (r1, r2, err uintptr)
|
||||||
|
```
|
||||||
|
The first and second are the standard ones; they differ only in how many
|
||||||
|
arguments can be passed to the kernel. The third is for low-level use by the
|
||||||
|
ForkExec wrapper. Unlike the first two, it does not call into the scheduler to
|
||||||
|
let it know that a system call is running.
|
||||||
|
|
||||||
|
When porting Go to a new architecture/OS, this file must be implemented for
|
||||||
|
each GOOS/GOARCH pair.
|
||||||
|
|
||||||
|
### mksysnum
|
||||||
|
|
||||||
|
Mksysnum is a Go program located at `${GOOS}/mksysnum.go` (or `mksysnum_${GOOS}.go`
|
||||||
|
for the old system). This program takes in a list of header files containing the
|
||||||
|
syscall number declarations and parses them to produce the corresponding list of
|
||||||
|
Go numeric constants. See `zsysnum_${GOOS}_${GOARCH}.go` for the generated
|
||||||
|
constants.
|
||||||
|
|
||||||
|
Adding new syscall numbers is mostly done by running the build on a sufficiently
|
||||||
|
new installation of the target OS (or updating the source checkouts for the
|
||||||
|
new build system). However, depending on the OS, you may need to update the
|
||||||
|
parsing in mksysnum.
|
||||||
|
|
||||||
|
### mksyscall.go
|
||||||
|
|
||||||
|
The `syscall.go`, `syscall_${GOOS}.go`, `syscall_${GOOS}_${GOARCH}.go` are
|
||||||
|
hand-written Go files which implement system calls (for unix, the specific OS,
|
||||||
|
or the specific OS/Architecture pair respectively) that need special handling
|
||||||
|
and list `//sys` comments giving prototypes for ones that can be generated.
|
||||||
|
|
||||||
|
The mksyscall.go program takes the `//sys` and `//sysnb` comments and converts
|
||||||
|
them into syscalls. This requires the name of the prototype in the comment to
|
||||||
|
match a syscall number in the `zsysnum_${GOOS}_${GOARCH}.go` file. The function
|
||||||
|
prototype can be exported (capitalized) or not.
|
||||||
|
|
||||||
|
Adding a new syscall often just requires adding a new `//sys` function prototype
|
||||||
|
with the desired arguments and a capitalized name so it is exported. However, if
|
||||||
|
you want the interface to the syscall to be different, often one will make an
|
||||||
|
unexported `//sys` prototype, and then write a custom wrapper in
|
||||||
|
`syscall_${GOOS}.go`.
|
||||||
|
|
||||||
|
### types files
|
||||||
|
|
||||||
|
For each OS, there is a hand-written Go file at `${GOOS}/types.go` (or
|
||||||
|
`types_${GOOS}.go` on the old system). This file includes standard C headers and
|
||||||
|
creates Go type aliases to the corresponding C types. The file is then fed
|
||||||
|
through godef to get the Go compatible definitions. Finally, the generated code
|
||||||
|
is fed though mkpost.go to format the code correctly and remove any hidden or
|
||||||
|
private identifiers. This cleaned-up code is written to
|
||||||
|
`ztypes_${GOOS}_${GOARCH}.go`.
|
||||||
|
|
||||||
|
The hardest part about preparing this file is figuring out which headers to
|
||||||
|
include and which symbols need to be `#define`d to get the actual data
|
||||||
|
structures that pass through to the kernel system calls. Some C libraries
|
||||||
|
preset alternate versions for binary compatibility and translate them on the
|
||||||
|
way in and out of system calls, but there is almost always a `#define` that can
|
||||||
|
get the real ones.
|
||||||
|
See `types_darwin.go` and `linux/types.go` for examples.
|
||||||
|
|
||||||
|
To add a new type, add in the necessary include statement at the top of the
|
||||||
|
file (if it is not already there) and add in a type alias line. Note that if
|
||||||
|
your type is significantly different on different architectures, you may need
|
||||||
|
some `#if/#elif` macros in your include statements.
|
||||||
|
|
||||||
|
### mkerrors.sh
|
||||||
|
|
||||||
|
This script is used to generate the system's various constants. This doesn't
|
||||||
|
just include the error numbers and error strings, but also the signal numbers
|
||||||
|
and a wide variety of miscellaneous constants. The constants come from the list
|
||||||
|
of include files in the `includes_${uname}` variable. A regex then picks out
|
||||||
|
the desired `#define` statements, and generates the corresponding Go constants.
|
||||||
|
The error numbers and strings are generated from `#include <errno.h>`, and the
|
||||||
|
signal numbers and strings are generated from `#include <signal.h>`. All of
|
||||||
|
these constants are written to `zerrors_${GOOS}_${GOARCH}.go` via a C program,
|
||||||
|
`_errors.c`, which prints out all the constants.
|
||||||
|
|
||||||
|
To add a constant, add the header that includes it to the appropriate variable.
|
||||||
|
Then, edit the regex (if necessary) to match the desired constant. Avoid making
|
||||||
|
the regex too broad to avoid matching unintended constants.
|
||||||
|
|
||||||
|
### internal/mkmerge
|
||||||
|
|
||||||
|
This program is used to extract duplicate const, func, and type declarations
|
||||||
|
from the generated architecture-specific files listed below, and merge these
|
||||||
|
into a common file for each OS.
|
||||||
|
|
||||||
|
The merge is performed in the following steps:
|
||||||
|
1. Construct the set of common code that is idential in all architecture-specific files.
|
||||||
|
2. Write this common code to the merged file.
|
||||||
|
3. Remove the common code from all architecture-specific files.
|
||||||
|
|
||||||
|
|
||||||
|
## Generated files
|
||||||
|
|
||||||
|
### `zerrors_${GOOS}_${GOARCH}.go`
|
||||||
|
|
||||||
|
A file containing all of the system's generated error numbers, error strings,
|
||||||
|
signal numbers, and constants. Generated by `mkerrors.sh` (see above).
|
||||||
|
|
||||||
|
### `zsyscall_${GOOS}_${GOARCH}.go`
|
||||||
|
|
||||||
|
A file containing all the generated syscalls for a specific GOOS and GOARCH.
|
||||||
|
Generated by `mksyscall.go` (see above).
|
||||||
|
|
||||||
|
### `zsysnum_${GOOS}_${GOARCH}.go`
|
||||||
|
|
||||||
|
A list of numeric constants for all the syscall number of the specific GOOS
|
||||||
|
and GOARCH. Generated by mksysnum (see above).
|
||||||
|
|
||||||
|
### `ztypes_${GOOS}_${GOARCH}.go`
|
||||||
|
|
||||||
|
A file containing Go types for passing into (or returning from) syscalls.
|
||||||
|
Generated by godefs and the types file (see above).
|
||||||
86
vendor/golang.org/x/sys/unix/affinity_linux.go
generated
vendored
Normal file
86
vendor/golang.org/x/sys/unix/affinity_linux.go
generated
vendored
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
// CPU affinity functions
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import (
|
||||||
|
"math/bits"
|
||||||
|
"unsafe"
|
||||||
|
)
|
||||||
|
|
||||||
|
const cpuSetSize = _CPU_SETSIZE / _NCPUBITS
|
||||||
|
|
||||||
|
// CPUSet represents a CPU affinity mask.
|
||||||
|
type CPUSet [cpuSetSize]cpuMask
|
||||||
|
|
||||||
|
func schedAffinity(trap uintptr, pid int, set *CPUSet) error {
|
||||||
|
_, _, e := RawSyscall(trap, uintptr(pid), uintptr(unsafe.Sizeof(*set)), uintptr(unsafe.Pointer(set)))
|
||||||
|
if e != 0 {
|
||||||
|
return errnoErr(e)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchedGetaffinity gets the CPU affinity mask of the thread specified by pid.
|
||||||
|
// If pid is 0 the calling thread is used.
|
||||||
|
func SchedGetaffinity(pid int, set *CPUSet) error {
|
||||||
|
return schedAffinity(SYS_SCHED_GETAFFINITY, pid, set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SchedSetaffinity sets the CPU affinity mask of the thread specified by pid.
|
||||||
|
// If pid is 0 the calling thread is used.
|
||||||
|
func SchedSetaffinity(pid int, set *CPUSet) error {
|
||||||
|
return schedAffinity(SYS_SCHED_SETAFFINITY, pid, set)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Zero clears the set s, so that it contains no CPUs.
|
||||||
|
func (s *CPUSet) Zero() {
|
||||||
|
for i := range s {
|
||||||
|
s[i] = 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func cpuBitsIndex(cpu int) int {
|
||||||
|
return cpu / _NCPUBITS
|
||||||
|
}
|
||||||
|
|
||||||
|
func cpuBitsMask(cpu int) cpuMask {
|
||||||
|
return cpuMask(1 << (uint(cpu) % _NCPUBITS))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set adds cpu to the set s.
|
||||||
|
func (s *CPUSet) Set(cpu int) {
|
||||||
|
i := cpuBitsIndex(cpu)
|
||||||
|
if i < len(s) {
|
||||||
|
s[i] |= cpuBitsMask(cpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear removes cpu from the set s.
|
||||||
|
func (s *CPUSet) Clear(cpu int) {
|
||||||
|
i := cpuBitsIndex(cpu)
|
||||||
|
if i < len(s) {
|
||||||
|
s[i] &^= cpuBitsMask(cpu)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// IsSet reports whether cpu is in the set s.
|
||||||
|
func (s *CPUSet) IsSet(cpu int) bool {
|
||||||
|
i := cpuBitsIndex(cpu)
|
||||||
|
if i < len(s) {
|
||||||
|
return s[i]&cpuBitsMask(cpu) != 0
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Count returns the number of CPUs in the set s.
|
||||||
|
func (s *CPUSet) Count() int {
|
||||||
|
c := 0
|
||||||
|
for _, b := range s {
|
||||||
|
c += bits.OnesCount64(uint64(b))
|
||||||
|
}
|
||||||
|
return c
|
||||||
|
}
|
||||||
15
vendor/golang.org/x/sys/unix/aliases.go
generated
vendored
Normal file
15
vendor/golang.org/x/sys/unix/aliases.go
generated
vendored
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build (aix || darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris || zos) && go1.9
|
||||||
|
// +build aix darwin dragonfly freebsd linux netbsd openbsd solaris zos
|
||||||
|
// +build go1.9
|
||||||
|
|
||||||
|
package unix
|
||||||
|
|
||||||
|
import "syscall"
|
||||||
|
|
||||||
|
type Signal = syscall.Signal
|
||||||
|
type Errno = syscall.Errno
|
||||||
|
type SysProcAttr = syscall.SysProcAttr
|
||||||
18
vendor/golang.org/x/sys/unix/asm_aix_ppc64.s
generated
vendored
Normal file
18
vendor/golang.org/x/sys/unix/asm_aix_ppc64.s
generated
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
// Copyright 2018 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gc
|
||||||
|
// +build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for ppc64, AIX are implemented in runtime/syscall_aix.go
|
||||||
|
//
|
||||||
|
|
||||||
|
TEXT ·syscall6(SB),NOSPLIT,$0-88
|
||||||
|
JMP syscall·syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·rawSyscall6(SB),NOSPLIT,$0-88
|
||||||
|
JMP syscall·rawSyscall6(SB)
|
||||||
29
vendor/golang.org/x/sys/unix/asm_bsd_386.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_bsd_386.s
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build (freebsd || netbsd || openbsd) && gc
|
||||||
|
// +build freebsd netbsd openbsd
|
||||||
|
// +build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// System call support for 386 BSD
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
29
vendor/golang.org/x/sys/unix/asm_bsd_amd64.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_bsd_amd64.s
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build (darwin || dragonfly || freebsd || netbsd || openbsd) && gc
|
||||||
|
// +build darwin dragonfly freebsd netbsd openbsd
|
||||||
|
// +build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// System call support for AMD64 BSD
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
29
vendor/golang.org/x/sys/unix/asm_bsd_arm.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_bsd_arm.s
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build (freebsd || netbsd || openbsd) && gc
|
||||||
|
// +build freebsd netbsd openbsd
|
||||||
|
// +build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// System call support for ARM BSD
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
|
B syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
|
B syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-52
|
||||||
|
B syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
|
B syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
|
B syscall·RawSyscall6(SB)
|
||||||
29
vendor/golang.org/x/sys/unix/asm_bsd_arm64.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_bsd_arm64.s
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||||
|
// +build darwin freebsd netbsd openbsd
|
||||||
|
// +build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// System call support for ARM64 BSD
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
31
vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s
generated
vendored
Normal file
31
vendor/golang.org/x/sys/unix/asm_bsd_ppc64.s
generated
vendored
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Copyright 2022 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||||
|
// +build darwin freebsd netbsd openbsd
|
||||||
|
// +build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System call support for ppc64, BSD
|
||||||
|
//
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
29
vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s
generated
vendored
Normal file
29
vendor/golang.org/x/sys/unix/asm_bsd_riscv64.s
generated
vendored
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2021 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build (darwin || freebsd || netbsd || openbsd) && gc
|
||||||
|
// +build darwin freebsd netbsd openbsd
|
||||||
|
// +build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
// System call support for RISCV64 BSD
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall9(SB),NOSPLIT,$0-104
|
||||||
|
JMP syscall·Syscall9(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-56
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-80
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
66
vendor/golang.org/x/sys/unix/asm_linux_386.s
generated
vendored
Normal file
66
vendor/golang.org/x/sys/unix/asm_linux_386.s
generated
vendored
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// Copyright 2009 The Go Authors. All rights reserved.
|
||||||
|
// Use of this source code is governed by a BSD-style
|
||||||
|
// license that can be found in the LICENSE file.
|
||||||
|
|
||||||
|
//go:build gc
|
||||||
|
// +build gc
|
||||||
|
|
||||||
|
#include "textflag.h"
|
||||||
|
|
||||||
|
//
|
||||||
|
// System calls for 386, Linux
|
||||||
|
//
|
||||||
|
|
||||||
|
// See ../runtime/sys_linux_386.s for the reason why we always use int 0x80
|
||||||
|
// instead of the glibc-specific "CALL 0x10(GS)".
|
||||||
|
#define INVOKE_SYSCALL INT $0x80
|
||||||
|
|
||||||
|
// Just jump to package syscall's implementation for all these functions.
|
||||||
|
// The runtime may know about them.
|
||||||
|
|
||||||
|
TEXT ·Syscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·Syscall(SB)
|
||||||
|
|
||||||
|
TEXT ·Syscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·Syscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·SyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
CALL runtime·entersyscall(SB)
|
||||||
|
MOVL trap+0(FP), AX // syscall entry
|
||||||
|
MOVL a1+4(FP), BX
|
||||||
|
MOVL a2+8(FP), CX
|
||||||
|
MOVL a3+12(FP), DX
|
||||||
|
MOVL $0, SI
|
||||||
|
MOVL $0, DI
|
||||||
|
INVOKE_SYSCALL
|
||||||
|
MOVL AX, r1+16(FP)
|
||||||
|
MOVL DX, r2+20(FP)
|
||||||
|
CALL runtime·exitsyscall(SB)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·RawSyscall(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·RawSyscall(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscall6(SB),NOSPLIT,$0-40
|
||||||
|
JMP syscall·RawSyscall6(SB)
|
||||||
|
|
||||||
|
TEXT ·RawSyscallNoError(SB),NOSPLIT,$0-24
|
||||||
|
MOVL trap+0(FP), AX // syscall entry
|
||||||
|
MOVL a1+4(FP), BX
|
||||||
|
MOVL a2+8(FP), CX
|
||||||
|
MOVL a3+12(FP), DX
|
||||||
|
MOVL $0, SI
|
||||||
|
MOVL $0, DI
|
||||||
|
INVOKE_SYSCALL
|
||||||
|
MOVL AX, r1+16(FP)
|
||||||
|
MOVL DX, r2+20(FP)
|
||||||
|
RET
|
||||||
|
|
||||||
|
TEXT ·socketcall(SB),NOSPLIT,$0-36
|
||||||
|
JMP syscall·socketcall(SB)
|
||||||
|
|
||||||
|
TEXT ·rawsocketcall(SB),NOSPLIT,$0-36
|
||||||
|
JMP syscall·rawsocketcall(SB)
|
||||||
|
|
||||||
|
TEXT ·seek(SB),NOSPLIT,$0-28
|
||||||
|
JMP syscall·seek(SB)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user