chore: [full-ci] bump dependencies

This commit is contained in:
Roman Perekhod
2025-07-04 17:04:32 +02:00
parent a69f24ef5f
commit 32b454d41e
958 changed files with 55387 additions and 40791 deletions

143
go.mod
View File

@@ -5,7 +5,7 @@ go 1.24
toolchain go1.24.1
require (
dario.cat/mergo v1.0.1
dario.cat/mergo v1.0.2
github.com/CiscoM31/godata v1.0.10
github.com/KimMachineGun/automemlimit v0.7.3
github.com/Masterminds/semver v1.5.0
@@ -21,7 +21,7 @@ require (
github.com/dhowden/tag v0.0.0-20240417053706-3d75831295e8
github.com/dutchcoders/go-clamd v0.0.0-20170520113014-b970184f4d9e
github.com/egirna/icap-client v0.1.1
github.com/gabriel-vasile/mimetype v1.4.8
github.com/gabriel-vasile/mimetype v1.4.9
github.com/ggwhite/go-masker v1.1.0
github.com/go-chi/chi/v5 v5.2.2
github.com/go-chi/render v1.0.3
@@ -35,78 +35,78 @@ require (
github.com/go-micro/plugins/v4/store/nats-js-kv v0.0.0-20240726082623-6831adfdcdc4
github.com/go-micro/plugins/v4/wrapper/monitoring/prometheus v1.2.0
github.com/go-micro/plugins/v4/wrapper/trace/opentelemetry v1.2.0
github.com/go-playground/validator/v10 v10.26.0
github.com/go-playground/validator/v10 v10.27.0
github.com/gofrs/uuid v4.4.0+incompatible
github.com/golang-jwt/jwt/v5 v5.2.2
github.com/golang/protobuf v1.5.4
github.com/google/go-cmp v0.7.0
github.com/google/go-tika v0.3.1
github.com/google/uuid v1.6.0
github.com/gookit/config/v2 v2.2.5
github.com/gookit/config/v2 v2.2.6
github.com/gorilla/mux v1.8.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1
github.com/invopop/validation v0.8.0
github.com/jellydator/ttlcache/v2 v2.11.1
github.com/jellydator/ttlcache/v3 v3.3.0
github.com/jellydator/ttlcache/v3 v3.4.0
github.com/jinzhu/now v1.1.5
github.com/justinas/alice v1.2.0
github.com/kovidgoyal/imaging v1.6.3
github.com/leonelquinteros/gotext v1.7.1
github.com/kovidgoyal/imaging v1.6.4
github.com/leonelquinteros/gotext v1.7.2
github.com/libregraph/idm v0.5.0
github.com/libregraph/lico v0.65.0
github.com/libregraph/lico v0.66.0
github.com/mitchellh/mapstructure v1.5.0
github.com/mna/pigeon v1.3.0
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826
github.com/nats-io/nats-server/v2 v2.11.3
github.com/nats-io/nats.go v1.41.2
github.com/nats-io/nats-server/v2 v2.11.6
github.com/nats-io/nats.go v1.43.0
github.com/olekukonko/tablewriter v0.0.5
github.com/onsi/ginkgo v1.16.5
github.com/onsi/ginkgo/v2 v2.23.4
github.com/onsi/gomega v1.37.0
github.com/open-policy-agent/opa v1.4.2
github.com/open-policy-agent/opa v1.6.0
github.com/orcaman/concurrent-map v1.0.0
github.com/owncloud/libre-graph-api-go v1.0.5-0.20250217093259-fa3804be6c27
github.com/owncloud/reva/v2 v2.0.0-20250618124252-722b346820da
github.com/pkg/errors v0.9.1
github.com/pkg/xattr v0.4.10
github.com/prometheus/client_golang v1.21.1
github.com/pkg/xattr v0.4.12
github.com/prometheus/client_golang v1.22.0
github.com/r3labs/sse/v2 v2.10.0
github.com/riandyrn/otelchi v0.11.0
github.com/riandyrn/otelchi v0.12.1
github.com/rogpeppe/go-internal v1.14.1
github.com/rs/cors v1.11.1
github.com/rs/zerolog v1.34.0
github.com/shamaton/msgpack/v2 v2.2.2
github.com/shamaton/msgpack/v2 v2.2.3
github.com/sirupsen/logrus v1.9.3
github.com/spf13/afero v1.12.0
github.com/spf13/afero v1.14.0
github.com/spf13/cobra v1.9.1
github.com/stretchr/testify v1.10.0
github.com/test-go/testify v1.1.4
github.com/thejerf/suture/v4 v4.0.6
github.com/tidwall/gjson v1.18.0
github.com/tus/tusd/v2 v2.6.0
github.com/unrolled/secure v1.16.0
github.com/urfave/cli/v2 v2.27.5
github.com/tus/tusd/v2 v2.8.0
github.com/unrolled/secure v1.17.0
github.com/urfave/cli/v2 v2.27.7
github.com/xhit/go-simple-mail/v2 v2.16.0
go-micro.dev/v4 v4.11.0
go.etcd.io/bbolt v1.4.0
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0
go.opentelemetry.io/contrib/zpages v0.60.0
go.opentelemetry.io/otel v1.36.0
go.etcd.io/bbolt v1.4.2
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0
go.opentelemetry.io/contrib/zpages v0.62.0
go.opentelemetry.io/otel v1.37.0
go.opentelemetry.io/otel/exporters/jaeger v1.17.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0
go.opentelemetry.io/otel/sdk v1.36.0
go.opentelemetry.io/otel/trace v1.36.0
golang.org/x/crypto v0.38.0
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c
golang.org/x/image v0.27.0
golang.org/x/net v0.40.0
golang.org/x/oauth2 v0.28.0
golang.org/x/sync v0.14.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0
go.opentelemetry.io/otel/sdk v1.37.0
go.opentelemetry.io/otel/trace v1.37.0
golang.org/x/crypto v0.39.0
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
golang.org/x/image v0.28.0
golang.org/x/net v0.41.0
golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.15.0
golang.org/x/term v0.32.0
golang.org/x/text v0.25.0
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a
google.golang.org/grpc v1.72.1
golang.org/x/text v0.26.0
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822
google.golang.org/grpc v1.73.0
google.golang.org/protobuf v1.36.6
gopkg.in/yaml.v2 v2.4.0
gotest.tools/v3 v3.5.2
@@ -117,7 +117,7 @@ require (
contrib.go.opencensus.io/exporter/prometheus v0.4.2 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/BurntSushi/toml v1.4.0 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/Masterminds/goutils v1.1.1 // indirect
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
@@ -152,7 +152,7 @@ require (
github.com/blevesearch/zapx/v16 v16.2.4 // indirect
github.com/bluele/gcache v0.0.2 // indirect
github.com/bombsimon/logrusr/v3 v3.1.0 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/ceph/go-ceph v0.33.0 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cevaris/ordered_map v0.0.0-20190319150403-3adeae072e73 // indirect
@@ -160,13 +160,13 @@ require (
github.com/coreos/go-semver v0.3.0 // indirect
github.com/coreos/go-systemd/v22 v22.5.0 // indirect
github.com/cornelk/hashmap v1.0.8 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.6 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
github.com/crewjam/httperr v0.2.0 // indirect
github.com/crewjam/saml v0.4.14 // indirect
github.com/cyphar/filepath-securejoin v0.2.5 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/deckarep/golang-set v1.8.0 // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/desertbit/timer v1.0.1 // indirect
github.com/dgraph-io/ristretto v0.2.0 // indirect
github.com/dgryski/go-farm v0.0.0-20240924180020-3414d57e47da // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
@@ -176,9 +176,9 @@ require (
github.com/emirpasic/gods v1.18.1 // indirect
github.com/emvi/iso-639-1 v1.1.0 // indirect
github.com/evanphx/json-patch/v5 v5.5.0 // indirect
github.com/fatih/color v1.14.1 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.8.0 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/gdexlab/go-render v1.0.1 // indirect
github.com/go-acme/lego/v4 v4.4.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
@@ -190,7 +190,7 @@ require (
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-micro/plugins/v4/events/natsjs v1.2.2 // indirect
github.com/go-micro/plugins/v4/store/nats-js v1.2.1 // indirect
@@ -207,8 +207,8 @@ require (
github.com/gobwas/httphead v0.1.0 // indirect
github.com/gobwas/pool v0.2.1 // indirect
github.com/gobwas/ws v1.2.1 // indirect
github.com/goccy/go-json v0.10.3 // indirect
github.com/goccy/go-yaml v1.11.2 // indirect
github.com/goccy/go-json v0.10.5 // indirect
github.com/goccy/go-yaml v1.12.0 // indirect
github.com/gofrs/flock v0.12.1 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
@@ -216,16 +216,15 @@ require (
github.com/golang/snappy v0.0.4 // indirect
github.com/gomodule/redigo v1.9.2 // indirect
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-tpm v0.9.3 // indirect
github.com/google/go-tpm v0.9.5 // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/google/renameio/v2 v2.0.0 // indirect
github.com/gookit/color v1.5.4 // indirect
github.com/gookit/goutil v0.6.15 // indirect
github.com/gookit/goutil v0.7.0 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/schema v1.4.1 // indirect
github.com/grpc-ecosystem/go-grpc-middleware v1.4.0 // indirect
github.com/hashicorp/go-hclog v1.6.3 // indirect
github.com/hashicorp/go-plugin v1.6.2 // indirect
github.com/hashicorp/go-plugin v1.6.3 // indirect
github.com/hashicorp/yamux v0.1.1 // indirect
github.com/huandu/xstrings v1.5.0 // indirect
github.com/iancoleman/strcase v0.3.0 // indirect
@@ -233,7 +232,7 @@ require (
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/jonboulle/clockwork v0.2.2 // indirect
github.com/jonboulle/clockwork v0.5.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/juliangruber/go-intersect v1.1.0 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
@@ -244,9 +243,9 @@ require (
github.com/longsleep/go-metrics v1.0.0 // indirect
github.com/longsleep/rndm v1.2.0 // indirect
github.com/mattermost/xml-roundtrip-validator v0.1.0 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.15 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/mattn/go-sqlite3 v1.14.24 // indirect
github.com/maxymania/go-system v0.0.0-20170110133659-647cc364bf0b // indirect
github.com/mendsley/gojwk v0.0.0-20141217222730-4d5ec6e58103 // indirect
@@ -276,19 +275,19 @@ require (
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/pquerna/cachecontrol v0.2.0 // indirect
github.com/prometheus/alertmanager v0.27.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.65.0 // indirect
github.com/prometheus/procfs v0.17.0 // indirect
github.com/prometheus/statsd_exporter v0.22.8 // indirect
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 // indirect
github.com/rivo/uniseg v0.4.7 // indirect
github.com/rs/xid v1.6.0 // indirect
github.com/russellhaering/goxmldsig v1.4.0 // indirect
github.com/russellhaering/goxmldsig v1.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/segmentio/kafka-go v0.4.47 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/sercand/kuberesolver/v5 v5.1.1 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/sergi/go-diff v1.4.0 // indirect
github.com/sethvargo/go-password v0.3.1 // indirect
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 // indirect
github.com/shurcooL/vfsgen v0.0.0-20200824052919-0d455de96546 // indirect
@@ -297,16 +296,16 @@ require (
github.com/spf13/pflag v1.0.6 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/studio-b12/gowebdav v0.9.0 // indirect
github.com/tchap/go-patricia/v2 v2.3.2 // indirect
github.com/tchap/go-patricia/v2 v2.3.3 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208 // indirect
github.com/trustelem/zxcvbn v1.0.1 // indirect
github.com/vektah/gqlparser/v2 v2.5.30 // indirect
github.com/wk8/go-ordered-map v1.0.0 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 // indirect
github.com/yashtewari/glob-intersection v0.2.0 // indirect
go.etcd.io/etcd/api/v3 v3.5.16 // indirect
@@ -314,25 +313,27 @@ require (
go.etcd.io/etcd/client/v3 v3.5.16 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.36.0 // indirect
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
go.opentelemetry.io/otel/metric v1.37.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
go.uber.org/atomic v1.11.0 // indirect
go.uber.org/automaxprocs v1.6.0 // indirect
go.uber.org/multierr v1.9.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.23.0 // indirect
golang.org/x/mod v0.24.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
golang.org/x/mod v0.25.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.31.0 // indirect
golang.org/x/time v0.12.0 // indirect
golang.org/x/tools v0.34.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 // indirect
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
sigs.k8s.io/yaml v1.5.0 // indirect
)
replace github.com/studio-b12/gowebdav => github.com/kobergj/gowebdav v0.0.0-20250102091030-aa65266db202

296
go.sum
View File

@@ -36,8 +36,8 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9
contrib.go.opencensus.io/exporter/ocagent v0.4.12/go.mod h1:450APlNTSR6FrvC3CTRqYosuDstRB9un7SOx2k/9ckA=
contrib.go.opencensus.io/exporter/prometheus v0.4.2 h1:sqfsYl5GIY/L570iT+l93ehxaWJs2/OwXtiWwew3oAg=
contrib.go.opencensus.io/exporter/prometheus v0.4.2/go.mod h1:dvEHbiKmgvbr5pjaF9fpw1KeYcjrnC1J8B+JKjsZyRQ=
dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s=
dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
@@ -61,8 +61,8 @@ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbt
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CiscoM31/godata v1.0.10 h1:DZdJ6M8QNh4HquvDDOqNLu6h77Wl86KGK7Qlbmb90sk=
github.com/CiscoM31/godata v1.0.10/go.mod h1:ZMiT6JuD3Rm83HEtiTx4JEChsd25YCrxchKGag/sdTc=
@@ -109,6 +109,8 @@ github.com/alexedwards/argon2id v1.0.0/go.mod h1:tYKkqIjzXvZdzPvADMWOEZ+l6+BD6Ct
github.com/aliyun/alibaba-cloud-sdk-go v1.61.976/go.mod h1:pUKYbK5JQ+1Dfxk80P0qxGqe5dkxDoabbZS7zOcouyA=
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964 h1:I9YN9WMo3SUh7p/4wKeNvD/IQla3U3SUa61U7ul+xM4=
github.com/amoghe/go-crypt v0.0.0-20220222110647-20eada5f5964/go.mod h1:eFiR01PwTcpbzXtdMces7zxg6utvFM5puiWHpWB8D/k=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ=
github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antithesishq/antithesis-sdk-go v0.4.3-default-no-op h1:+OSa/t11TFhqfrX0EOSqQBDJ0YlpmK0rDSiB19dg9M0=
@@ -130,7 +132,6 @@ github.com/aws/aws-sdk-go v1.55.7 h1:UJrkFq7es5CShfBwlWAC8DA077vp8PyVbQd3lqLiztE
github.com/aws/aws-sdk-go v1.55.7/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/bbalet/stopwords v1.0.0 h1:0TnGycCtY0zZi4ltKoOGRFIlZHv0WqpoIGUsObjztfo=
github.com/bbalet/stopwords v1.0.0/go.mod h1:sAWrQoDMfqARGIn4s6dp7OW7ISrshUD8IP2q3KoqPjc=
github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A=
github.com/beevik/etree v1.5.1 h1:TC3zyxYp+81wAmbsi8SWUpZCurbxa6S8RITYRSkNRwo=
github.com/beevik/etree v1.5.1/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@@ -199,8 +200,8 @@ github.com/c-bata/go-prompt v0.2.5/go.mod h1:vFnjEGDIIA/Lib7giyE4E9c50Lvl8j0S+7F
github.com/cenkalti/backoff v2.2.1+incompatible h1:tNowT99t7UNflLxfYYSlKYsBpXdEet03Pg2g16Swow4=
github.com/cenkalti/backoff v2.2.1+incompatible/go.mod h1:90ReRw6GdpyfrHakVjL/QHaoyV4aDUVVkXQJJJ3NXXM=
github.com/cenkalti/backoff/v4 v4.1.0/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/census-instrumentation/opencensus-proto v0.2.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/ceph/go-ceph v0.33.0 h1:xT9v/MAa+DIBmflyITyFkGRgWngATghGegKJguEOInQ=
@@ -235,9 +236,9 @@ github.com/cornelk/hashmap v1.0.8/go.mod h1:RfZb7JO3RviW/rT6emczVuC/oxpdz4UsSB2L
github.com/cpu/goacmedns v0.1.1/go.mod h1:MuaouqEhPAHxsbqjgnck5zeghuwBP1dLnPoobeGqugQ=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=
github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=
github.com/crewjam/httperr v0.2.0 h1:b2BfXR8U3AlIHwNeFFvZ+BV1LFvKLlzMjzaTnZMybNo=
github.com/crewjam/httperr v0.2.0/go.mod h1:Jlz+Sg/XqBQhyMjdDiC+GNNRzZTD7x39Gu3pglZ5oH4=
github.com/crewjam/saml v0.4.14 h1:g9FBNx62osKusnFzs3QTN5L9CVA/Egfgm+stJShzw/c=
@@ -256,8 +257,9 @@ github.com/davidbyttow/govips/v2 v2.16.0/go.mod h1:clH5/IDVmG5eVyc23qYpyi7kmOT0B
github.com/deckarep/golang-set v1.8.0 h1:sk9/l/KqpunDwP7pSjUg0keiOOLEnOBHzykLrsPppp4=
github.com/deckarep/golang-set v1.8.0/go.mod h1:5nI87KwE7wgsBU1F4GKAw2Qod7p5kyS383rP6+o6qqo=
github.com/deepmap/oapi-codegen v1.3.11/go.mod h1:suMvK7+rKlx3+tpa8ByptmvoXbAV70wERKTOGH3hLp0=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe7Z5G/twsBW0KEalRQXZzf8ufSh9I=
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE=
github.com/desertbit/timer v1.0.1 h1:yRpYNn5Vaaj6QXecdLMPMJsW81JLiI1eokUft5nBmeo=
github.com/desertbit/timer v1.0.1/go.mod h1:htRrYeY5V/t4iu1xCJ5XsQvp4xve8QulXXctAzxqcwE=
github.com/dgraph-io/badger/v4 v4.7.0 h1:Q+J8HApYAY7UMpL8d9owqiB+odzEc0zn/aqOD9jhc6Y=
github.com/dgraph-io/badger/v4 v4.7.0/go.mod h1:He7TzG3YBy3j4f5baj5B7Zl2XyfNe5bl4Udl0aPemVA=
github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE=
@@ -303,8 +305,8 @@ github.com/evanphx/json-patch/v5 v5.5.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2Vvl
github.com/exoscale/egoscale v0.46.0/go.mod h1:mpEXBpROAa/2i5GC0r33rfxG+TxSEka11g1PIXt9+zc=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk=
github.com/fatih/color v1.14.1 h1:qfhVLaG5s+nCROl1zJsZRxFeYrHLqWroPOQ8BWiNb4w=
github.com/fatih/color v1.14.1/go.mod h1:2oHN61fhTpgcxD3TSWCgKDiH1+x4OiDVVGH8WlgGZGg=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
@@ -315,10 +317,10 @@ github.com/foxcpp/go-mockdns v1.1.0 h1:jI0rD8M0wuYAxL7r/ynTrCQQq0BVqfB99Vgk7Dlme
github.com/foxcpp/go-mockdns v1.1.0/go.mod h1:IhLeSFGed3mJIAXPH2aiRQB+kqz7oqu8ld2qVbOu7Wk=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/8M=
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
github.com/gdexlab/go-render v1.0.1 h1:rxqB3vo5s4n1kF0ySmoNeSPRYkEsyHgln4jFIQY7v0U=
github.com/gdexlab/go-render v1.0.1/go.mod h1:wRi5nW2qfjiGj4mPukH4UV0IknS1cHD4VgFTmJX5JzM=
github.com/getkin/kin-openapi v0.13.0/go.mod h1:WGRs2ZMM1Q8LR1QBEwUxC6RJEfaBcD0s+pcEVXFuAjw=
@@ -375,8 +377,8 @@ github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG
github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-micro/plugins/v4/client/grpc v1.2.1 h1:7xAwZRCO6mdUtBHsYIQs1/eCTdhCrnjF70GB+AVd6L0=
@@ -409,8 +411,8 @@ github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/o
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.26.0 h1:SP05Nqhjcvz81uJaRfEV0YBSSSGMc/iMaVtFbr3Sw2k=
github.com/go-playground/validator/v10 v10.26.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4=
github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo=
github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC0oI=
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-resty/resty/v2 v2.1.1-0.20191201195748-d7b97669fe48/go.mod h1:dZGr0i9PLlaaTD4H/hoZIDjQ+r6xq8mgbRzHZf7f2J8=
@@ -435,10 +437,10 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=
github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=
github.com/gobwas/ws v1.2.1 h1:F2aeBZrm2NDsc7vbovKrWSogd4wvfAxg0FQ89/iqOTk=
github.com/gobwas/ws v1.2.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.11.2 h1:joq77SxuyIs9zzxEjgyLBugMQ9NEgTWxXfz2wVqwAaQ=
github.com/goccy/go-yaml v1.11.2/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.12.0 h1:/1WHjnMsI1dlIBQutrvSMGZRQufVO3asrHfTwfACoPM=
github.com/goccy/go-yaml v1.12.0/go.mod h1:wKnAMd44+9JAAnGQpWVEgBzGt3YuTaQ4uXoHvE4m7WU=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
@@ -524,8 +526,8 @@ github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/go-tika v0.3.1 h1:l+jr10hDhZjcgxFRfcQChRLo1bPXQeLFluMyvDhXTTA=
github.com/google/go-tika v0.3.1/go.mod h1:DJh5N8qxXIl85QkqmXknd+PeeRkUOTbvwyYf7ieDz6c=
github.com/google/go-tpm v0.9.3 h1:+yx0/anQuGzi+ssRqeD6WpXjW2L/V0dItUayO0i9sRc=
github.com/google/go-tpm v0.9.3/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/go-tpm v0.9.5 h1:ocUmnDebX54dnW+MQWGQRbdaAcJELsa6PqZhJ48KwVU=
github.com/google/go-tpm v0.9.5/go.mod h1:h9jEsEECg7gtLis0upRBQU+GhYVH6jMjrFxI8u6bVUY=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -547,12 +549,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
github.com/gookit/config/v2 v2.2.5 h1:RECbYYbtherywmzn3LNeu9NA5ZqhD7MSKEMsJ7l+MpU=
github.com/gookit/config/v2 v2.2.5/go.mod h1:NeX+yiNYn6Ei10eJvCQFXuHEPIE/IPS8bqaFIsszzaM=
github.com/gookit/goutil v0.6.15 h1:mMQ0ElojNZoyPD0eVROk5QXJPh2uKR4g06slgPDF5Jo=
github.com/gookit/goutil v0.6.15/go.mod h1:qdKdYEHQdEtyH+4fNdQNZfJHhI0jUZzHxQVAV3DaMDY=
github.com/gookit/config/v2 v2.2.6 h1:8ZbkSr3gnFg1En8za9X3vldnZca3y3C7kaBLGsdLghE=
github.com/gookit/config/v2 v2.2.6/go.mod h1:++APDf3Ebj6mjzW1ALkegvg1evQKyx4FpuQqQZ2s2WM=
github.com/gookit/goutil v0.7.0 h1:HD4PUDW2LOSKIEBJPFD8PzNGLsL46ztpfXWVU+WtAxk=
github.com/gookit/goutil v0.7.0/go.mod h1:vJS9HXctYTCLtCsZot5L5xF+O1oR17cDYO9R0HxBmnU=
github.com/gookit/ini/v2 v2.2.3 h1:nSbN+x9OfQPcMObTFP+XuHt8ev6ndv/fWWqxFhPMu2E=
github.com/gookit/ini/v2 v2.2.3/go.mod h1:Vu6p7P7xcfmb8KYu3L0ek8bqu/Im63N81q208SCCZY4=
github.com/gophercloud/gophercloud v0.15.1-0.20210202035223-633d73521055/go.mod h1:wRtmUelyIIv3CSSDI47aUwbs075O6i+LY+pXsKCBsb4=
@@ -575,8 +575,8 @@ github.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vb
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
github.com/grpc-ecosystem/grpc-gateway v1.8.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1 h1:e9Rjr40Z98/clHv5Yg79Is0NtosR5LXRvdr7o/6NwbA=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.1/go.mod h1:tIxuGz/9mpox++sgp9fJjHO0+q1X9/UOWd798aAm22M=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
github.com/h2non/parth v0.0.0-20190131123155-b4df798d6542/go.mod h1:Ow0tF8D4Kplbc8s8sSb3V2oUCygFHVp8gC3Dn6U4MNI=
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
@@ -588,8 +588,8 @@ github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVH
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
github.com/hashicorp/go-plugin v1.6.2 h1:zdGAEd0V1lCaU0u+MxWQhtSDQmahpkwOun8U8EiRVog=
github.com/hashicorp/go-plugin v1.6.2/go.mod h1:CkgLQ5CZqNmdL9U9JzM532t8ZiYQ35+pj3b1FD37R0Q=
github.com/hashicorp/go-plugin v1.6.3 h1:xgHB+ZUSYeuJi96WtxEjzi23uh7YQpznjGh0U0UUrwg=
github.com/hashicorp/go-plugin v1.6.3/go.mod h1:MRobyh+Wc/nYy1V4KAXUiYfzxoYhs7V1mlH1Z7iY2h0=
github.com/hashicorp/go-retryablehttp v0.6.6/go.mod h1:vAew36LZh98gCBJNLH42IQ1ER/9wtLZZ8meHqQvEYWY=
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
@@ -641,8 +641,8 @@ github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZ
github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc=
github.com/jellydator/ttlcache/v2 v2.11.1 h1:AZGME43Eh2Vv3giG6GeqeLeFXxwxn1/qHItqWZl6U64=
github.com/jellydator/ttlcache/v2 v2.11.1/go.mod h1:RtE5Snf0/57e+2cLWFYWCCsLas2Hy3c5Z4n14XmSvTI=
github.com/jellydator/ttlcache/v3 v3.3.0 h1:BdoC9cE81qXfrxeb9eoJi9dWrdhSuwXMAnHTbnBm4Wc=
github.com/jellydator/ttlcache/v3 v3.3.0/go.mod h1:bj2/e0l4jRnQdrnSTaGTsh4GSXvMjQcy41i7th0GVGw=
github.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY=
github.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
github.com/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c=
github.com/jhump/protoreflect v1.15.1/go.mod h1:jD/2GMKKE6OqX8qTjhADU1e6DShO+gavG9e0Q693nKo=
@@ -654,8 +654,8 @@ github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHW
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ=
github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8=
github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I=
github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@@ -697,14 +697,12 @@ github.com/kolo/xmlrpc v0.0.0-20200310150728-e0350524596b/go.mod h1:o03bZfuBwAXH
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kovidgoyal/imaging v1.6.3 h1:iNPpv7ygiaB/NOztc6APMT7yr9UwBS+rOZwIbAdtyY8=
github.com/kovidgoyal/imaging v1.6.3/go.mod h1:sHvcLOOVhJuto2IoNdPLEqnAUoL5ZfHEF0PpNH+882g=
github.com/kovidgoyal/imaging v1.6.4 h1:K0idhRPXnRrJBKnBYcTfI1HTWSNDeAn7hYDvf9I0dCk=
github.com/kovidgoyal/imaging v1.6.4/go.mod h1:bEIgsaZmXlvFfkv/CUxr9rJook6AQkJnpB5EPosRfRY=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
@@ -719,12 +717,12 @@ github.com/labstack/echo/v4 v4.1.11/go.mod h1:i541M3Fj6f76NZtHSj7TXnyM8n2gaodfvf
github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/leonelquinteros/gotext v1.7.1 h1:/JNPeE3lY5JeVYv2+KBpz39994W3W9fmZCGq3eO9Ri8=
github.com/leonelquinteros/gotext v1.7.1/go.mod h1:I0WoFDn9u2D3VbPnnDPT8mzZu0iSXG8iih+AH2fHHqg=
github.com/leonelquinteros/gotext v1.7.2 h1:bDPndU8nt+/kRo1m4l/1OXiiy2v7Z7dfPQ9+YP7G1Mc=
github.com/leonelquinteros/gotext v1.7.2/go.mod h1:9/haCkm5P7Jay1sxKDGJ5WIg4zkz8oZKw4ekNpALob8=
github.com/libregraph/idm v0.5.0 h1:tDMwKbAOZzdeDYMxVlY5PbSqRKO7dbAW9KT42A51WSk=
github.com/libregraph/idm v0.5.0/go.mod h1:BGMwIQ/6orJSPVzJ1x6kgG2JyG9GY05YFmbsnaD80k0=
github.com/libregraph/lico v0.65.0 h1:h8GYJhTOBuNvxQzdViHdWkNSUp+YbPB1H18Bfn/zdlY=
github.com/libregraph/lico v0.65.0/go.mod h1:6w+kgoTYiXpJ7VriAaKJfeyF0eV/Stapd9pnK64du84=
github.com/libregraph/lico v0.66.0 h1:7T6fD1YF0Ep9n0g4KN6dvWHTlDC3awrQpgsP5GdYCF4=
github.com/libregraph/lico v0.66.0/go.mod h1:QI7NfmAkAWQ2y97iVfLv10S8tcvPQjc630uyfHGjIOw=
github.com/libregraph/oidc-go v1.1.0 h1:RyudjL3UyQblqeBQI06W53PniWobqODeeyAy6v/HumA=
github.com/libregraph/oidc-go v1.1.0/go.mod h1:qW9ubcXvZrfbbWZBaLMuk7bt5qAUMYyt9/NtXQt07Cw=
github.com/linode/linodego v0.25.3/go.mod h1:GSBKPpjoQfxEfryoCRcgkuUOCuVtGHWhzI8OMdycNTE=
@@ -748,8 +746,9 @@ github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2yME+cCiQ=
@@ -763,8 +762,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.6/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.24 h1:tpSp2G2KyMnnQu99ngJ47EIkWVmliIizyZBfPrBWDRM=
github.com/mattn/go-sqlite3 v1.14.24/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/mattn/go-tty v0.0.0-20180219170247-931426f7535a/go.mod h1:XPvLUNfbS4fJH25nqRHfWLMa1ONC8Amw+mIA639KxkE=
@@ -822,10 +821,10 @@ github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRW
github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
github.com/nats-io/jwt/v2 v2.7.4 h1:jXFuDDxs/GQjGDZGhNgH4tXzSUK6WQi2rsj4xmsNOtI=
github.com/nats-io/jwt/v2 v2.7.4/go.mod h1:me11pOkwObtcBNR8AiMrUbtVOUGkqYjMQZ6jnSdVUIA=
github.com/nats-io/nats-server/v2 v2.11.3 h1:AbGtXxuwjo0gBroLGGr/dE0vf24kTKdRnBq/3z/Fdoc=
github.com/nats-io/nats-server/v2 v2.11.3/go.mod h1:6Z6Fd+JgckqzKig7DYwhgrE7bJ6fypPHnGPND+DqgMY=
github.com/nats-io/nats.go v1.41.2 h1:5UkfLAtu/036s99AhFRlyNDI1Ieylb36qbGjJzHixos=
github.com/nats-io/nats.go v1.41.2/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/nats-server/v2 v2.11.6 h1:4VXRjbTUFKEB+7UoaKL3F5Y83xC7MxPoIONOnGgpkHw=
github.com/nats-io/nats-server/v2 v2.11.6/go.mod h1:2xoztlcb4lDL5Blh1/BiukkKELXvKQ5Vy29FPVRBUYs=
github.com/nats-io/nats.go v1.43.0 h1:uRFZ2FEoRvP64+UUhaTokyS18XBCR/xM2vQZKO4i8ug=
github.com/nats-io/nats.go v1.43.0/go.mod h1:iRWIPokVIFbVijxuMQq4y9ttaBTMe0SFdlZfMDd+33g=
github.com/nats-io/nkeys v0.4.11 h1:q44qGV008kYd9W1b1nEBkNzvnWxtRSQ7A8BoqRrcfa0=
github.com/nats-io/nkeys v0.4.11/go.mod h1:szDimtgmfOi9n25JpfIdGw12tZFYXqhGxjhVxsatHVE=
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
@@ -858,8 +857,8 @@ github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7J
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
github.com/open-policy-agent/opa v1.4.2 h1:ag4upP7zMsa4WE2p1pwAFeG4Pn3mNwfAx9DLhhJfbjU=
github.com/open-policy-agent/opa v1.4.2/go.mod h1:DNzZPKqKh4U0n0ANxcCVlw8lCSv2c+h5G/3QvSYdWZ8=
github.com/open-policy-agent/opa v1.6.0 h1:/S/cnNQJ2MUMNzizHPbisTWBHowmLkPrugY5jjkPlRQ=
github.com/open-policy-agent/opa v1.6.0/go.mod h1:zFmw4P+W62+CWGYRDDswfVYSCnPo6oYaktQnfIaRFC4=
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
@@ -890,15 +889,14 @@ github.com/pierrec/lz4/v4 v4.1.15 h1:MO0/ucJhngq7299dKLwIMtgTfbkoSPF6AoMYDd8Q4q0
github.com/pierrec/lz4/v4 v4.1.15/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
github.com/pkg/term v1.1.0/go.mod h1:E25nymQcrSllhX42Ok8MRm1+hyBdHY0dCeiKZ9jpNGw=
github.com/pkg/xattr v0.4.10 h1:Qe0mtiNFHQZ296vRgUjRCoPHPqH7VdTOrZx3g0T+pGA=
github.com/pkg/xattr v0.4.10/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/pkg/xattr v0.4.12 h1:rRTkSyFNTRElv6pkA3zpjHpQ90p/OdHQC1GmGh1aTjM=
github.com/pkg/xattr v0.4.12/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -921,16 +919,16 @@ github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqr
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
github.com/prometheus/client_golang v1.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk=
github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg=
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
github.com/prometheus/client_model v0.0.0-20170216185247-6f3806018612/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190115171406-56726106282f/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
github.com/prometheus/common v0.0.0-20170706130215-fb369f752a7f/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
github.com/prometheus/common v0.2.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
@@ -942,8 +940,8 @@ github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
github.com/prometheus/common v0.35.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
github.com/prometheus/common v0.65.0 h1:QDwzd+G1twt//Kwj/Ww6E9FQq1iVMmODnILtW1t2VzE=
github.com/prometheus/common v0.65.0/go.mod h1:0gZns+BLRQ3V6NdaerOhMbwwRbNh9hkGINtQAsP5GS8=
github.com/prometheus/procfs v0.0.0-20170703101242-e645f4e5aaa8/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
@@ -954,8 +952,8 @@ github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4O
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/prometheus/procfs v0.17.0 h1:FuLQ+05u4ZI+SS/w9+BWEM2TXiHKsUQ9TADiRH7DuK0=
github.com/prometheus/procfs v0.17.0/go.mod h1:oPQLaDAMRbA+u8H5Pbfq+dl3VDAvHxMUOVhe0wYB2zw=
github.com/prometheus/statsd_exporter v0.22.7/go.mod h1:N/TevpjkIh9ccs6nuzY3jQn9dFqnUakOjnEuMPJJJnI=
github.com/prometheus/statsd_exporter v0.22.8 h1:Qo2D9ZzaQG+id9i5NYNGmbf1aa/KxKbB9aKfMS+Yib0=
github.com/prometheus/statsd_exporter v0.22.8/go.mod h1:/DzwbTEaFTE0Ojz5PqcSk6+PFHOPWGxdXVr6yC8eFOM=
@@ -964,17 +962,15 @@ github.com/r3labs/sse/v2 v2.10.0 h1:hFEkLLFY4LDifoHdiCN/LlGBAdVJYsANaLqNYa1l/v0=
github.com/r3labs/sse/v2 v2.10.0/go.mod h1:Igau6Whc+F17QUgML1fYe1VPZzTV6EMCnYktEmkNJ7I=
github.com/rainycape/memcache v0.0.0-20150622160815-1031fa0ce2f2/go.mod h1:7tZKcyumwBO6qip7RNQ5r77yrssm9bfCowcLEBcU5IA=
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/riandyrn/otelchi v0.11.0 h1:x9MFoTgHcwCC2DdWkTEEZ2ZQFkbl6z7GXLQtTANN6Gk=
github.com/riandyrn/otelchi v0.11.0/go.mod h1:FlBYmG9fBQu0jFRvZZrATP4mDvLX2H5gwELfpZvNlxY=
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9 h1:bsUq1dX0N8AOIL7EB/X911+m4EHsnWEHeJ0c+3TTBrg=
github.com/rcrowley/go-metrics v0.0.0-20250401214520-65e299d6c5c9/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
github.com/riandyrn/otelchi v0.12.1 h1:FdRKK3/RgZ/T+d+qTH5Uw3MFx0KwRF38SkdfTMMq/m8=
github.com/riandyrn/otelchi v0.12.1/go.mod h1:weZZeUJURvtCcbWsdb7Y6F8KFZGedJlSrgUjq9VirV8=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
@@ -983,8 +979,8 @@ github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
github.com/russellhaering/goxmldsig v1.4.0 h1:8UcDh/xGyQiyrW+Fq5t8f+l2DLB1+zlhYzkPUJ7Qhys=
github.com/russellhaering/goxmldsig v1.4.0/go.mod h1:gM4MDENBQf7M+V824SGfyIUVFWydB7n0KkEubVJl+Tw=
github.com/russellhaering/goxmldsig v1.5.0 h1:AU2UkkYIUOTyZRbe08XMThaOCelArgvNfYapcmSjBNw=
github.com/russellhaering/goxmldsig v1.5.0/go.mod h1:x98CjQNFJcWfMxeOrMnMKg70lvDP6tE0nTaeUnjXDmk=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -998,12 +994,12 @@ github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
github.com/sercand/kuberesolver/v5 v5.1.1 h1:CYH+d67G0sGBj7q5wLK61yzqJJ8gLLC8aeprPTHb6yY=
github.com/sercand/kuberesolver/v5 v5.1.1/go.mod h1:Fs1KbKhVRnB2aDWN12NjKCB+RgYMWZJ294T3BtmVCpQ=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN3Uc8sB6B/s6Z4t2xvBgU1htSHuq8=
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=
github.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
github.com/sethvargo/go-password v0.3.1 h1:WqrLTjo7X6AcVYfC6R7GtSyuUQR9hGyAj/f1PYQZCJU=
github.com/sethvargo/go-password v0.3.1/go.mod h1:rXofC1zT54N7R8K/h1WDUdkf9BOx5OptoxrMBcrXzvs=
github.com/shamaton/msgpack/v2 v2.2.2 h1:GOIg0c9LV04VwzOOqZSrmsv/JzjNOOMxnS/HvOHGdgs=
github.com/shamaton/msgpack/v2 v2.2.2/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI=
github.com/shamaton/msgpack/v2 v2.2.3 h1:uDOHmxQySlvlUYfQwdjxyybAOzjlQsD1Vjy+4jmO9NM=
github.com/shamaton/msgpack/v2 v2.2.3/go.mod h1:6khjYnkx73f7VQU7wjcFS9DFjs+59naVWJv1TB7qdOI=
github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKlUeu/erjjvaPEYiI=
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shurcooL/httpfs v0.0.0-20190707220628-8d4bc4ba7749 h1:bUGsEnyNbVPw06Bs80sCeARAlK8lhwqGyi6UT8ymuGk=
@@ -1032,8 +1028,8 @@ github.com/spacewander/go-suffix-tree v0.0.0-20191010040751-0865e368c784/go.mod
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
github.com/spf13/afero v1.4.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I=
github.com/spf13/afero v1.12.0 h1:UcOPyRBYczmFn6yvphxkn9ZEOY65cpwGKb5mL36mrqs=
github.com/spf13/afero v1.12.0/go.mod h1:ZTlWwG4/ahT8W7T0WQ5uYmjI9duaLQGy3Q2OAl4sk/4=
github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA=
github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo=
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
@@ -1068,8 +1064,8 @@ github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOf
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stvp/go-udp-testing v0.0.0-20201019212854-469649b16807/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/tchap/go-patricia/v2 v2.3.2 h1:xTHFutuitO2zqKAQ5rCROYgUb7Or/+IC3fts9/Yc7nM=
github.com/tchap/go-patricia/v2 v2.3.2/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/tchap/go-patricia/v2 v2.3.3 h1:xfNEsODumaEcCcY3gI0hYPZ/PcpVv5ju6RMAhgwZDDc=
github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k=
github.com/test-go/testify v1.1.4 h1:Tf9lntrKUMHiXQ07qBScBTSA0dhYQlu83hswqelv1iE=
github.com/test-go/testify v1.1.4/go.mod h1:rH7cfJo/47vWGdi4GPj16x3/t1xGOj2YxzmNQzk2ghU=
github.com/thanhpk/randstr v1.0.6 h1:psAOktJFD4vV9NEVb3qkhRSMvYh4ORRaj1+w/hn4B+o=
@@ -1093,16 +1089,18 @@ github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208/go.mod h1:BzWtXXrXz
github.com/transip/gotransip/v6 v6.2.0/go.mod h1:pQZ36hWWRahCUXkFWlx9Hs711gLd8J4qdgLdRzmtY+g=
github.com/trustelem/zxcvbn v1.0.1 h1:mp4JFtzdDYGj9WYSD3KQSkwwUumWNFzXaAjckaTYpsc=
github.com/trustelem/zxcvbn v1.0.1/go.mod h1:zonUyKeh7sw6psPf/e3DtRqkRyZvAbOfjNz/aO7YQ5s=
github.com/tus/tusd/v2 v2.6.0 h1:Je243QDKnFTvm/WkLH2bd1oQ+7trolrflRWyuI0PdWI=
github.com/tus/tusd/v2 v2.6.0/go.mod h1:1Eb1lBoSRBfYJ/mQfFVjyw8ZdNMdBqW17vgQKl3Ah9g=
github.com/tus/tusd/v2 v2.8.0 h1:X2jGxQ05jAW4inDd2ogmOKqwnb4c/D0lw2yhgHayWyU=
github.com/tus/tusd/v2 v2.8.0/go.mod h1:3/zEOVQQIwmJhvNam8phV4x/UQt68ZmZiTzeuJUNhVo=
github.com/uber-go/atomic v1.3.2/go.mod h1:/Ct5t2lcmbJ4OSe/waGBoaVvVqtO0bmtfVNex1PFV8g=
github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/urfave/cli/v2 v2.27.5 h1:WoHEJLdsXr6dDWoJgMq/CboDmyY/8HMMH1fTECbih+w=
github.com/urfave/cli/v2 v2.27.5/go.mod h1:3Sevf16NykTbInEnD0yKkjDAeZDS0A6bzhBH5hrMvTQ=
github.com/urfave/cli/v2 v2.27.7 h1:bH59vdhbjLv3LAvIu6gd0usJHgoTTPhCFib8qqOwXYU=
github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/valyala/fasttemplate v1.1.0/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPUpymEIMZ47gx8=
github.com/vektah/gqlparser/v2 v2.5.30 h1:EqLwGAFLIzt1wpx1IPpY67DwUujF1OfzgEyDsLrN6kE=
github.com/vektah/gqlparser/v2 v2.5.30/go.mod h1:D1/VCZtV3LPnQrcPBeR/q5jkSQIPti0uYCP/RI0gIeo=
github.com/vinyldns/go-vinyldns v0.0.0-20200917153823-148a5f6b8f14/go.mod h1:RWc47jtnVuQv6+lY3c768WtXCas/Xi+U5UFc5xULmYg=
github.com/vultr/govultr/v2 v2.0.0/go.mod h1:2PsEeg+gs3p/Fo5Pw8F9mv+DUBEOlrNZ8GmCTGmhOhs=
github.com/wk8/go-ordered-map v1.0.0 h1:BV7z+2PaK8LTSd/mWgY12HyMAo5CEgkHqbkVq2thqr8=
@@ -1124,8 +1122,6 @@ github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQ
github.com/xhit/go-simple-mail/v2 v2.16.0 h1:ouGy/Ww4kuaqu2E2UrDw7SvLaziWTB60ICLkIkNVccA=
github.com/xhit/go-simple-mail/v2 v2.16.0/go.mod h1:b7P5ygho6SYE+VIqpxA6QkYfv4teeyG4MKqB3utRu98=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1 h1:gEOO8jv9F4OT7lGCjxCBTO/36wtF6j2nSip77qHd4x4=
github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM=
github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg=
@@ -1138,8 +1134,8 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.etcd.io/bbolt v1.4.2 h1:IrUHp260R8c+zYx/Tm8QZr04CX+qWS5PGfPdevhdm1I=
go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM=
go.etcd.io/etcd/api/v3 v3.5.16 h1:WvmyJVbjWqK4R1E+B12RRHz3bRGy9XVfh++MgbN+6n0=
go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28=
go.etcd.io/etcd/client/pkg/v3 v3.5.16 h1:ZgY48uH6UvB+/7R9Yf4x574uCO3jIx0TRDyetSfId3Q=
@@ -1158,32 +1154,32 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ=
go.opentelemetry.io/contrib/zpages v0.60.0 h1:wOM9ie1Hz4H88L9KE6GrGbKJhfm+8F1NfW/Y3q9Xt+8=
go.opentelemetry.io/contrib/zpages v0.60.0/go.mod h1:xqfToSRGh2MYUsfyErNz8jnNDPlnpZqWM/y6Z2Cx7xw=
go.opentelemetry.io/otel v1.36.0 h1:UumtzIklRBY6cI/lllNZlALOF5nNIzJVb16APdvgTXg=
go.opentelemetry.io/otel v1.36.0/go.mod h1:/TcFMXYjyRNh8khOAO9ybYkqaDBb/70aVwkNML4pP8E=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0 h1:rbRJ8BBoVMsQShESYZ0FkvcITu8X8QNwJogcLUmDNNw=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.62.0/go.mod h1:ru6KHrNtNHxM4nD/vd6QrLVWgKhxPYgblq4VAtNawTQ=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=
go.opentelemetry.io/contrib/zpages v0.62.0 h1:9fUYTLmrK0x/lweM2uM+BOx069jLx8PxVqWhegGJ9Bo=
go.opentelemetry.io/contrib/zpages v0.62.0/go.mod h1:C8kXoiC1Ytvereztus2R+kqdSa6W/MZ8FfS8Zwj+LiM=
go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ=
go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0 h1:D7UpUy2Xc2wsi1Ras6V40q806WM07rqoCWzXu7Sqy+4=
go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0 h1:xJ2qHD0C1BeYVTLLR9sX12+Qb95kfeD/byKj6Ky1pXg=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.35.0/go.mod h1:u5BF1xyjstDowA1R5QAO9JHzqK+ublenEW/dyqTjBVk=
go.opentelemetry.io/otel/metric v1.36.0 h1:MoWPKVhQvJ+eeXWHFBOPoBOi20jh6Iq2CcCREuTYufE=
go.opentelemetry.io/otel/metric v1.36.0/go.mod h1:zC7Ks+yeyJt4xig9DEw9kuUFe5C3zLbVjV2PzT6qzbs=
go.opentelemetry.io/otel/sdk v1.36.0 h1:b6SYIuLRs88ztox4EyrvRti80uXIFy+Sqzoh9kFULbs=
go.opentelemetry.io/otel/sdk v1.36.0/go.mod h1:+lC+mTgD+MUWfjJubi2vvXWcVxyr9rmlshZni72pXeY=
go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o=
go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w=
go.opentelemetry.io/otel/trace v1.36.0 h1:ahxWNuqZjpdiFAyrIoQ4GIiAIhxAunQR6MUoKrsNd4w=
go.opentelemetry.io/otel/trace v1.36.0/go.mod h1:gQ+OnDZzrybY4k4seLzPAWNwVBBVlF2szhehOBB/tGA=
go.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4=
go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 h1:EtFWSnwW9hGObjkIdmlnWSydO+Qs8OwzfzXLUPg4xOc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0/go.mod h1:QjUEoiGCPkvFZ/MjK6ZZfNOS6mfVEVKYE99dFhuN2LI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE=
go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E=
go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI=
go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4=
go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0=
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -1196,13 +1192,17 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.9.0 h1:7fIwc/ZtS0q++VgcfqFDxSBZVv/Xo49/SYnDFupUwlI=
go.uber.org/multierr v1.9.0/go.mod h1:X2jQV1h+kxSjClGpnseKVIxpmcjrj7MNnI0bnlfKTVQ=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/ratelimit v0.0.0-20180316092928-c15da0234277/go.mod h1:2X8KaoNd1J0lZV+PxJk/5+DGbO/tpwLR1m++a7FnB/Y=
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI=
go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY=
go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY=
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
golang.org/x/crypto v0.0.0-20180621125126-a49355c7e3f8/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
@@ -1224,8 +1224,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.38.0 h1:jt+WWG8IZlBnVbomuhg2Mdq0+BBQaHbtqHEFEigjUV8=
golang.org/x/crypto v0.38.0/go.mod h1:MvrbAqul58NNYPKnOra203SB9vpuZW0e+RRZV+Ggqjw=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1236,13 +1236,13 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.18.0/go.mod h1:4yyo5vMFQjVjUcVk4jEQcU9MGy/rulF5WvUILseCM2E=
golang.org/x/image v0.27.0 h1:C8gA4oWU/tKkdCfYT6T2u4faJu3MeNS5O8UPWlPF61w=
golang.org/x/image v0.27.0/go.mod h1:xbdrClrAUway1MUTEZDq9mz/UpRwYAkFFNUslZtcB+g=
golang.org/x/image v0.28.0 h1:gdem5JW1OLS4FbkWgLO+7ZeFzYtL3xClb97GaUzYMFE=
golang.org/x/image v0.28.0/go.mod h1:GUJYXtnGKEUgggyzh+Vxt+AviiCcyiwpsl8iQ8MvwGY=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1267,8 +1267,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1323,8 +1323,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.40.0 h1:79Xs7wF06Gbdcg4kdCCIQArK11Z1hr5POQ6+fIYHNuY=
golang.org/x/net v0.40.0/go.mod h1:y0hY0exeL2Pku80/zKK7tpntoX23cqL3Oa6njdgRtds=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1332,8 +1332,8 @@ golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4Iltr
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc=
golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8=
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@@ -1351,8 +1351,8 @@ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.14.0 h1:woo0S4Yywslg6hp4eUFjTVOyKt0RookbpAHG4c1HmhQ=
golang.org/x/sync v0.14.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180622082034-63fc586f45fe/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1463,16 +1463,16 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.25.0 h1:qVyWApTSYLk/drJRO5mDlNYskwQznZmkpV2c8q9zls4=
golang.org/x/text v0.25.0/go.mod h1:WEdwpYrmk1qmdHvhkSTNPm3app7v4rsT8F2UD6+VHIA=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20201208040808-7e3f01d25324/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE=
golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
@@ -1527,8 +1527,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU=
golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1588,12 +1588,12 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a h1:nwKuGPlUAt+aR+pcrkfFRrTU1BVrSmYyYMxYbUIVHr0=
google.golang.org/genproto/googleapis/api v0.0.0-20250218202821-56aae31c358a/go.mod h1:3kWAYMk1I75K4vykHtKt2ycnOgpA6974V7bREqbsenU=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237 h1:cJfm9zPbe1e873mHJzmQ1nwVEeRDU/T1wXDK2kUSU34=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250519155744-55703ea1f237/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:sAo5UzpjUwgFBCzupwhcLcxHVDK7vG5IqI30YnwX2eE=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY=
google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -1609,8 +1609,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.72.1 h1:HR03wO6eyZ7lknl75XlxABNVLLFc2PAb6mHlYh756mA=
google.golang.org/grpc v1.72.1/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e h1:m7aQHHqd0q89mRwhwS9Bx2rjyl/hsFAeta+uGrHsQaU=
google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e/go.mod h1:gID3PKrg7pWKntu9Ss6zTLJ0ttC0X9IHgREOCZwbCVU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
@@ -1683,7 +1683,7 @@ honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
stash.kopano.io/kgol/rndm v1.1.2 h1:vriNehb5NuglfGqZPkgeFr2Y5AjXtQCF4vEl4kqc6nc=
stash.kopano.io/kgol/rndm v1.1.2/go.mod h1:CBvpAHlOwyu/XipxfLGk02UN3K3P6hQ8E2JoTbNWfJU=

7
vendor/dario.cat/mergo/FUNDING.json vendored Normal file
View File

@@ -0,0 +1,7 @@
{
"drips": {
"ethereum": {
"ownedBy": "0x6160020e7102237aC41bdb156e94401692D76930"
}
}
}

View File

@@ -85,7 +85,6 @@ Mergo is used by [thousands](https://deps.dev/go/dario.cat%2Fmergo/v1.0.0/depend
* [goreleaser/goreleaser](https://github.com/goreleaser/goreleaser)
* [go-micro/go-micro](https://github.com/go-micro/go-micro)
* [grafana/loki](https://github.com/grafana/loki)
* [kubernetes/kubernetes](https://github.com/kubernetes/kubernetes)
* [masterminds/sprig](github.com/Masterminds/sprig)
* [moby/moby](https://github.com/moby/moby)
* [slackhq/nebula](https://github.com/slackhq/nebula)
@@ -191,10 +190,6 @@ func main() {
}
```
Note: if test are failing due missing package, please execute:
go get gopkg.in/yaml.v3
### Transformers
Transformers allow to merge specific types differently than in the default behavior. In other words, now you can customize how some types are merged. For example, `time.Time` is a struct; it doesn't have zero value but IsZero can return true because it has fields with zero value. How can we merge a non-zero `time.Time`?

View File

@@ -4,8 +4,8 @@
| Version | Supported |
| ------- | ------------------ |
| 0.3.x | :white_check_mark: |
| < 0.3 | :x: |
| 1.x.x | :white_check_mark: |
| < 1.0 | :x: |
## Security contact information

View File

@@ -3,7 +3,7 @@ reflection interface similar to Go's standard library `json` and `xml` packages.
Compatible with TOML version [v1.0.0](https://toml.io/en/v1.0.0).
Documentation: https://godocs.io/github.com/BurntSushi/toml
Documentation: https://pkg.go.dev/github.com/BurntSushi/toml
See the [releases page](https://github.com/BurntSushi/toml/releases) for a
changelog; this information is also in the git tag annotations (e.g. `git show

View File

@@ -196,6 +196,19 @@ func (md *MetaData) PrimitiveDecode(primValue Primitive, v any) error {
return md.unify(primValue.undecoded, rvalue(v))
}
// markDecodedRecursive is a helper to mark any key under the given tmap as
// decoded, recursing as needed
func markDecodedRecursive(md *MetaData, tmap map[string]any) {
for key := range tmap {
md.decoded[md.context.add(key).String()] = struct{}{}
if tmap, ok := tmap[key].(map[string]any); ok {
md.context = append(md.context, key)
markDecodedRecursive(md, tmap)
md.context = md.context[0 : len(md.context)-1]
}
}
}
// unify performs a sort of type unification based on the structure of `rv`,
// which is the client representation.
//
@@ -222,6 +235,16 @@ func (md *MetaData) unify(data any, rv reflect.Value) error {
if err != nil {
return md.parseErr(err)
}
// Assume the Unmarshaler decoded everything, so mark all keys under
// this table as decoded.
if tmap, ok := data.(map[string]any); ok {
markDecodedRecursive(md, tmap)
}
if aot, ok := data.([]map[string]any); ok {
for _, tmap := range aot {
markDecodedRecursive(md, tmap)
}
}
return nil
}
if v, ok := rvi.(encoding.TextUnmarshaler); ok {
@@ -540,12 +563,14 @@ func (md *MetaData) badtype(dst string, data any) error {
func (md *MetaData) parseErr(err error) error {
k := md.context.String()
d := string(md.data)
return ParseError{
LastKey: k,
Position: md.keyInfo[k].pos,
Line: md.keyInfo[k].pos.Line,
Message: err.Error(),
err: err,
input: string(md.data),
LastKey: k,
Position: md.keyInfo[k].pos.withCol(d),
Line: md.keyInfo[k].pos.Line,
input: d,
}
}

View File

@@ -402,31 +402,30 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
// Sort keys so that we have deterministic output. And write keys directly
// underneath this key first, before writing sub-structs or sub-maps.
var mapKeysDirect, mapKeysSub []string
var mapKeysDirect, mapKeysSub []reflect.Value
for _, mapKey := range rv.MapKeys() {
k := mapKey.String()
if typeIsTable(tomlTypeOfGo(eindirect(rv.MapIndex(mapKey)))) {
mapKeysSub = append(mapKeysSub, k)
mapKeysSub = append(mapKeysSub, mapKey)
} else {
mapKeysDirect = append(mapKeysDirect, k)
mapKeysDirect = append(mapKeysDirect, mapKey)
}
}
var writeMapKeys = func(mapKeys []string, trailC bool) {
sort.Strings(mapKeys)
writeMapKeys := func(mapKeys []reflect.Value, trailC bool) {
sort.Slice(mapKeys, func(i, j int) bool { return mapKeys[i].String() < mapKeys[j].String() })
for i, mapKey := range mapKeys {
val := eindirect(rv.MapIndex(reflect.ValueOf(mapKey)))
val := eindirect(rv.MapIndex(mapKey))
if isNil(val) {
continue
}
if inline {
enc.writeKeyValue(Key{mapKey}, val, true)
enc.writeKeyValue(Key{mapKey.String()}, val, true)
if trailC || i != len(mapKeys)-1 {
enc.wf(", ")
}
} else {
enc.encode(key.add(mapKey), val)
enc.encode(key.add(mapKey.String()), val)
}
}
}
@@ -441,8 +440,6 @@ func (enc *Encoder) eMap(key Key, rv reflect.Value, inline bool) {
}
}
const is32Bit = (32 << (^uint(0) >> 63)) == 32
func pointerTo(t reflect.Type) reflect.Type {
if t.Kind() == reflect.Ptr {
return pointerTo(t.Elem())
@@ -477,15 +474,14 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
frv := eindirect(rv.Field(i))
if is32Bit {
// Copy so it works correct on 32bit archs; not clear why this
// is needed. See #314, and https://www.reddit.com/r/golang/comments/pnx8v4
// This also works fine on 64bit, but 32bit archs are somewhat
// rare and this is a wee bit faster.
// Need to make a copy because ... ehm, I don't know why... I guess
// allocating a new array can cause it to fail(?)
//
// Done for: https://github.com/BurntSushi/toml/issues/430
// Previously only on 32bit for: https://github.com/BurntSushi/toml/issues/314
copyStart := make([]int, len(start))
copy(copyStart, start)
start = copyStart
}
// Treat anonymous struct fields with tag names as though they are
// not anonymous, like encoding/json does.
@@ -507,7 +503,7 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
}
addFields(rt, rv, nil)
writeFields := func(fields [][]int) {
writeFields := func(fields [][]int, totalFields int) {
for _, fieldIndex := range fields {
fieldType := rt.FieldByIndex(fieldIndex)
fieldVal := rv.FieldByIndex(fieldIndex)
@@ -537,7 +533,7 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
if inline {
enc.writeKeyValue(Key{keyName}, fieldVal, true)
if fieldIndex[0] != len(fields)-1 {
if fieldIndex[0] != totalFields-1 {
enc.wf(", ")
}
} else {
@@ -549,8 +545,10 @@ func (enc *Encoder) eStruct(key Key, rv reflect.Value, inline bool) {
if inline {
enc.wf("{")
}
writeFields(fieldsDirect)
writeFields(fieldsSub)
l := len(fieldsDirect) + len(fieldsSub)
writeFields(fieldsDirect, l)
writeFields(fieldsSub, l)
if inline {
enc.wf("}")
}

View File

@@ -67,21 +67,36 @@ type ParseError struct {
// Position of an error.
type Position struct {
Line int // Line number, starting at 1.
Col int // Error column, starting at 1.
Start int // Start of error, as byte offset starting at 0.
Len int // Lenght in bytes.
Len int // Length of the error in bytes.
}
func (p Position) withCol(tomlFile string) Position {
var (
pos int
lines = strings.Split(tomlFile, "\n")
)
for i := range lines {
ll := len(lines[i]) + 1 // +1 for the removed newline
if pos+ll >= p.Start {
p.Col = p.Start - pos + 1
if p.Col < 1 { // Should never happen, but just in case.
p.Col = 1
}
break
}
pos += ll
}
return p
}
func (pe ParseError) Error() string {
msg := pe.Message
if msg == "" { // Error from errorf()
msg = pe.err.Error()
}
if pe.LastKey == "" {
return fmt.Sprintf("toml: line %d: %s", pe.Position.Line, msg)
return fmt.Sprintf("toml: line %d: %s", pe.Position.Line, pe.Message)
}
return fmt.Sprintf("toml: line %d (last key %q): %s",
pe.Position.Line, pe.LastKey, msg)
pe.Position.Line, pe.LastKey, pe.Message)
}
// ErrorWithPosition returns the error with detailed location context.
@@ -92,26 +107,19 @@ func (pe ParseError) ErrorWithPosition() string {
return pe.Error()
}
var (
lines = strings.Split(pe.input, "\n")
col = pe.column(lines)
b = new(strings.Builder)
)
msg := pe.Message
if msg == "" {
msg = pe.err.Error()
}
// TODO: don't show control characters as literals? This may not show up
// well everywhere.
var (
lines = strings.Split(pe.input, "\n")
b = new(strings.Builder)
)
if pe.Position.Len == 1 {
fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d:\n\n",
msg, pe.Position.Line, col+1)
pe.Message, pe.Position.Line, pe.Position.Col)
} else {
fmt.Fprintf(b, "toml: error: %s\n\nAt line %d, column %d-%d:\n\n",
msg, pe.Position.Line, col, col+pe.Position.Len)
pe.Message, pe.Position.Line, pe.Position.Col, pe.Position.Col+pe.Position.Len-1)
}
if pe.Position.Line > 2 {
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line-2, expandTab(lines[pe.Position.Line-3]))
@@ -129,7 +137,7 @@ func (pe ParseError) ErrorWithPosition() string {
diff := len(expanded) - len(lines[pe.Position.Line-1])
fmt.Fprintf(b, "% 7d | %s\n", pe.Position.Line, expanded)
fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", col+diff), strings.Repeat("^", pe.Position.Len))
fmt.Fprintf(b, "% 10s%s%s\n", "", strings.Repeat(" ", pe.Position.Col-1+diff), strings.Repeat("^", pe.Position.Len))
return b.String()
}
@@ -151,23 +159,6 @@ func (pe ParseError) ErrorWithUsage() string {
return m
}
func (pe ParseError) column(lines []string) int {
var pos, col int
for i := range lines {
ll := len(lines[i]) + 1 // +1 for the removed newline
if pos+ll >= pe.Position.Start {
col = pe.Position.Start - pos
if col < 0 { // Should never happen, but just in case.
col = 0
}
break
}
pos += ll
}
return col
}
func expandTab(s string) string {
var (
b strings.Builder

View File

@@ -275,7 +275,9 @@ func (lx *lexer) errorPos(start, length int, err error) stateFn {
func (lx *lexer) errorf(format string, values ...any) stateFn {
if lx.atEOF {
pos := lx.getPos()
if lx.pos >= 1 && lx.input[lx.pos-1] == '\n' {
pos.Line--
}
pos.Len = 1
pos.Start = lx.pos - 1
lx.items <- item{typ: itemError, pos: pos, err: fmt.Errorf(format, values...)}
@@ -492,6 +494,9 @@ func lexKeyEnd(lx *lexer) stateFn {
lx.emit(itemKeyEnd)
return lexSkip(lx, lexValue)
default:
if r == '\n' {
return lx.errorPrevLine(fmt.Errorf("expected '.' or '=', but got %q instead", r))
}
return lx.errorf("expected '.' or '=', but got %q instead", r)
}
}
@@ -560,6 +565,9 @@ func lexValue(lx *lexer) stateFn {
if r == eof {
return lx.errorf("unexpected EOF; expected value")
}
if r == '\n' {
return lx.errorPrevLine(fmt.Errorf("expected value but found %q instead", r))
}
return lx.errorf("expected value but found %q instead", r)
}
@@ -1111,7 +1119,7 @@ func lexBaseNumberOrDate(lx *lexer) stateFn {
case 'x':
r = lx.peek()
if !isHex(r) {
lx.errorf("not a hexidecimal number: '%s%c'", lx.current(), r)
lx.errorf("not a hexadecimal number: '%s%c'", lx.current(), r)
}
return lexHexInteger
}
@@ -1259,23 +1267,6 @@ func isBinary(r rune) bool { return r == '0' || r == '1' }
func isOctal(r rune) bool { return r >= '0' && r <= '7' }
func isHex(r rune) bool { return (r >= '0' && r <= '9') || (r|0x20 >= 'a' && r|0x20 <= 'f') }
func isBareKeyChar(r rune, tomlNext bool) bool {
if tomlNext {
return (r >= 'A' && r <= 'Z') ||
(r >= 'a' && r <= 'z') ||
(r >= '0' && r <= '9') ||
r == '_' || r == '-' ||
r == 0xb2 || r == 0xb3 || r == 0xb9 || (r >= 0xbc && r <= 0xbe) ||
(r >= 0xc0 && r <= 0xd6) || (r >= 0xd8 && r <= 0xf6) || (r >= 0xf8 && r <= 0x037d) ||
(r >= 0x037f && r <= 0x1fff) ||
(r >= 0x200c && r <= 0x200d) || (r >= 0x203f && r <= 0x2040) ||
(r >= 0x2070 && r <= 0x218f) || (r >= 0x2460 && r <= 0x24ff) ||
(r >= 0x2c00 && r <= 0x2fef) || (r >= 0x3001 && r <= 0xd7ff) ||
(r >= 0xf900 && r <= 0xfdcf) || (r >= 0xfdf0 && r <= 0xfffd) ||
(r >= 0x10000 && r <= 0xeffff)
}
return (r >= 'A' && r <= 'Z') ||
(r >= 'a' && r <= 'z') ||
(r >= '0' && r <= '9') ||
r == '_' || r == '-'
return (r >= 'A' && r <= 'Z') || (r >= 'a' && r <= 'z') ||
(r >= '0' && r <= '9') || r == '_' || r == '-'
}

View File

@@ -135,9 +135,6 @@ func (k Key) maybeQuoted(i int) string {
// Like append(), but only increase the cap by 1.
func (k Key) add(piece string) Key {
if cap(k) > len(k) {
return append(k, piece)
}
newKey := make(Key, len(k)+1)
copy(newKey, k)
newKey[len(k)] = piece

View File

@@ -50,7 +50,6 @@ func parse(data string) (p *parser, err error) {
// it anyway.
if strings.HasPrefix(data, "\xff\xfe") || strings.HasPrefix(data, "\xfe\xff") { // UTF-16
data = data[2:]
//lint:ignore S1017 https://github.com/dominikh/go-tools/issues/1447
} else if strings.HasPrefix(data, "\xef\xbb\xbf") { // UTF-8
data = data[3:]
}
@@ -65,7 +64,7 @@ func parse(data string) (p *parser, err error) {
if i := strings.IndexRune(data[:ex], 0); i > -1 {
return nil, ParseError{
Message: "files cannot contain NULL bytes; probably using UTF-16; TOML files must be UTF-8",
Position: Position{Line: 1, Start: i, Len: 1},
Position: Position{Line: 1, Col: 1, Start: i, Len: 1},
Line: 1,
input: data,
}
@@ -92,8 +91,9 @@ func parse(data string) (p *parser, err error) {
func (p *parser) panicErr(it item, err error) {
panic(ParseError{
Message: err.Error(),
err: err,
Position: it.pos,
Position: it.pos.withCol(p.lx.input),
Line: it.pos.Len,
LastKey: p.current(),
})
@@ -102,7 +102,7 @@ func (p *parser) panicErr(it item, err error) {
func (p *parser) panicItemf(it item, format string, v ...any) {
panic(ParseError{
Message: fmt.Sprintf(format, v...),
Position: it.pos,
Position: it.pos.withCol(p.lx.input),
Line: it.pos.Len,
LastKey: p.current(),
})
@@ -111,7 +111,7 @@ func (p *parser) panicItemf(it item, format string, v ...any) {
func (p *parser) panicf(format string, v ...any) {
panic(ParseError{
Message: fmt.Sprintf(format, v...),
Position: p.pos,
Position: p.pos.withCol(p.lx.input),
Line: p.pos.Line,
LastKey: p.current(),
})
@@ -123,10 +123,11 @@ func (p *parser) next() item {
if it.typ == itemError {
if it.err != nil {
panic(ParseError{
Position: it.pos,
Message: it.err.Error(),
err: it.err,
Position: it.pos.withCol(p.lx.input),
Line: it.pos.Line,
LastKey: p.current(),
err: it.err,
})
}
@@ -527,7 +528,7 @@ func numUnderscoresOK(s string) bool {
}
}
// isHexis a superset of all the permissable characters surrounding an
// isHex is a superset of all the permissible characters surrounding an
// underscore.
accept = isHex(r)
}

View File

@@ -1,62 +0,0 @@
package backoff
import (
"context"
"time"
)
// BackOffContext is a backoff policy that stops retrying after the context
// is canceled.
type BackOffContext interface { // nolint: golint
BackOff
Context() context.Context
}
type backOffContext struct {
BackOff
ctx context.Context
}
// WithContext returns a BackOffContext with context ctx
//
// ctx must not be nil
func WithContext(b BackOff, ctx context.Context) BackOffContext { // nolint: golint
if ctx == nil {
panic("nil context")
}
if b, ok := b.(*backOffContext); ok {
return &backOffContext{
BackOff: b.BackOff,
ctx: ctx,
}
}
return &backOffContext{
BackOff: b,
ctx: ctx,
}
}
func getContext(b BackOff) context.Context {
if cb, ok := b.(BackOffContext); ok {
return cb.Context()
}
if tb, ok := b.(*backOffTries); ok {
return getContext(tb.delegate)
}
return context.Background()
}
func (b *backOffContext) Context() context.Context {
return b.ctx
}
func (b *backOffContext) NextBackOff() time.Duration {
select {
case <-b.ctx.Done():
return Stop
default:
return b.BackOff.NextBackOff()
}
}

View File

@@ -1,216 +0,0 @@
package backoff
import (
"math/rand"
"time"
)
/*
ExponentialBackOff is a backoff implementation that increases the backoff
period for each retry attempt using a randomization function that grows exponentially.
NextBackOff() is calculated using the following formula:
randomized interval =
RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
In other words NextBackOff() will range between the randomization factor
percentage below and above the retry interval.
For example, given the following parameters:
RetryInterval = 2
RandomizationFactor = 0.5
Multiplier = 2
the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
multiplied by the exponential, that is, between 2 and 6 seconds.
Note: MaxInterval caps the RetryInterval and not the randomized interval.
If the time elapsed since an ExponentialBackOff instance is created goes past the
MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
The elapsed time can be reset by calling Reset().
Example: Given the following default arguments, for 10 tries the sequence will be,
and assuming we go over the MaxElapsedTime on the 10th try:
Request # RetryInterval (seconds) Randomized Interval (seconds)
1 0.5 [0.25, 0.75]
2 0.75 [0.375, 1.125]
3 1.125 [0.562, 1.687]
4 1.687 [0.8435, 2.53]
5 2.53 [1.265, 3.795]
6 3.795 [1.897, 5.692]
7 5.692 [2.846, 8.538]
8 8.538 [4.269, 12.807]
9 12.807 [6.403, 19.210]
10 19.210 backoff.Stop
Note: Implementation is not thread-safe.
*/
type ExponentialBackOff struct {
InitialInterval time.Duration
RandomizationFactor float64
Multiplier float64
MaxInterval time.Duration
// After MaxElapsedTime the ExponentialBackOff returns Stop.
// It never stops if MaxElapsedTime == 0.
MaxElapsedTime time.Duration
Stop time.Duration
Clock Clock
currentInterval time.Duration
startTime time.Time
}
// Clock is an interface that returns current time for BackOff.
type Clock interface {
Now() time.Time
}
// ExponentialBackOffOpts is a function type used to configure ExponentialBackOff options.
type ExponentialBackOffOpts func(*ExponentialBackOff)
// Default values for ExponentialBackOff.
const (
DefaultInitialInterval = 500 * time.Millisecond
DefaultRandomizationFactor = 0.5
DefaultMultiplier = 1.5
DefaultMaxInterval = 60 * time.Second
DefaultMaxElapsedTime = 15 * time.Minute
)
// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
func NewExponentialBackOff(opts ...ExponentialBackOffOpts) *ExponentialBackOff {
b := &ExponentialBackOff{
InitialInterval: DefaultInitialInterval,
RandomizationFactor: DefaultRandomizationFactor,
Multiplier: DefaultMultiplier,
MaxInterval: DefaultMaxInterval,
MaxElapsedTime: DefaultMaxElapsedTime,
Stop: Stop,
Clock: SystemClock,
}
for _, fn := range opts {
fn(b)
}
b.Reset()
return b
}
// WithInitialInterval sets the initial interval between retries.
func WithInitialInterval(duration time.Duration) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.InitialInterval = duration
}
}
// WithRandomizationFactor sets the randomization factor to add jitter to intervals.
func WithRandomizationFactor(randomizationFactor float64) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.RandomizationFactor = randomizationFactor
}
}
// WithMultiplier sets the multiplier for increasing the interval after each retry.
func WithMultiplier(multiplier float64) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.Multiplier = multiplier
}
}
// WithMaxInterval sets the maximum interval between retries.
func WithMaxInterval(duration time.Duration) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.MaxInterval = duration
}
}
// WithMaxElapsedTime sets the maximum total time for retries.
func WithMaxElapsedTime(duration time.Duration) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.MaxElapsedTime = duration
}
}
// WithRetryStopDuration sets the duration after which retries should stop.
func WithRetryStopDuration(duration time.Duration) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.Stop = duration
}
}
// WithClockProvider sets the clock used to measure time.
func WithClockProvider(clock Clock) ExponentialBackOffOpts {
return func(ebo *ExponentialBackOff) {
ebo.Clock = clock
}
}
type systemClock struct{}
func (t systemClock) Now() time.Time {
return time.Now()
}
// SystemClock implements Clock interface that uses time.Now().
var SystemClock = systemClock{}
// Reset the interval back to the initial retry interval and restarts the timer.
// Reset must be called before using b.
func (b *ExponentialBackOff) Reset() {
b.currentInterval = b.InitialInterval
b.startTime = b.Clock.Now()
}
// NextBackOff calculates the next backoff interval using the formula:
// Randomized interval = RetryInterval * (1 ± RandomizationFactor)
func (b *ExponentialBackOff) NextBackOff() time.Duration {
// Make sure we have not gone over the maximum elapsed time.
elapsed := b.GetElapsedTime()
next := getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
b.incrementCurrentInterval()
if b.MaxElapsedTime != 0 && elapsed+next > b.MaxElapsedTime {
return b.Stop
}
return next
}
// GetElapsedTime returns the elapsed time since an ExponentialBackOff instance
// is created and is reset when Reset() is called.
//
// The elapsed time is computed using time.Now().UnixNano(). It is
// safe to call even while the backoff policy is used by a running
// ticker.
func (b *ExponentialBackOff) GetElapsedTime() time.Duration {
return b.Clock.Now().Sub(b.startTime)
}
// Increments the current interval by multiplying it with the multiplier.
func (b *ExponentialBackOff) incrementCurrentInterval() {
// Check for overflow, if overflow is detected set the current interval to the max interval.
if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
b.currentInterval = b.MaxInterval
} else {
b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
}
}
// Returns a random value from the following interval:
// [currentInterval - randomizationFactor * currentInterval, currentInterval + randomizationFactor * currentInterval].
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
if randomizationFactor == 0 {
return currentInterval // make sure no randomness is used when randomizationFactor is 0.
}
var delta = randomizationFactor * float64(currentInterval)
var minInterval = float64(currentInterval) - delta
var maxInterval = float64(currentInterval) + delta
// Get a random value from the range [minInterval, maxInterval].
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
// we want a 33% chance for selecting either 1, 2 or 3.
return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
}

View File

@@ -1,146 +0,0 @@
package backoff
import (
"errors"
"time"
)
// An OperationWithData is executing by RetryWithData() or RetryNotifyWithData().
// The operation will be retried using a backoff policy if it returns an error.
type OperationWithData[T any] func() (T, error)
// An Operation is executing by Retry() or RetryNotify().
// The operation will be retried using a backoff policy if it returns an error.
type Operation func() error
func (o Operation) withEmptyData() OperationWithData[struct{}] {
return func() (struct{}, error) {
return struct{}{}, o()
}
}
// Notify is a notify-on-error function. It receives an operation error and
// backoff delay if the operation failed (with an error).
//
// NOTE that if the backoff policy stated to stop retrying,
// the notify function isn't called.
type Notify func(error, time.Duration)
// Retry the operation o until it does not return error or BackOff stops.
// o is guaranteed to be run at least once.
//
// If o returns a *PermanentError, the operation is not retried, and the
// wrapped error is returned.
//
// Retry sleeps the goroutine for the duration returned by BackOff after a
// failed operation returns.
func Retry(o Operation, b BackOff) error {
return RetryNotify(o, b, nil)
}
// RetryWithData is like Retry but returns data in the response too.
func RetryWithData[T any](o OperationWithData[T], b BackOff) (T, error) {
return RetryNotifyWithData(o, b, nil)
}
// RetryNotify calls notify function with the error and wait duration
// for each failed attempt before sleep.
func RetryNotify(operation Operation, b BackOff, notify Notify) error {
return RetryNotifyWithTimer(operation, b, notify, nil)
}
// RetryNotifyWithData is like RetryNotify but returns data in the response too.
func RetryNotifyWithData[T any](operation OperationWithData[T], b BackOff, notify Notify) (T, error) {
return doRetryNotify(operation, b, notify, nil)
}
// RetryNotifyWithTimer calls notify function with the error and wait duration using the given Timer
// for each failed attempt before sleep.
// A default timer that uses system timer is used when nil is passed.
func RetryNotifyWithTimer(operation Operation, b BackOff, notify Notify, t Timer) error {
_, err := doRetryNotify(operation.withEmptyData(), b, notify, t)
return err
}
// RetryNotifyWithTimerAndData is like RetryNotifyWithTimer but returns data in the response too.
func RetryNotifyWithTimerAndData[T any](operation OperationWithData[T], b BackOff, notify Notify, t Timer) (T, error) {
return doRetryNotify(operation, b, notify, t)
}
func doRetryNotify[T any](operation OperationWithData[T], b BackOff, notify Notify, t Timer) (T, error) {
var (
err error
next time.Duration
res T
)
if t == nil {
t = &defaultTimer{}
}
defer func() {
t.Stop()
}()
ctx := getContext(b)
b.Reset()
for {
res, err = operation()
if err == nil {
return res, nil
}
var permanent *PermanentError
if errors.As(err, &permanent) {
return res, permanent.Err
}
if next = b.NextBackOff(); next == Stop {
if cerr := ctx.Err(); cerr != nil {
return res, cerr
}
return res, err
}
if notify != nil {
notify(err, next)
}
t.Start(next)
select {
case <-ctx.Done():
return res, ctx.Err()
case <-t.C():
}
}
}
// PermanentError signals that the operation should not be retried.
type PermanentError struct {
Err error
}
func (e *PermanentError) Error() string {
return e.Err.Error()
}
func (e *PermanentError) Unwrap() error {
return e.Err
}
func (e *PermanentError) Is(target error) bool {
_, ok := target.(*PermanentError)
return ok
}
// Permanent wraps the given err in a *PermanentError.
func Permanent(err error) error {
if err == nil {
return nil
}
return &PermanentError{
Err: err,
}
}

View File

@@ -1,38 +0,0 @@
package backoff
import "time"
/*
WithMaxRetries creates a wrapper around another BackOff, which will
return Stop if NextBackOff() has been called too many times since
the last time Reset() was called
Note: Implementation is not thread-safe.
*/
func WithMaxRetries(b BackOff, max uint64) BackOff {
return &backOffTries{delegate: b, maxTries: max}
}
type backOffTries struct {
delegate BackOff
maxTries uint64
numTries uint64
}
func (b *backOffTries) NextBackOff() time.Duration {
if b.maxTries == 0 {
return Stop
}
if b.maxTries > 0 {
if b.maxTries <= b.numTries {
return Stop
}
b.numTries++
}
return b.delegate.NextBackOff()
}
func (b *backOffTries) Reset() {
b.numTries = 0
b.delegate.Reset()
}

29
vendor/github.com/cenkalti/backoff/v5/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,29 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [5.0.0] - 2024-12-19
### Added
- RetryAfterError can be returned from an operation to indicate how long to wait before the next retry.
### Changed
- Retry function now accepts additional options for specifying max number of tries and max elapsed time.
- Retry function now accepts a context.Context.
- Operation function signature changed to return result (any type) and error.
### Removed
- RetryNotify* and RetryWithData functions. Only single Retry function remains.
- Optional arguments from ExponentialBackoff constructor.
- Clock and Timer interfaces.
### Fixed
- The original error is returned from Retry if there's a PermanentError. (#144)
- The Retry function respects the wrapped PermanentError. (#140)

View File

@@ -1,4 +1,4 @@
# Exponential Backoff [![GoDoc][godoc image]][godoc] [![Coverage Status][coveralls image]][coveralls]
# Exponential Backoff [![GoDoc][godoc image]][godoc]
This is a Go port of the exponential backoff algorithm from [Google's HTTP Client Library for Java][google-http-java-client].
@@ -9,9 +9,11 @@ The retries exponentially increase and stop increasing when a certain threshold
## Usage
Import path is `github.com/cenkalti/backoff/v4`. Please note the version part at the end.
Import path is `github.com/cenkalti/backoff/v5`. Please note the version part at the end.
Use https://pkg.go.dev/github.com/cenkalti/backoff/v4 to view the documentation.
For most cases, use `Retry` function. See [example_test.go][example] for an example.
If you have specific needs, copy `Retry` function (from [retry.go][retry-src]) into your code and modify it as needed.
## Contributing
@@ -19,12 +21,11 @@ Use https://pkg.go.dev/github.com/cenkalti/backoff/v4 to view the documentation.
* Please don't send a PR without opening an issue and discussing it first.
* If proposed change is not a common use case, I will probably not accept it.
[godoc]: https://pkg.go.dev/github.com/cenkalti/backoff/v4
[godoc]: https://pkg.go.dev/github.com/cenkalti/backoff/v5
[godoc image]: https://godoc.org/github.com/cenkalti/backoff?status.png
[coveralls]: https://coveralls.io/github/cenkalti/backoff?branch=master
[coveralls image]: https://coveralls.io/repos/github/cenkalti/backoff/badge.svg?branch=master
[google-http-java-client]: https://github.com/google/google-http-java-client/blob/da1aa993e90285ec18579f1553339b00e19b3ab5/google-http-client/src/main/java/com/google/api/client/util/ExponentialBackOff.java
[exponential backoff wiki]: http://en.wikipedia.org/wiki/Exponential_backoff
[advanced example]: https://pkg.go.dev/github.com/cenkalti/backoff/v4?tab=doc#pkg-examples
[retry-src]: https://github.com/cenkalti/backoff/blob/v5/retry.go
[example]: https://github.com/cenkalti/backoff/blob/v5/example_test.go

View File

@@ -15,12 +15,12 @@ import "time"
// BackOff is a backoff policy for retrying an operation.
type BackOff interface {
// NextBackOff returns the duration to wait before retrying the operation,
// or backoff. Stop to indicate that no more retries should be made.
// backoff.Stop to indicate that no more retries should be made.
//
// Example usage:
//
// duration := backoff.NextBackOff();
// if (duration == backoff.Stop) {
// duration := backoff.NextBackOff()
// if duration == backoff.Stop {
// // Do not retry operation.
// } else {
// // Sleep for duration and retry operation.

46
vendor/github.com/cenkalti/backoff/v5/error.go generated vendored Normal file
View File

@@ -0,0 +1,46 @@
package backoff
import (
"fmt"
"time"
)
// PermanentError signals that the operation should not be retried.
type PermanentError struct {
Err error
}
// Permanent wraps the given err in a *PermanentError.
func Permanent(err error) error {
if err == nil {
return nil
}
return &PermanentError{
Err: err,
}
}
// Error returns a string representation of the Permanent error.
func (e *PermanentError) Error() string {
return e.Err.Error()
}
// Unwrap returns the wrapped error.
func (e *PermanentError) Unwrap() error {
return e.Err
}
// RetryAfterError signals that the operation should be retried after the given duration.
type RetryAfterError struct {
Duration time.Duration
}
// RetryAfter returns a RetryAfter error that specifies how long to wait before retrying.
func RetryAfter(seconds int) error {
return &RetryAfterError{Duration: time.Duration(seconds) * time.Second}
}
// Error returns a string representation of the RetryAfter error.
func (e *RetryAfterError) Error() string {
return fmt.Sprintf("retry after %s", e.Duration)
}

125
vendor/github.com/cenkalti/backoff/v5/exponential.go generated vendored Normal file
View File

@@ -0,0 +1,125 @@
package backoff
import (
"math/rand"
"time"
)
/*
ExponentialBackOff is a backoff implementation that increases the backoff
period for each retry attempt using a randomization function that grows exponentially.
NextBackOff() is calculated using the following formula:
randomized interval =
RetryInterval * (random value in range [1 - RandomizationFactor, 1 + RandomizationFactor])
In other words NextBackOff() will range between the randomization factor
percentage below and above the retry interval.
For example, given the following parameters:
RetryInterval = 2
RandomizationFactor = 0.5
Multiplier = 2
the actual backoff period used in the next retry attempt will range between 1 and 3 seconds,
multiplied by the exponential, that is, between 2 and 6 seconds.
Note: MaxInterval caps the RetryInterval and not the randomized interval.
If the time elapsed since an ExponentialBackOff instance is created goes past the
MaxElapsedTime, then the method NextBackOff() starts returning backoff.Stop.
The elapsed time can be reset by calling Reset().
Example: Given the following default arguments, for 10 tries the sequence will be,
and assuming we go over the MaxElapsedTime on the 10th try:
Request # RetryInterval (seconds) Randomized Interval (seconds)
1 0.5 [0.25, 0.75]
2 0.75 [0.375, 1.125]
3 1.125 [0.562, 1.687]
4 1.687 [0.8435, 2.53]
5 2.53 [1.265, 3.795]
6 3.795 [1.897, 5.692]
7 5.692 [2.846, 8.538]
8 8.538 [4.269, 12.807]
9 12.807 [6.403, 19.210]
10 19.210 backoff.Stop
Note: Implementation is not thread-safe.
*/
type ExponentialBackOff struct {
InitialInterval time.Duration
RandomizationFactor float64
Multiplier float64
MaxInterval time.Duration
currentInterval time.Duration
}
// Default values for ExponentialBackOff.
const (
DefaultInitialInterval = 500 * time.Millisecond
DefaultRandomizationFactor = 0.5
DefaultMultiplier = 1.5
DefaultMaxInterval = 60 * time.Second
)
// NewExponentialBackOff creates an instance of ExponentialBackOff using default values.
func NewExponentialBackOff() *ExponentialBackOff {
return &ExponentialBackOff{
InitialInterval: DefaultInitialInterval,
RandomizationFactor: DefaultRandomizationFactor,
Multiplier: DefaultMultiplier,
MaxInterval: DefaultMaxInterval,
}
}
// Reset the interval back to the initial retry interval and restarts the timer.
// Reset must be called before using b.
func (b *ExponentialBackOff) Reset() {
b.currentInterval = b.InitialInterval
}
// NextBackOff calculates the next backoff interval using the formula:
//
// Randomized interval = RetryInterval * (1 ± RandomizationFactor)
func (b *ExponentialBackOff) NextBackOff() time.Duration {
if b.currentInterval == 0 {
b.currentInterval = b.InitialInterval
}
next := getRandomValueFromInterval(b.RandomizationFactor, rand.Float64(), b.currentInterval)
b.incrementCurrentInterval()
return next
}
// Increments the current interval by multiplying it with the multiplier.
func (b *ExponentialBackOff) incrementCurrentInterval() {
// Check for overflow, if overflow is detected set the current interval to the max interval.
if float64(b.currentInterval) >= float64(b.MaxInterval)/b.Multiplier {
b.currentInterval = b.MaxInterval
} else {
b.currentInterval = time.Duration(float64(b.currentInterval) * b.Multiplier)
}
}
// Returns a random value from the following interval:
//
// [currentInterval - randomizationFactor * currentInterval, currentInterval + randomizationFactor * currentInterval].
func getRandomValueFromInterval(randomizationFactor, random float64, currentInterval time.Duration) time.Duration {
if randomizationFactor == 0 {
return currentInterval // make sure no randomness is used when randomizationFactor is 0.
}
var delta = randomizationFactor * float64(currentInterval)
var minInterval = float64(currentInterval) - delta
var maxInterval = float64(currentInterval) + delta
// Get a random value from the range [minInterval, maxInterval].
// The formula used below has a +1 because if the minInterval is 1 and the maxInterval is 3 then
// we want a 33% chance for selecting either 1, 2 or 3.
return time.Duration(minInterval + (random * (maxInterval - minInterval + 1)))
}

139
vendor/github.com/cenkalti/backoff/v5/retry.go generated vendored Normal file
View File

@@ -0,0 +1,139 @@
package backoff
import (
"context"
"errors"
"time"
)
// DefaultMaxElapsedTime sets a default limit for the total retry duration.
const DefaultMaxElapsedTime = 15 * time.Minute
// Operation is a function that attempts an operation and may be retried.
type Operation[T any] func() (T, error)
// Notify is a function called on operation error with the error and backoff duration.
type Notify func(error, time.Duration)
// retryOptions holds configuration settings for the retry mechanism.
type retryOptions struct {
BackOff BackOff // Strategy for calculating backoff periods.
Timer timer // Timer to manage retry delays.
Notify Notify // Optional function to notify on each retry error.
MaxTries uint // Maximum number of retry attempts.
MaxElapsedTime time.Duration // Maximum total time for all retries.
}
type RetryOption func(*retryOptions)
// WithBackOff configures a custom backoff strategy.
func WithBackOff(b BackOff) RetryOption {
return func(args *retryOptions) {
args.BackOff = b
}
}
// withTimer sets a custom timer for managing delays between retries.
func withTimer(t timer) RetryOption {
return func(args *retryOptions) {
args.Timer = t
}
}
// WithNotify sets a notification function to handle retry errors.
func WithNotify(n Notify) RetryOption {
return func(args *retryOptions) {
args.Notify = n
}
}
// WithMaxTries limits the number of retry attempts.
func WithMaxTries(n uint) RetryOption {
return func(args *retryOptions) {
args.MaxTries = n
}
}
// WithMaxElapsedTime limits the total duration for retry attempts.
func WithMaxElapsedTime(d time.Duration) RetryOption {
return func(args *retryOptions) {
args.MaxElapsedTime = d
}
}
// Retry attempts the operation until success, a permanent error, or backoff completion.
// It ensures the operation is executed at least once.
//
// Returns the operation result or error if retries are exhausted or context is cancelled.
func Retry[T any](ctx context.Context, operation Operation[T], opts ...RetryOption) (T, error) {
// Initialize default retry options.
args := &retryOptions{
BackOff: NewExponentialBackOff(),
Timer: &defaultTimer{},
MaxElapsedTime: DefaultMaxElapsedTime,
}
// Apply user-provided options to the default settings.
for _, opt := range opts {
opt(args)
}
defer args.Timer.Stop()
startedAt := time.Now()
args.BackOff.Reset()
for numTries := uint(1); ; numTries++ {
// Execute the operation.
res, err := operation()
if err == nil {
return res, nil
}
// Stop retrying if maximum tries exceeded.
if args.MaxTries > 0 && numTries >= args.MaxTries {
return res, err
}
// Handle permanent errors without retrying.
var permanent *PermanentError
if errors.As(err, &permanent) {
return res, err
}
// Stop retrying if context is cancelled.
if cerr := context.Cause(ctx); cerr != nil {
return res, cerr
}
// Calculate next backoff duration.
next := args.BackOff.NextBackOff()
if next == Stop {
return res, err
}
// Reset backoff if RetryAfterError is encountered.
var retryAfter *RetryAfterError
if errors.As(err, &retryAfter) {
next = retryAfter.Duration
args.BackOff.Reset()
}
// Stop retrying if maximum elapsed time exceeded.
if args.MaxElapsedTime > 0 && time.Since(startedAt)+next > args.MaxElapsedTime {
return res, err
}
// Notify on error if a notifier function is provided.
if args.Notify != nil {
args.Notify(err, next)
}
// Wait for the next backoff period or context cancellation.
args.Timer.Start(next)
select {
case <-args.Timer.C():
case <-ctx.Done():
return res, context.Cause(ctx)
}
}
}

View File

@@ -1,7 +1,6 @@
package backoff
import (
"context"
"sync"
"time"
)
@@ -14,8 +13,7 @@ type Ticker struct {
C <-chan time.Time
c chan time.Time
b BackOff
ctx context.Context
timer Timer
timer timer
stop chan struct{}
stopOnce sync.Once
}
@@ -27,22 +25,12 @@ type Ticker struct {
// provided backoff policy (notably calling NextBackOff or Reset)
// while the ticker is running.
func NewTicker(b BackOff) *Ticker {
return NewTickerWithTimer(b, &defaultTimer{})
}
// NewTickerWithTimer returns a new Ticker with a custom timer.
// A default timer that uses system timer is used when nil is passed.
func NewTickerWithTimer(b BackOff, timer Timer) *Ticker {
if timer == nil {
timer = &defaultTimer{}
}
c := make(chan time.Time)
t := &Ticker{
C: c,
c: c,
b: b,
ctx: getContext(b),
timer: timer,
timer: &defaultTimer{},
stop: make(chan struct{}),
}
t.b.Reset()
@@ -73,8 +61,6 @@ func (t *Ticker) run() {
case <-t.stop:
t.c = nil // Prevent future ticks from being sent to the channel.
return
case <-t.ctx.Done():
return
}
}
}

View File

@@ -2,7 +2,7 @@ package backoff
import "time"
type Timer interface {
type timer interface {
Start(duration time.Duration)
Stop()
C() <-chan time.Time

View File

@@ -1,3 +1,4 @@
// Package md2man aims in converting markdown into roff (man pages).
package md2man
import (

View File

@@ -47,13 +47,13 @@ const (
tableStart = "\n.TS\nallbox;\n"
tableEnd = ".TE\n"
tableCellStart = "T{\n"
tableCellEnd = "\nT}\n"
tableCellEnd = "\nT}"
tablePreprocessor = `'\" t`
)
// NewRoffRenderer creates a new blackfriday Renderer for generating roff documents
// from markdown
func NewRoffRenderer() *roffRenderer { // nolint: golint
func NewRoffRenderer() *roffRenderer {
return &roffRenderer{}
}
@@ -316,9 +316,8 @@ func (r *roffRenderer) handleTableCell(w io.Writer, node *blackfriday.Node, ente
} else if nodeLiteralSize(node) > 30 {
end = tableCellEnd
}
if node.Next == nil && end != tableCellEnd {
// Last cell: need to carriage return if we are at the end of the
// header row and content isn't wrapped in a "tablecell"
if node.Next == nil {
// Last cell: need to carriage return if we are at the end of the header row.
end += crTag
}
out(w, end)

View File

@@ -12,7 +12,7 @@ type Timer struct {
C <-chan time.Time
i int // heap index.
when int64 // Timer wakes up at when.
when time.Time // Timer wakes up at when.
// f is called in a locked context on timeout. This function must not block
// and must behave well-defined.

View File

@@ -15,24 +15,9 @@ func init() {
go timerRoutine()
}
// when is a helper function for setting the 'when' field of a runtimeTimer.
// It returns what the time will be, in nanoseconds, Duration d in the future.
// If d is negative, it is ignored. If the returned value would be less than
// zero because of an overflow, MaxInt64 is returned.
func when(d time.Duration) int64 {
if d <= 0 {
return time.Now().UnixNano()
}
t := time.Now().UnixNano() + int64(d)
if t < 0 {
t = 1<<63 - 1 // math.MaxInt64
}
return t
}
// Add the timer to the heap.
func addTimer(t *Timer, d time.Duration) {
t.when = when(d)
t.when = time.Now().Add(d)
mutex.Lock()
addTimerLocked(t)
@@ -91,7 +76,7 @@ func resetTimer(t *Timer, d time.Duration) (b bool) {
mutex.Lock()
b = delTimerLocked(t)
t.reset()
t.when = when(d)
t.when = time.Now().Add(d)
addTimerLocked(t)
mutex.Unlock()
return
@@ -107,7 +92,6 @@ func reschedule() {
func timerRoutine() {
var now time.Time
var delta int64
var last int
var sleepTimerActive bool
@@ -139,12 +123,12 @@ Loop:
}
t := timers[0]
delta = t.when - now.UnixNano()
delta := t.when.Sub(now)
// Sleep if not expired.
if delta > 0 {
mutex.Unlock()
sleepTimer.Reset(time.Duration(delta))
sleepTimer.Reset(delta)
sleepTimerActive = true
continue Loop
}
@@ -182,7 +166,7 @@ func siftupTimer(i int) {
var p int
for i > 0 {
p = (i - 1) / 4 // parent
if when >= timers[p].when {
if !when.Before(timers[p].when) {
break
}
timers[i] = timers[p]
@@ -204,22 +188,22 @@ func siftdownTimer(i int) {
break
}
w := timers[c].when
if c+1 < n && timers[c+1].when < w {
if c+1 < n && timers[c+1].when.Before(w) {
w = timers[c+1].when
c++
}
if c3 < n {
w3 := timers[c3].when
if c3+1 < n && timers[c3+1].when < w3 {
if c3+1 < n && timers[c3+1].when.Before(w3) {
w3 = timers[c3+1].when
c3++
}
if w3 < w {
if w3.Before(w) {
w = w3
c = c3
}
}
if w >= when {
if !w.Before(when) {
break
}
timers[i] = timers[c]

View File

@@ -9,7 +9,7 @@ suits you.
## Install
```bash
```
go get github.com/fatih/color
```
@@ -30,6 +30,18 @@ color.Magenta("And many others ..")
```
### RGB colors
If your terminal supports 24-bit colors, you can use RGB color codes.
```go
color.RGB(255, 128, 0).Println("foreground orange")
color.RGB(230, 42, 42).Println("foreground red")
color.BgRGB(255, 128, 0).Println("background orange")
color.BgRGB(230, 42, 42).Println("background red")
```
### Mix and reuse colors
```go
@@ -49,6 +61,11 @@ boldRed.Println("This will print text in bold red.")
whiteBackground := red.Add(color.BgWhite)
whiteBackground.Println("Red text with white background.")
// Mix with RGB color codes
color.RGB(255, 128, 0).AddBgRGB(0, 0, 0).Println("orange with black background")
color.BgRGB(255, 128, 0).AddRGB(255, 255, 255).Println("orange background with white foreground")
```
### Use your own output (io.Writer)
@@ -161,10 +178,6 @@ c.Println("This prints again cyan...")
To output color in GitHub Actions (or other CI systems that support ANSI colors), make sure to set `color.NoColor = false` so that it bypasses the check for non-tty output streams.
## Todo
* Save/Return previous values
* Evaluate fmt.Formatter interface
## Credits

View File

@@ -65,6 +65,29 @@ const (
CrossedOut
)
const (
ResetBold Attribute = iota + 22
ResetItalic
ResetUnderline
ResetBlinking
_
ResetReversed
ResetConcealed
ResetCrossedOut
)
var mapResetAttributes map[Attribute]Attribute = map[Attribute]Attribute{
Bold: ResetBold,
Faint: ResetBold,
Italic: ResetItalic,
Underline: ResetUnderline,
BlinkSlow: ResetBlinking,
BlinkRapid: ResetBlinking,
ReverseVideo: ResetReversed,
Concealed: ResetConcealed,
CrossedOut: ResetCrossedOut,
}
// Foreground text colors
const (
FgBlack Attribute = iota + 30
@@ -75,6 +98,9 @@ const (
FgMagenta
FgCyan
FgWhite
// used internally for 256 and 24-bit coloring
foreground
)
// Foreground Hi-Intensity text colors
@@ -99,6 +125,9 @@ const (
BgMagenta
BgCyan
BgWhite
// used internally for 256 and 24-bit coloring
background
)
// Background Hi-Intensity text colors
@@ -127,6 +156,30 @@ func New(value ...Attribute) *Color {
return c
}
// RGB returns a new foreground color in 24-bit RGB.
func RGB(r, g, b int) *Color {
return New(foreground, 2, Attribute(r), Attribute(g), Attribute(b))
}
// BgRGB returns a new background color in 24-bit RGB.
func BgRGB(r, g, b int) *Color {
return New(background, 2, Attribute(r), Attribute(g), Attribute(b))
}
// AddRGB is used to chain foreground RGB SGR parameters. Use as many as parameters to combine
// and create custom color objects. Example: .Add(34, 0, 12).Add(255, 128, 0).
func (c *Color) AddRGB(r, g, b int) *Color {
c.params = append(c.params, foreground, 2, Attribute(r), Attribute(g), Attribute(b))
return c
}
// AddRGB is used to chain background RGB SGR parameters. Use as many as parameters to combine
// and create custom color objects. Example: .Add(34, 0, 12).Add(255, 128, 0).
func (c *Color) AddBgRGB(r, g, b int) *Color {
c.params = append(c.params, background, 2, Attribute(r), Attribute(g), Attribute(b))
return c
}
// Set sets the given parameters immediately. It will change the color of
// output with the given SGR parameters until color.Unset() is called.
func Set(p ...Attribute) *Color {
@@ -246,10 +299,7 @@ func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
// On Windows, users should wrap w with colorable.NewColorable() if w is of
// type *os.File.
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
c.SetWriter(w)
defer c.UnsetWriter(w)
return fmt.Fprintln(w, a...)
return fmt.Fprintln(w, c.wrap(sprintln(a...)))
}
// Println formats using the default formats for its operands and writes to
@@ -258,10 +308,7 @@ func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
// encountered. This is the standard fmt.Print() method wrapped with the given
// color.
func (c *Color) Println(a ...interface{}) (n int, err error) {
c.Set()
defer c.unset()
return fmt.Fprintln(Output, a...)
return fmt.Fprintln(Output, c.wrap(sprintln(a...)))
}
// Sprint is just like Print, but returns a string instead of printing it.
@@ -271,7 +318,7 @@ func (c *Color) Sprint(a ...interface{}) string {
// Sprintln is just like Println, but returns a string instead of printing it.
func (c *Color) Sprintln(a ...interface{}) string {
return c.wrap(fmt.Sprintln(a...))
return c.wrap(sprintln(a...)) + "\n"
}
// Sprintf is just like Printf, but returns a string instead of printing it.
@@ -353,7 +400,7 @@ func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
// string. Windows users should use this in conjunction with color.Output.
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
return func(a ...interface{}) string {
return c.wrap(fmt.Sprintln(a...))
return c.wrap(sprintln(a...)) + "\n"
}
}
@@ -383,7 +430,18 @@ func (c *Color) format() string {
}
func (c *Color) unformat() string {
return fmt.Sprintf("%s[%dm", escape, Reset)
//return fmt.Sprintf("%s[%dm", escape, Reset)
//for each element in sequence let's use the specific reset escape, or the generic one if not found
format := make([]string, len(c.params))
for i, v := range c.params {
format[i] = strconv.Itoa(int(Reset))
ra, ok := mapResetAttributes[v]
if ok {
format[i] = strconv.Itoa(int(ra))
}
}
return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";"))
}
// DisableColor disables the color output. Useful to not change any existing
@@ -411,6 +469,12 @@ func (c *Color) isNoColorSet() bool {
// Equals returns a boolean value indicating whether two colors are equal.
func (c *Color) Equals(c2 *Color) bool {
if c == nil && c2 == nil {
return true
}
if c == nil || c2 == nil {
return false
}
if len(c.params) != len(c2.params) {
return false
}
@@ -614,3 +678,8 @@ func HiCyanString(format string, a ...interface{}) string { return colorString(f
func HiWhiteString(format string, a ...interface{}) string {
return colorString(format, FgHiWhite, a...)
}
// sprintln is a helper function to format a string with fmt.Sprintln and trim the trailing newline.
func sprintln(a ...interface{}) string {
return strings.TrimSuffix(fmt.Sprintln(a...), "\n")
}

19
vendor/github.com/fatih/color/color_windows.go generated vendored Normal file
View File

@@ -0,0 +1,19 @@
package color
import (
"os"
"golang.org/x/sys/windows"
)
func init() {
// Opt-in for ansi color support for current process.
// https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#output-sequences
var outMode uint32
out := windows.Handle(os.Stdout.Fd())
if err := windows.GetConsoleMode(out, &outMode); err != nil {
return
}
outMode |= windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
_ = windows.SetConsoleMode(out, outMode)
}

View File

@@ -1,7 +1,7 @@
freebsd_task:
name: 'FreeBSD'
freebsd_instance:
image_family: freebsd-14-1
image_family: freebsd-14-2
install_script:
- pkg update -f
- pkg install -y go

View File

@@ -1,6 +1,39 @@
# Changelog
1.8.0 2023-10-31
1.9.0 2024-04-04
----------------
### Changes and fixes
- all: make BufferedWatcher buffered again ([#657])
- inotify: fix race when adding/removing watches while a watched path is being
deleted ([#678], [#686])
- inotify: don't send empty event if a watched path is unmounted ([#655])
- inotify: don't register duplicate watches when watching both a symlink and its
target; previously that would get "half-added" and removing the second would
panic ([#679])
- kqueue: fix watching relative symlinks ([#681])
- kqueue: correctly mark pre-existing entries when watching a link to a dir on
kqueue ([#682])
- illumos: don't send error if changed file is deleted while processing the
event ([#678])
[#657]: https://github.com/fsnotify/fsnotify/pull/657
[#678]: https://github.com/fsnotify/fsnotify/pull/678
[#686]: https://github.com/fsnotify/fsnotify/pull/686
[#655]: https://github.com/fsnotify/fsnotify/pull/655
[#681]: https://github.com/fsnotify/fsnotify/pull/681
[#679]: https://github.com/fsnotify/fsnotify/pull/679
[#682]: https://github.com/fsnotify/fsnotify/pull/682
1.8.0 2024-10-31
----------------
### Additions

View File

@@ -77,6 +77,7 @@ End-of-line escapes with `\` are not supported.
debug [yes/no] # Enable/disable FSNOTIFY_DEBUG (tests are run in
parallel by default, so -parallel=1 is probably a good
idea).
print [any strings] # Print text to stdout; for debugging.
touch path
mkdir [-p] dir

View File

@@ -15,7 +15,6 @@ Platform support:
| ReadDirectoryChangesW | Windows | Supported |
| FEN | illumos | Supported |
| fanotify | Linux 5.9+ | [Not yet](https://github.com/fsnotify/fsnotify/issues/114) |
| AHAFS | AIX | [aix branch]; experimental due to lack of maintainer and test environment |
| FSEvents | macOS | [Needs support in x/sys/unix][fsevents] |
| USN Journals | Windows | [Needs support in x/sys/windows][usn] |
| Polling | *All* | [Not yet](https://github.com/fsnotify/fsnotify/issues/9) |
@@ -25,7 +24,6 @@ untested.
[fsevents]: https://github.com/fsnotify/fsnotify/issues/11#issuecomment-1279133120
[usn]: https://github.com/fsnotify/fsnotify/issues/53#issuecomment-1279829847
[aix branch]: https://github.com/fsnotify/fsnotify/issues/353#issuecomment-1284590129
Usage
-----

View File

@@ -9,6 +9,7 @@ package fsnotify
import (
"errors"
"fmt"
"io/fs"
"os"
"path/filepath"
"sync"
@@ -19,27 +20,25 @@ import (
)
type fen struct {
*shared
Events chan Event
Errors chan error
mu sync.Mutex
port *unix.EventPort
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
dirs map[string]Op // Explicitly watched directories
watches map[string]Op // Explicitly watched non-directories
}
func newBackend(ev chan Event, errs chan error) (backend, error) {
return newBufferedBackend(0, ev, errs)
}
var defaultBufferSize = 0
func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
func newBackend(ev chan Event, errs chan error) (backend, error) {
w := &fen{
shared: newShared(ev, errs),
Events: ev,
Errors: errs,
dirs: make(map[string]Op),
watches: make(map[string]Op),
done: make(chan struct{}),
}
var err error
@@ -52,49 +51,10 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
return w, nil
}
// sendEvent attempts to send an event to the user, returning true if the event
// was put in the channel successfully and false if the watcher has been closed.
func (w *fen) sendEvent(name string, op Op) (sent bool) {
select {
case <-w.done:
return false
case w.Events <- Event{Name: name, Op: op}:
return true
}
}
// sendError attempts to send an error to the user, returning true if the error
// was put in the channel successfully and false if the watcher has been closed.
func (w *fen) sendError(err error) (sent bool) {
if err == nil {
return true
}
select {
case <-w.done:
return false
case w.Errors <- err:
return true
}
}
func (w *fen) isClosed() bool {
select {
case <-w.done:
return true
default:
return false
}
}
func (w *fen) Close() error {
// Take the lock used by associateFile to prevent lingering events from
// being processed after the close
w.mu.Lock()
defer w.mu.Unlock()
if w.isClosed() {
if w.shared.close() {
return nil
}
close(w.done)
return w.port.Close()
}
@@ -209,7 +169,7 @@ func (w *fen) readEvents() {
return
}
// There was an error not caused by calling w.Close()
if !w.sendError(err) {
if !w.sendError(fmt.Errorf("port.Get: %w", err)) {
return
}
}
@@ -277,13 +237,13 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
isWatched := watchedDir || watchedPath
if events&unix.FILE_DELETE != 0 {
if !w.sendEvent(path, Remove) {
if !w.sendEvent(Event{Name: path, Op: Remove}) {
return nil
}
reRegister = false
}
if events&unix.FILE_RENAME_FROM != 0 {
if !w.sendEvent(path, Rename) {
if !w.sendEvent(Event{Name: path, Op: Rename}) {
return nil
}
// Don't keep watching the new file name
@@ -297,7 +257,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
// inotify reports a Remove event in this case, so we simulate this
// here.
if !w.sendEvent(path, Remove) {
if !w.sendEvent(Event{Name: path, Op: Remove}) {
return nil
}
// Don't keep watching the file that was removed
@@ -331,7 +291,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
// get here, the sudirectory is already gone. Clearly we were watching
// this path but now it is gone. Let's tell the user that it was
// removed.
if !w.sendEvent(path, Remove) {
if !w.sendEvent(Event{Name: path, Op: Remove}) {
return nil
}
// Suppress extra write events on removed directories; they are not
@@ -346,7 +306,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
if err != nil {
// The symlink still exists, but the target is gone. Report the
// Remove similar to above.
if !w.sendEvent(path, Remove) {
if !w.sendEvent(Event{Name: path, Op: Remove}) {
return nil
}
// Don't return the error
@@ -359,7 +319,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
return err
}
} else {
if !w.sendEvent(path, Write) {
if !w.sendEvent(Event{Name: path, Op: Write}) {
return nil
}
}
@@ -367,7 +327,7 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
if events&unix.FILE_ATTRIB != 0 && stat != nil {
// Only send Chmod if perms changed
if stat.Mode().Perm() != fmode.Perm() {
if !w.sendEvent(path, Chmod) {
if !w.sendEvent(Event{Name: path, Op: Chmod}) {
return nil
}
}
@@ -376,17 +336,27 @@ func (w *fen) handleEvent(event *unix.PortEvent) error {
if stat != nil {
// If we get here, it means we've hit an event above that requires us to
// continue watching the file or directory
return w.associateFile(path, stat, isWatched)
err := w.associateFile(path, stat, isWatched)
if errors.Is(err, fs.ErrNotExist) {
// Path may have been removed since the stat.
err = nil
}
return err
}
return nil
}
func (w *fen) updateDirectory(path string) error {
// The directory was modified, so we must find unwatched entities and watch
// them. If something was removed from the directory, nothing will happen,
// as everything else should still be watched.
// them. If something was removed from the directory, nothing will happen, as
// everything else should still be watched.
func (w *fen) updateDirectory(path string) error {
files, err := os.ReadDir(path)
if err != nil {
// Directory no longer exists: probably just deleted since we got the
// event.
if errors.Is(err, fs.ErrNotExist) {
return nil
}
return err
}
@@ -401,10 +371,15 @@ func (w *fen) updateDirectory(path string) error {
return err
}
err = w.associateFile(path, finfo, false)
if errors.Is(err, fs.ErrNotExist) {
// File may have disappeared between getting the dir listing and
// adding the port: that's okay to ignore.
continue
}
if !w.sendError(err) {
return nil
}
if !w.sendEvent(path, Create) {
if !w.sendEvent(Event{Name: path, Op: Create}) {
return nil
}
}
@@ -430,7 +405,7 @@ func (w *fen) associateFile(path string, stat os.FileInfo, follow bool) error {
// has fired but we haven't processed it yet.
err := w.port.DissociatePath(path)
if err != nil && !errors.Is(err, unix.ENOENT) {
return err
return fmt.Errorf("port.DissociatePath(%q): %w", path, err)
}
}
@@ -446,14 +421,22 @@ func (w *fen) associateFile(path string, stat os.FileInfo, follow bool) error {
if true {
events |= unix.FILE_ATTRIB
}
return w.port.AssociatePath(path, stat, events, stat.Mode())
err := w.port.AssociatePath(path, stat, events, stat.Mode())
if err != nil {
return fmt.Errorf("port.AssociatePath(%q): %w", path, err)
}
return nil
}
func (w *fen) dissociateFile(path string, stat os.FileInfo, unused bool) error {
if !w.port.PathIsWatched(path) {
return nil
}
return w.port.DissociatePath(path)
err := w.port.DissociatePath(path)
if err != nil {
return fmt.Errorf("port.DissociatePath(%q): %w", path, err)
}
return nil
}
func (w *fen) WatchList() []string {

View File

@@ -19,6 +19,7 @@ import (
)
type inotify struct {
*shared
Events chan Event
Errors chan error
@@ -27,8 +28,6 @@ type inotify struct {
fd int
inotifyFile *os.File
watches *watches
done chan struct{} // Channel for sending a "quit message" to the reader goroutine
doneMu sync.Mutex
doneResp chan struct{} // Channel to respond to Close
// Store rename cookies in an array, with the index wrapping to 0. Almost
@@ -52,7 +51,6 @@ type inotify struct {
type (
watches struct {
mu sync.RWMutex
wd map[uint32]*watch // wd → watch
path map[string]uint32 // pathname → wd
}
@@ -75,34 +73,13 @@ func newWatches() *watches {
}
}
func (w *watches) len() int {
w.mu.RLock()
defer w.mu.RUnlock()
return len(w.wd)
}
func (w *watches) add(ww *watch) {
w.mu.Lock()
defer w.mu.Unlock()
w.wd[ww.wd] = ww
w.path[ww.path] = ww.wd
}
func (w *watches) remove(wd uint32) {
w.mu.Lock()
defer w.mu.Unlock()
watch := w.wd[wd] // Could have had Remove() called. See #616.
if watch == nil {
return
}
delete(w.path, watch.path)
delete(w.wd, wd)
}
func (w *watches) byPath(path string) *watch { return w.wd[w.path[path]] }
func (w *watches) byWd(wd uint32) *watch { return w.wd[wd] }
func (w *watches) len() int { return len(w.wd) }
func (w *watches) add(ww *watch) { w.wd[ww.wd] = ww; w.path[ww.path] = ww.wd }
func (w *watches) remove(watch *watch) { delete(w.path, watch.path); delete(w.wd, watch.wd) }
func (w *watches) removePath(path string) ([]uint32, error) {
w.mu.Lock()
defer w.mu.Unlock()
path, recurse := recursivePath(path)
wd, ok := w.path[path]
if !ok {
@@ -123,7 +100,7 @@ func (w *watches) removePath(path string) ([]uint32, error) {
wds := make([]uint32, 0, 8)
wds = append(wds, wd)
for p, rwd := range w.path {
if filepath.HasPrefix(p, path) {
if strings.HasPrefix(p, path) {
delete(w.path, p)
delete(w.wd, rwd)
wds = append(wds, rwd)
@@ -132,22 +109,7 @@ func (w *watches) removePath(path string) ([]uint32, error) {
return wds, nil
}
func (w *watches) byPath(path string) *watch {
w.mu.RLock()
defer w.mu.RUnlock()
return w.wd[w.path[path]]
}
func (w *watches) byWd(wd uint32) *watch {
w.mu.RLock()
defer w.mu.RUnlock()
return w.wd[wd]
}
func (w *watches) updatePath(path string, f func(*watch) (*watch, error)) error {
w.mu.Lock()
defer w.mu.Unlock()
var existing *watch
wd, ok := w.path[path]
if ok {
@@ -170,11 +132,9 @@ func (w *watches) updatePath(path string, f func(*watch) (*watch, error)) error
return nil
}
func newBackend(ev chan Event, errs chan error) (backend, error) {
return newBufferedBackend(0, ev, errs)
}
var defaultBufferSize = 0
func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
func newBackend(ev chan Event, errs chan error) (backend, error) {
// Need to set nonblocking mode for SetDeadline to work, otherwise blocking
// I/O operations won't terminate on close.
fd, errno := unix.InotifyInit1(unix.IN_CLOEXEC | unix.IN_NONBLOCK)
@@ -183,12 +143,12 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
}
w := &inotify{
shared: newShared(ev, errs),
Events: ev,
Errors: errs,
fd: fd,
inotifyFile: os.NewFile(uintptr(fd), ""),
watches: newWatches(),
done: make(chan struct{}),
doneResp: make(chan struct{}),
}
@@ -196,46 +156,10 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
return w, nil
}
// Returns true if the event was sent, or false if watcher is closed.
func (w *inotify) sendEvent(e Event) bool {
select {
case <-w.done:
return false
case w.Events <- e:
return true
}
}
// Returns true if the error was sent, or false if watcher is closed.
func (w *inotify) sendError(err error) bool {
if err == nil {
return true
}
select {
case <-w.done:
return false
case w.Errors <- err:
return true
}
}
func (w *inotify) isClosed() bool {
select {
case <-w.done:
return true
default:
return false
}
}
func (w *inotify) Close() error {
w.doneMu.Lock()
if w.isClosed() {
w.doneMu.Unlock()
if w.shared.close() {
return nil
}
close(w.done)
w.doneMu.Unlock()
// Causes any blocking reads to return with an error, provided the file
// still supports deadline operations.
@@ -244,9 +168,7 @@ func (w *inotify) Close() error {
return err
}
// Wait for goroutine to close
<-w.doneResp
<-w.doneResp // Wait for readEvents() to finish.
return nil
}
@@ -266,37 +188,7 @@ func (w *inotify) AddWith(path string, opts ...addOpt) error {
return fmt.Errorf("%w: %s", xErrUnsupported, with.op)
}
path, recurse := recursivePath(path)
if recurse {
return filepath.WalkDir(path, func(root string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() {
if root == path {
return fmt.Errorf("fsnotify: not a directory: %q", path)
}
return nil
}
// Send a Create event when adding new directory from a recursive
// watch; this is for "mkdir -p one/two/three". Usually all those
// directories will be created before we can set up watchers on the
// subdirectories, so only "one" would be sent as a Create event and
// not "one/two" and "one/two/three" (inotifywait -r has the same
// problem).
if with.sendCreate && root != path {
w.sendEvent(Event{Name: root, Op: Create})
}
return w.add(root, with, true)
})
}
return w.add(path, with, false)
}
func (w *inotify) add(path string, with withOpts, recurse bool) error {
add := func(path string, with withOpts, recurse bool) error {
var flags uint32
if with.noFollow {
flags |= unix.IN_DONT_FOLLOW
@@ -331,6 +223,38 @@ func (w *inotify) add(path string, with withOpts, recurse bool) error {
return w.register(path, flags, recurse)
}
w.mu.Lock()
defer w.mu.Unlock()
path, recurse := recursivePath(path)
if recurse {
return filepath.WalkDir(path, func(root string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if !d.IsDir() {
if root == path {
return fmt.Errorf("fsnotify: not a directory: %q", path)
}
return nil
}
// Send a Create event when adding new directory from a recursive
// watch; this is for "mkdir -p one/two/three". Usually all those
// directories will be created before we can set up watchers on the
// subdirectories, so only "one" would be sent as a Create event and
// not "one/two" and "one/two/three" (inotifywait -r has the same
// problem).
if with.sendCreate && root != path {
w.sendEvent(Event{Name: root, Op: Create})
}
return add(root, with, true)
})
}
return add(path, with, false)
}
func (w *inotify) register(path string, flags uint32, recurse bool) error {
return w.watches.updatePath(path, func(existing *watch) (*watch, error) {
if existing != nil {
@@ -342,6 +266,10 @@ func (w *inotify) register(path string, flags uint32, recurse bool) error {
return nil, err
}
if e, ok := w.watches.wd[uint32(wd)]; ok {
return e, nil
}
if existing == nil {
return &watch{
wd: uint32(wd),
@@ -365,6 +293,9 @@ func (w *inotify) Remove(name string) error {
fmt.Fprintf(os.Stderr, "FSNOTIFY_DEBUG: %s Remove(%q)\n",
time.Now().Format("15:04:05.000000000"), name)
}
w.mu.Lock()
defer w.mu.Unlock()
return w.remove(filepath.Clean(name))
}
@@ -399,13 +330,12 @@ func (w *inotify) WatchList() []string {
return nil
}
w.mu.Lock()
defer w.mu.Unlock()
entries := make([]string, 0, w.watches.len())
w.watches.mu.RLock()
for pathname := range w.watches.path {
entries = append(entries, pathname)
}
w.watches.mu.RUnlock()
return entries
}
@@ -418,21 +348,17 @@ func (w *inotify) readEvents() {
close(w.Events)
}()
var (
buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
errno error // Syscall errno
)
var buf [unix.SizeofInotifyEvent * 4096]byte // Buffer for a maximum of 4096 raw events
for {
// See if we have been closed.
if w.isClosed() {
return
}
n, err := w.inotifyFile.Read(buf[:])
switch {
case errors.Unwrap(err) == os.ErrClosed:
if err != nil {
if errors.Is(err, os.ErrClosed) {
return
case err != nil:
}
if !w.sendError(err) {
return
}
@@ -440,13 +366,9 @@ func (w *inotify) readEvents() {
}
if n < unix.SizeofInotifyEvent {
var err error
err := errors.New("notify: short read in readEvents()") // Read was too short.
if n == 0 {
err = io.EOF // If EOF is received. This should really never happen.
} else if n < 0 {
err = errno // If an error occurred while reading.
} else {
err = errors.New("notify: short read in readEvents()") // Read was too short.
}
if !w.sendError(err) {
return
@@ -454,111 +376,121 @@ func (w *inotify) readEvents() {
continue
}
// We don't know how many events we just read into the buffer
// While the offset points to at least one whole event...
// We don't know how many events we just read into the buffer While the
// offset points to at least one whole event.
var offset uint32
for offset <= uint32(n-unix.SizeofInotifyEvent) {
var (
// Point "raw" to the event in the buffer
raw = (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
mask = uint32(raw.Mask)
nameLen = uint32(raw.Len)
// Move to the next event in the buffer
next = func() { offset += unix.SizeofInotifyEvent + nameLen }
)
// Point to the event in the buffer.
inEvent := (*unix.InotifyEvent)(unsafe.Pointer(&buf[offset]))
if mask&unix.IN_Q_OVERFLOW != 0 {
if inEvent.Mask&unix.IN_Q_OVERFLOW != 0 {
if !w.sendError(ErrEventOverflow) {
return
}
}
/// If the event happened to the watched directory or the watched
/// file, the kernel doesn't append the filename to the event, but
/// we would like to always fill the the "Name" field with a valid
/// filename. We retrieve the path of the watch from the "paths"
/// map.
watch := w.watches.byWd(uint32(raw.Wd))
/// Can be nil if Remove() was called in another goroutine for this
/// path inbetween reading the events from the kernel and reading
/// the internal state. Not much we can do about it, so just skip.
/// See #616.
if watch == nil {
next()
continue
ev, ok := w.handleEvent(inEvent, &buf, offset)
if !ok {
return
}
if !w.sendEvent(ev) {
return
}
name := watch.path
// Move to the next event in the buffer
offset += unix.SizeofInotifyEvent + inEvent.Len
}
}
}
func (w *inotify) handleEvent(inEvent *unix.InotifyEvent, buf *[65536]byte, offset uint32) (Event, bool) {
w.mu.Lock()
defer w.mu.Unlock()
/// If the event happened to the watched directory or the watched file, the
/// kernel doesn't append the filename to the event, but we would like to
/// always fill the the "Name" field with a valid filename. We retrieve the
/// path of the watch from the "paths" map.
///
/// Can be nil if Remove() was called in another goroutine for this path
/// inbetween reading the events from the kernel and reading the internal
/// state. Not much we can do about it, so just skip. See #616.
watch := w.watches.byWd(uint32(inEvent.Wd))
if watch == nil {
return Event{}, true
}
var (
name = watch.path
nameLen = uint32(inEvent.Len)
)
if nameLen > 0 {
/// Point "bytes" at the first byte of the filename
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&buf[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
bb := *buf
bytes := (*[unix.PathMax]byte)(unsafe.Pointer(&bb[offset+unix.SizeofInotifyEvent]))[:nameLen:nameLen]
/// The filename is padded with NULL bytes. TrimRight() gets rid of those.
name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\000")
name += "/" + strings.TrimRight(string(bytes[0:nameLen]), "\x00")
}
if debug {
internal.Debug(name, raw.Mask, raw.Cookie)
internal.Debug(name, inEvent.Mask, inEvent.Cookie)
}
if mask&unix.IN_IGNORED != 0 { //&& event.Op != 0
next()
continue
if inEvent.Mask&unix.IN_IGNORED != 0 || inEvent.Mask&unix.IN_UNMOUNT != 0 {
w.watches.remove(watch)
return Event{}, true
}
// inotify will automatically remove the watch on deletes; just need
// to clean our state here.
if mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
w.watches.remove(watch.wd)
if inEvent.Mask&unix.IN_DELETE_SELF == unix.IN_DELETE_SELF {
w.watches.remove(watch)
}
// We can't really update the state when a watched path is moved;
// only IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove
// the watch.
if mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
if watch.recurse {
next() // Do nothing
continue
// We can't really update the state when a watched path is moved; only
// IN_MOVE_SELF is sent and not IN_MOVED_{FROM,TO}. So remove the watch.
if inEvent.Mask&unix.IN_MOVE_SELF == unix.IN_MOVE_SELF {
if watch.recurse { // Do nothing
return Event{}, true
}
err := w.remove(watch.path)
if err != nil && !errors.Is(err, ErrNonExistentWatch) {
if !w.sendError(err) {
return
return Event{}, false
}
}
}
/// Skip if we're watching both this path and the parent; the parent
/// will already send a delete so no need to do it twice.
if mask&unix.IN_DELETE_SELF != 0 {
if _, ok := w.watches.path[filepath.Dir(watch.path)]; ok {
next()
continue
/// Skip if we're watching both this path and the parent; the parent will
/// already send a delete so no need to do it twice.
if inEvent.Mask&unix.IN_DELETE_SELF != 0 {
_, ok := w.watches.path[filepath.Dir(watch.path)]
if ok {
return Event{}, true
}
}
ev := w.newEvent(name, mask, raw.Cookie)
ev := w.newEvent(name, inEvent.Mask, inEvent.Cookie)
// Need to update watch path for recurse.
if watch.recurse {
isDir := mask&unix.IN_ISDIR == unix.IN_ISDIR
isDir := inEvent.Mask&unix.IN_ISDIR == unix.IN_ISDIR
/// New directory created: set up watch on it.
if isDir && ev.Has(Create) {
err := w.register(ev.Name, watch.flags, true)
if !w.sendError(err) {
return
return Event{}, false
}
// This was a directory rename, so we need to update all
// the children.
// This was a directory rename, so we need to update all the
// children.
//
// TODO: this is of course pretty slow; we should use a
// better data structure for storing all of this, e.g. store
// children in the watch. I have some code for this in my
// kqueue refactor we can use in the future. For now I'm
// okay with this as it's not publicly available.
// Correctness first, performance second.
// TODO: this is of course pretty slow; we should use a better data
// structure for storing all of this, e.g. store children in the
// watch. I have some code for this in my kqueue refactor we can use
// in the future. For now I'm okay with this as it's not publicly
// available. Correctness first, performance second.
if ev.renamedFrom != "" {
w.watches.mu.Lock()
for k, ww := range w.watches.wd {
if k == watch.wd || ww.path == ev.Name {
continue
@@ -568,18 +500,11 @@ func (w *inotify) readEvents() {
w.watches.wd[k] = ww
}
}
w.watches.mu.Unlock()
}
}
}
/// Send the events that are not ignored on the events channel
if !w.sendEvent(ev) {
return
}
next()
}
}
return ev, true
}
func (w *inotify) isRecursive(path string) bool {
@@ -650,8 +575,8 @@ func (w *inotify) xSupports(op Op) bool {
}
func (w *inotify) state() {
w.watches.mu.Lock()
defer w.watches.mu.Unlock()
w.mu.Lock()
defer w.mu.Unlock()
for wd, ww := range w.watches.wd {
fmt.Fprintf(os.Stderr, "%4d: recurse=%t %q\n", wd, ww.recurse, ww.path)
}

View File

@@ -16,14 +16,13 @@ import (
)
type kqueue struct {
*shared
Events chan Event
Errors chan error
kq int // File descriptor (as returned by the kqueue() syscall).
closepipe [2]int // Pipe used for closing kq.
watches *watches
done chan struct{}
doneMu sync.Mutex
}
type (
@@ -132,14 +131,18 @@ func (w *watches) byPath(path string) (watch, bool) {
return info, ok
}
func (w *watches) updateDirFlags(path string, flags uint32) {
func (w *watches) updateDirFlags(path string, flags uint32) bool {
w.mu.Lock()
defer w.mu.Unlock()
fd := w.path[path]
fd, ok := w.path[path]
if !ok { // Already deleted: don't re-set it here.
return false
}
info := w.wd[fd]
info.dirFlags = flags
w.wd[fd] = info
return true
}
func (w *watches) remove(fd int, path string) bool {
@@ -179,22 +182,20 @@ func (w *watches) seenBefore(path string) bool {
return ok
}
func newBackend(ev chan Event, errs chan error) (backend, error) {
return newBufferedBackend(0, ev, errs)
}
var defaultBufferSize = 0
func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
func newBackend(ev chan Event, errs chan error) (backend, error) {
kq, closepipe, err := newKqueue()
if err != nil {
return nil, err
}
w := &kqueue{
shared: newShared(ev, errs),
Events: ev,
Errors: errs,
kq: kq,
closepipe: closepipe,
done: make(chan struct{}),
watches: newWatches(),
}
@@ -210,7 +211,7 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
// all.
func newKqueue() (kq int, closepipe [2]int, err error) {
kq, err = unix.Kqueue()
if kq == -1 {
if err != nil {
return kq, closepipe, err
}
@@ -239,54 +240,17 @@ func newKqueue() (kq int, closepipe [2]int, err error) {
return kq, closepipe, nil
}
// Returns true if the event was sent, or false if watcher is closed.
func (w *kqueue) sendEvent(e Event) bool {
select {
case <-w.done:
return false
case w.Events <- e:
return true
}
}
// Returns true if the error was sent, or false if watcher is closed.
func (w *kqueue) sendError(err error) bool {
if err == nil {
return true
}
select {
case <-w.done:
return false
case w.Errors <- err:
return true
}
}
func (w *kqueue) isClosed() bool {
select {
case <-w.done:
return true
default:
return false
}
}
func (w *kqueue) Close() error {
w.doneMu.Lock()
if w.isClosed() {
w.doneMu.Unlock()
if w.shared.close() {
return nil
}
close(w.done)
w.doneMu.Unlock()
pathsToRemove := w.watches.listPaths(false)
for _, name := range pathsToRemove {
w.Remove(name)
}
// Send "quit" message to the reader goroutine.
unix.Close(w.closepipe[1])
unix.Close(w.closepipe[1]) // Send "quit" message to readEvents
return nil
}
@@ -303,7 +267,7 @@ func (w *kqueue) AddWith(name string, opts ...addOpt) error {
return fmt.Errorf("%w: %s", xErrUnsupported, with.op)
}
_, err := w.addWatch(name, noteAllEvents)
_, err := w.addWatch(name, noteAllEvents, false)
if err != nil {
return err
}
@@ -366,7 +330,7 @@ const noteAllEvents = unix.NOTE_DELETE | unix.NOTE_WRITE | unix.NOTE_ATTRIB | un
// described in kevent(2).
//
// Returns the real path to the file which was added, with symlinks resolved.
func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
func (w *kqueue) addWatch(name string, flags uint32, listDir bool) (string, error) {
if w.isClosed() {
return "", ErrClosed
}
@@ -385,15 +349,15 @@ func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
return "", nil
}
// Follow symlinks.
if fi.Mode()&os.ModeSymlink == os.ModeSymlink {
// Follow symlinks, but only for paths added with Add(), and not paths
// we're adding from internalWatch from a listdir.
if !listDir && fi.Mode()&os.ModeSymlink == os.ModeSymlink {
link, err := os.Readlink(name)
if err != nil {
// Return nil because Linux can add unresolvable symlinks to the
// watch list without problems, so maintain consistency with
// that. There will be no file events for broken symlinks.
// TODO: more specific check; returns os.PathError; ENOENT?
return "", nil
return "", err
}
if !filepath.IsAbs(link) {
link = filepath.Join(filepath.Dir(name), link)
}
_, alreadyWatching = w.watches.byPath(link)
@@ -408,7 +372,7 @@ func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
name = link
fi, err = os.Lstat(name)
if err != nil {
return "", nil
return "", err
}
}
@@ -422,7 +386,6 @@ func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
if errors.Is(err, unix.EINTR) {
continue
}
return "", err
}
@@ -444,10 +407,16 @@ func (w *kqueue) addWatch(name string, flags uint32) (string, error) {
if info.isDir {
watchDir := (flags&unix.NOTE_WRITE) == unix.NOTE_WRITE &&
(!alreadyWatching || (info.dirFlags&unix.NOTE_WRITE) != unix.NOTE_WRITE)
w.watches.updateDirFlags(name, flags)
if !w.watches.updateDirFlags(name, flags) {
return "", nil
}
if watchDir {
if err := w.watchDirectoryFiles(name); err != nil {
d := name
if info.linkName != "" {
d = info.linkName
}
if err := w.watchDirectoryFiles(d); err != nil {
return "", err
}
}
@@ -644,19 +613,22 @@ func (w *kqueue) dirChange(dir string) error {
if errors.Is(err, os.ErrNotExist) {
return nil
}
return fmt.Errorf("fsnotify.dirChange: %w", err)
return fmt.Errorf("fsnotify.dirChange %q: %w", dir, err)
}
for _, f := range files {
fi, err := f.Info()
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil
}
return fmt.Errorf("fsnotify.dirChange: %w", err)
}
err = w.sendCreateIfNew(filepath.Join(dir, fi.Name()), fi)
if err != nil {
// Don't need to send an error if this file isn't readable.
if errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) {
if errors.Is(err, unix.EACCES) || errors.Is(err, unix.EPERM) || errors.Is(err, os.ErrNotExist) {
return nil
}
return fmt.Errorf("fsnotify.dirChange: %w", err)
@@ -688,11 +660,11 @@ func (w *kqueue) internalWatch(name string, fi os.FileInfo) (string, error) {
// mimic Linux providing delete events for subdirectories, but preserve
// the flags used if currently watching subdirectory
info, _ := w.watches.byPath(name)
return w.addWatch(name, info.dirFlags|unix.NOTE_DELETE|unix.NOTE_RENAME)
return w.addWatch(name, info.dirFlags|unix.NOTE_DELETE|unix.NOTE_RENAME, true)
}
// watch file to mimic Linux inotify
return w.addWatch(name, noteAllEvents)
// Watch file to mimic Linux inotify.
return w.addWatch(name, noteAllEvents, true)
}
// Register events with the queue.
@@ -722,9 +694,9 @@ func (w *kqueue) read(events []unix.Kevent_t) ([]unix.Kevent_t, error) {
}
func (w *kqueue) xSupports(op Op) bool {
if runtime.GOOS == "freebsd" {
//if runtime.GOOS == "freebsd" {
// return true // Supports everything.
}
//}
if op.Has(xUnportableOpen) || op.Has(xUnportableRead) ||
op.Has(xUnportableCloseWrite) || op.Has(xUnportableCloseRead) {
return false

View File

@@ -9,12 +9,11 @@ type other struct {
Errors chan error
}
var defaultBufferSize = 0
func newBackend(ev chan Event, errs chan error) (backend, error) {
return nil, errors.New("fsnotify not supported on the current platform")
}
func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
return newBackend(ev, errs)
}
func (w *other) Close() error { return nil }
func (w *other) WatchList() []string { return nil }
func (w *other) Add(name string) error { return nil }

View File

@@ -28,18 +28,16 @@ type readDirChangesW struct {
port windows.Handle // Handle to completion port
input chan *input // Inputs to the reader are sent on this channel
quit chan chan<- error
done chan chan<- error
mu sync.Mutex // Protects access to watches, closed
watches watchMap // Map of watches (key: i-number)
closed bool // Set to true when Close() is first called
}
func newBackend(ev chan Event, errs chan error) (backend, error) {
return newBufferedBackend(50, ev, errs)
}
var defaultBufferSize = 50
func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error) {
func newBackend(ev chan Event, errs chan error) (backend, error) {
port, err := windows.CreateIoCompletionPort(windows.InvalidHandle, 0, 0, 0)
if err != nil {
return nil, os.NewSyscallError("CreateIoCompletionPort", err)
@@ -50,7 +48,7 @@ func newBufferedBackend(sz uint, ev chan Event, errs chan error) (backend, error
port: port,
watches: make(watchMap),
input: make(chan *input, 1),
quit: make(chan chan<- error, 1),
done: make(chan chan<- error, 1),
}
go w.readEvents()
return w, nil
@@ -70,8 +68,8 @@ func (w *readDirChangesW) sendEvent(name, renamedFrom string, mask uint64) bool
event := w.newEvent(name, uint32(mask))
event.renamedFrom = renamedFrom
select {
case ch := <-w.quit:
w.quit <- ch
case ch := <-w.done:
w.done <- ch
case w.Events <- event:
}
return true
@@ -83,10 +81,10 @@ func (w *readDirChangesW) sendError(err error) bool {
return true
}
select {
case <-w.done:
return false
case w.Errors <- err:
return true
case <-w.quit:
return false
}
}
@@ -99,9 +97,9 @@ func (w *readDirChangesW) Close() error {
w.closed = true
w.mu.Unlock()
// Send "quit" message to the reader goroutine
// Send "done" message to the reader goroutine
ch := make(chan error)
w.quit <- ch
w.done <- ch
if err := w.wakeupReader(); err != nil {
return err
}
@@ -495,7 +493,7 @@ func (w *readDirChangesW) readEvents() {
watch := (*watch)(unsafe.Pointer(ov))
if watch == nil {
select {
case ch := <-w.quit:
case ch := <-w.done:
w.mu.Lock()
var indexes []indexMap
for _, index := range w.watches {

View File

@@ -244,12 +244,13 @@ var (
// ErrUnsupported is returned by AddWith() when WithOps() specified an
// Unportable event that's not supported on this platform.
//lint:ignore ST1012 not relevant
xErrUnsupported = errors.New("fsnotify: not supported with this backend")
)
// NewWatcher creates a new Watcher.
func NewWatcher() (*Watcher, error) {
ev, errs := make(chan Event), make(chan error)
ev, errs := make(chan Event, defaultBufferSize), make(chan error)
b, err := newBackend(ev, errs)
if err != nil {
return nil, err
@@ -266,8 +267,8 @@ func NewWatcher() (*Watcher, error) {
// cases, and whenever possible you will be better off increasing the kernel
// buffers instead of adding a large userspace buffer.
func NewBufferedWatcher(sz uint) (*Watcher, error) {
ev, errs := make(chan Event), make(chan error)
b, err := newBufferedBackend(sz, ev, errs)
ev, errs := make(chan Event, sz), make(chan error)
b, err := newBackend(ev, errs)
if err != nil {
return nil, err
}
@@ -337,7 +338,8 @@ func (w *Watcher) Close() error { return w.b.Close() }
// WatchList returns all paths explicitly added with [Watcher.Add] (and are not
// yet removed).
//
// Returns nil if [Watcher.Close] was called.
// The order is undefined, and may differ per call. Returns nil if
// [Watcher.Close] was called.
func (w *Watcher) WatchList() []string { return w.b.WatchList() }
// Supports reports if all the listed operations are supported by this platform.

View File

@@ -9,14 +9,14 @@ import (
)
var (
SyscallEACCES = syscall.EACCES
UnixEACCES = unix.EACCES
ErrSyscallEACCES = syscall.EACCES
ErrUnixEACCES = unix.EACCES
)
var maxfiles uint64
// Go 1.19 will do this automatically: https://go-review.googlesource.com/c/go/+/393354/
func SetRlimit() {
// Go 1.19 will do this automatically: https://go-review.googlesource.com/c/go/+/393354/
var l syscall.Rlimit
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &l)
if err == nil && l.Cur != l.Max {

View File

@@ -9,8 +9,8 @@ import (
)
var (
SyscallEACCES = syscall.EACCES
UnixEACCES = unix.EACCES
ErrSyscallEACCES = syscall.EACCES
ErrUnixEACCES = unix.EACCES
)
var maxfiles uint64

View File

@@ -1,4 +1,4 @@
//go:build !windows && !darwin && !freebsd
//go:build !windows && !darwin && !freebsd && !plan9
package internal
@@ -9,8 +9,8 @@ import (
)
var (
SyscallEACCES = syscall.EACCES
UnixEACCES = unix.EACCES
ErrSyscallEACCES = syscall.EACCES
ErrUnixEACCES = unix.EACCES
)
var maxfiles uint64

View File

@@ -10,8 +10,8 @@ import (
// Just a dummy.
var (
SyscallEACCES = errors.New("dummy")
UnixEACCES = errors.New("dummy")
ErrSyscallEACCES = errors.New("dummy")
ErrUnixEACCES = errors.New("dummy")
)
func SetRlimit() {}

64
vendor/github.com/fsnotify/fsnotify/shared.go generated vendored Normal file
View File

@@ -0,0 +1,64 @@
package fsnotify
import "sync"
type shared struct {
Events chan Event
Errors chan error
done chan struct{}
mu sync.Mutex
}
func newShared(ev chan Event, errs chan error) *shared {
return &shared{
Events: ev,
Errors: errs,
done: make(chan struct{}),
}
}
// Returns true if the event was sent, or false if watcher is closed.
func (w *shared) sendEvent(e Event) bool {
if e.Op == 0 {
return true
}
select {
case <-w.done:
return false
case w.Events <- e:
return true
}
}
// Returns true if the error was sent, or false if watcher is closed.
func (w *shared) sendError(err error) bool {
if err == nil {
return true
}
select {
case <-w.done:
return false
case w.Errors <- err:
return true
}
}
func (w *shared) isClosed() bool {
select {
case <-w.done:
return true
default:
return false
}
}
// Mark as closed; returns true if it was already closed.
func (w *shared) close() bool {
w.mu.Lock()
defer w.mu.Unlock()
if w.isClosed() {
return true
}
close(w.done)
return false
}

3
vendor/github.com/fsnotify/fsnotify/staticcheck.conf generated vendored Normal file
View File

@@ -0,0 +1,3 @@
checks = ['all',
'-U1000', # Don't complain about unused functions.
]

View File

@@ -0,0 +1,5 @@
version: "2"
linters:
exclusions:
presets:
- std-error-handling

View File

@@ -1,567 +0,0 @@
// 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.
// Package json provides a JSON value parser state machine.
// This package is almost entirely copied from the Go stdlib.
// Changes made to it permit users of the package to tell
// if some slice of bytes is a valid beginning of a json string.
package json
import (
"fmt"
"sync"
)
type (
scanStatus int
)
const (
parseObjectKey = iota // parsing object key (before colon)
parseObjectValue // parsing object value (after colon)
parseArrayValue // parsing array value
scanContinue scanStatus = iota // uninteresting byte
scanBeginLiteral // end implied by next result != scanContinue
scanBeginObject // begin object
scanObjectKey // just finished object key (string)
scanObjectValue // just finished non-last object value
scanEndObject // end object (implies scanObjectValue if possible)
scanBeginArray // begin array
scanArrayValue // just finished array value
scanEndArray // end array (implies scanArrayValue if possible)
scanSkipSpace // space byte; can skip; known to be last "continue" result
scanEnd // top-level value ended *before* this byte; known to be first "stop" result
scanError // hit an error, scanner.err.
// This limits the max nesting depth to prevent stack overflow.
// This is permitted by https://tools.ietf.org/html/rfc7159#section-9
maxNestingDepth = 10000
)
type (
scanner struct {
step func(*scanner, byte) scanStatus
parseState []int
endTop bool
err error
index int
}
)
var scannerPool = sync.Pool{
New: func() any {
return &scanner{}
},
}
func newScanner() *scanner {
s := scannerPool.Get().(*scanner)
s.reset()
return s
}
func freeScanner(s *scanner) {
// Avoid hanging on to too much memory in extreme cases.
if len(s.parseState) > 1024 {
s.parseState = nil
}
scannerPool.Put(s)
}
// Scan returns the number of bytes scanned and if there was any error
// in trying to reach the end of data.
func Scan(data []byte) (int, error) {
s := newScanner()
defer freeScanner(s)
_ = checkValid(data, s)
return s.index, s.err
}
// checkValid verifies that data is valid JSON-encoded data.
// scan is passed in for use by checkValid to avoid an allocation.
func checkValid(data []byte, scan *scanner) error {
for _, c := range data {
scan.index++
if scan.step(scan, c) == scanError {
return scan.err
}
}
if scan.eof() == scanError {
return scan.err
}
return nil
}
func isSpace(c byte) bool {
return c == ' ' || c == '\t' || c == '\r' || c == '\n'
}
func (s *scanner) reset() {
s.step = stateBeginValue
s.parseState = s.parseState[0:0]
s.err = nil
s.endTop = false
s.index = 0
}
// eof tells the scanner that the end of input has been reached.
// It returns a scan status just as s.step does.
func (s *scanner) eof() scanStatus {
if s.err != nil {
return scanError
}
if s.endTop {
return scanEnd
}
s.step(s, ' ')
if s.endTop {
return scanEnd
}
if s.err == nil {
s.err = fmt.Errorf("unexpected end of JSON input")
}
return scanError
}
// pushParseState pushes a new parse state p onto the parse stack.
// an error state is returned if maxNestingDepth was exceeded, otherwise successState is returned.
func (s *scanner) pushParseState(c byte, newParseState int, successState scanStatus) scanStatus {
s.parseState = append(s.parseState, newParseState)
if len(s.parseState) <= maxNestingDepth {
return successState
}
return s.error(c, "exceeded max depth")
}
// popParseState pops a parse state (already obtained) off the stack
// and updates s.step accordingly.
func (s *scanner) popParseState() {
n := len(s.parseState) - 1
s.parseState = s.parseState[0:n]
if n == 0 {
s.step = stateEndTop
s.endTop = true
} else {
s.step = stateEndValue
}
}
// stateBeginValueOrEmpty is the state after reading `[`.
func stateBeginValueOrEmpty(s *scanner, c byte) scanStatus {
if c <= ' ' && isSpace(c) {
return scanSkipSpace
}
if c == ']' {
return stateEndValue(s, c)
}
return stateBeginValue(s, c)
}
// stateBeginValue is the state at the beginning of the input.
func stateBeginValue(s *scanner, c byte) scanStatus {
if c <= ' ' && isSpace(c) {
return scanSkipSpace
}
switch c {
case '{':
s.step = stateBeginStringOrEmpty
return s.pushParseState(c, parseObjectKey, scanBeginObject)
case '[':
s.step = stateBeginValueOrEmpty
return s.pushParseState(c, parseArrayValue, scanBeginArray)
case '"':
s.step = stateInString
return scanBeginLiteral
case '-':
s.step = stateNeg
return scanBeginLiteral
case '0': // beginning of 0.123
s.step = state0
return scanBeginLiteral
case 't': // beginning of true
s.step = stateT
return scanBeginLiteral
case 'f': // beginning of false
s.step = stateF
return scanBeginLiteral
case 'n': // beginning of null
s.step = stateN
return scanBeginLiteral
}
if '1' <= c && c <= '9' { // beginning of 1234.5
s.step = state1
return scanBeginLiteral
}
return s.error(c, "looking for beginning of value")
}
// stateBeginStringOrEmpty is the state after reading `{`.
func stateBeginStringOrEmpty(s *scanner, c byte) scanStatus {
if c <= ' ' && isSpace(c) {
return scanSkipSpace
}
if c == '}' {
n := len(s.parseState)
s.parseState[n-1] = parseObjectValue
return stateEndValue(s, c)
}
return stateBeginString(s, c)
}
// stateBeginString is the state after reading `{"key": value,`.
func stateBeginString(s *scanner, c byte) scanStatus {
if c <= ' ' && isSpace(c) {
return scanSkipSpace
}
if c == '"' {
s.step = stateInString
return scanBeginLiteral
}
return s.error(c, "looking for beginning of object key string")
}
// stateEndValue is the state after completing a value,
// such as after reading `{}` or `true` or `["x"`.
func stateEndValue(s *scanner, c byte) scanStatus {
n := len(s.parseState)
if n == 0 {
// Completed top-level before the current byte.
s.step = stateEndTop
s.endTop = true
return stateEndTop(s, c)
}
if c <= ' ' && isSpace(c) {
s.step = stateEndValue
return scanSkipSpace
}
ps := s.parseState[n-1]
switch ps {
case parseObjectKey:
if c == ':' {
s.parseState[n-1] = parseObjectValue
s.step = stateBeginValue
return scanObjectKey
}
return s.error(c, "after object key")
case parseObjectValue:
if c == ',' {
s.parseState[n-1] = parseObjectKey
s.step = stateBeginString
return scanObjectValue
}
if c == '}' {
s.popParseState()
return scanEndObject
}
return s.error(c, "after object key:value pair")
case parseArrayValue:
if c == ',' {
s.step = stateBeginValue
return scanArrayValue
}
if c == ']' {
s.popParseState()
return scanEndArray
}
return s.error(c, "after array element")
}
return s.error(c, "")
}
// stateEndTop is the state after finishing the top-level value,
// such as after reading `{}` or `[1,2,3]`.
// Only space characters should be seen now.
func stateEndTop(s *scanner, c byte) scanStatus {
if c != ' ' && c != '\t' && c != '\r' && c != '\n' {
// Complain about non-space byte on next call.
s.error(c, "after top-level value")
}
return scanEnd
}
// stateInString is the state after reading `"`.
func stateInString(s *scanner, c byte) scanStatus {
if c == '"' {
s.step = stateEndValue
return scanContinue
}
if c == '\\' {
s.step = stateInStringEsc
return scanContinue
}
if c < 0x20 {
return s.error(c, "in string literal")
}
return scanContinue
}
// stateInStringEsc is the state after reading `"\` during a quoted string.
func stateInStringEsc(s *scanner, c byte) scanStatus {
switch c {
case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
s.step = stateInString
return scanContinue
case 'u':
s.step = stateInStringEscU
return scanContinue
}
return s.error(c, "in string escape code")
}
// stateInStringEscU is the state after reading `"\u` during a quoted string.
func stateInStringEscU(s *scanner, c byte) scanStatus {
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
s.step = stateInStringEscU1
return scanContinue
}
// numbers
return s.error(c, "in \\u hexadecimal character escape")
}
// stateInStringEscU1 is the state after reading `"\u1` during a quoted string.
func stateInStringEscU1(s *scanner, c byte) scanStatus {
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
s.step = stateInStringEscU12
return scanContinue
}
// numbers
return s.error(c, "in \\u hexadecimal character escape")
}
// stateInStringEscU12 is the state after reading `"\u12` during a quoted string.
func stateInStringEscU12(s *scanner, c byte) scanStatus {
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
s.step = stateInStringEscU123
return scanContinue
}
// numbers
return s.error(c, "in \\u hexadecimal character escape")
}
// stateInStringEscU123 is the state after reading `"\u123` during a quoted string.
func stateInStringEscU123(s *scanner, c byte) scanStatus {
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
s.step = stateInString
return scanContinue
}
// numbers
return s.error(c, "in \\u hexadecimal character escape")
}
// stateNeg is the state after reading `-` during a number.
func stateNeg(s *scanner, c byte) scanStatus {
if c == '0' {
s.step = state0
return scanContinue
}
if '1' <= c && c <= '9' {
s.step = state1
return scanContinue
}
return s.error(c, "in numeric literal")
}
// state1 is the state after reading a non-zero integer during a number,
// such as after reading `1` or `100` but not `0`.
func state1(s *scanner, c byte) scanStatus {
if '0' <= c && c <= '9' {
s.step = state1
return scanContinue
}
return state0(s, c)
}
// state0 is the state after reading `0` during a number.
func state0(s *scanner, c byte) scanStatus {
if c == '.' {
s.step = stateDot
return scanContinue
}
if c == 'e' || c == 'E' {
s.step = stateE
return scanContinue
}
return stateEndValue(s, c)
}
// stateDot is the state after reading the integer and decimal point in a number,
// such as after reading `1.`.
func stateDot(s *scanner, c byte) scanStatus {
if '0' <= c && c <= '9' {
s.step = stateDot0
return scanContinue
}
return s.error(c, "after decimal point in numeric literal")
}
// stateDot0 is the state after reading the integer, decimal point, and subsequent
// digits of a number, such as after reading `3.14`.
func stateDot0(s *scanner, c byte) scanStatus {
if '0' <= c && c <= '9' {
return scanContinue
}
if c == 'e' || c == 'E' {
s.step = stateE
return scanContinue
}
return stateEndValue(s, c)
}
// stateE is the state after reading the mantissa and e in a number,
// such as after reading `314e` or `0.314e`.
func stateE(s *scanner, c byte) scanStatus {
if c == '+' || c == '-' {
s.step = stateESign
return scanContinue
}
return stateESign(s, c)
}
// stateESign is the state after reading the mantissa, e, and sign in a number,
// such as after reading `314e-` or `0.314e+`.
func stateESign(s *scanner, c byte) scanStatus {
if '0' <= c && c <= '9' {
s.step = stateE0
return scanContinue
}
return s.error(c, "in exponent of numeric literal")
}
// stateE0 is the state after reading the mantissa, e, optional sign,
// and at least one digit of the exponent in a number,
// such as after reading `314e-2` or `0.314e+1` or `3.14e0`.
func stateE0(s *scanner, c byte) scanStatus {
if '0' <= c && c <= '9' {
return scanContinue
}
return stateEndValue(s, c)
}
// stateT is the state after reading `t`.
func stateT(s *scanner, c byte) scanStatus {
if c == 'r' {
s.step = stateTr
return scanContinue
}
return s.error(c, "in literal true (expecting 'r')")
}
// stateTr is the state after reading `tr`.
func stateTr(s *scanner, c byte) scanStatus {
if c == 'u' {
s.step = stateTru
return scanContinue
}
return s.error(c, "in literal true (expecting 'u')")
}
// stateTru is the state after reading `tru`.
func stateTru(s *scanner, c byte) scanStatus {
if c == 'e' {
s.step = stateEndValue
return scanContinue
}
return s.error(c, "in literal true (expecting 'e')")
}
// stateF is the state after reading `f`.
func stateF(s *scanner, c byte) scanStatus {
if c == 'a' {
s.step = stateFa
return scanContinue
}
return s.error(c, "in literal false (expecting 'a')")
}
// stateFa is the state after reading `fa`.
func stateFa(s *scanner, c byte) scanStatus {
if c == 'l' {
s.step = stateFal
return scanContinue
}
return s.error(c, "in literal false (expecting 'l')")
}
// stateFal is the state after reading `fal`.
func stateFal(s *scanner, c byte) scanStatus {
if c == 's' {
s.step = stateFals
return scanContinue
}
return s.error(c, "in literal false (expecting 's')")
}
// stateFals is the state after reading `fals`.
func stateFals(s *scanner, c byte) scanStatus {
if c == 'e' {
s.step = stateEndValue
return scanContinue
}
return s.error(c, "in literal false (expecting 'e')")
}
// stateN is the state after reading `n`.
func stateN(s *scanner, c byte) scanStatus {
if c == 'u' {
s.step = stateNu
return scanContinue
}
return s.error(c, "in literal null (expecting 'u')")
}
// stateNu is the state after reading `nu`.
func stateNu(s *scanner, c byte) scanStatus {
if c == 'l' {
s.step = stateNul
return scanContinue
}
return s.error(c, "in literal null (expecting 'l')")
}
// stateNul is the state after reading `nul`.
func stateNul(s *scanner, c byte) scanStatus {
if c == 'l' {
s.step = stateEndValue
return scanContinue
}
return s.error(c, "in literal null (expecting 'l')")
}
// stateError is the state after reaching a syntax error,
// such as after reading `[1}` or `5.1.2`.
func stateError(s *scanner, c byte) scanStatus {
return scanError
}
// error records an error and switches to the error state.
func (s *scanner) error(c byte, context string) scanStatus {
s.step = stateError
s.err = fmt.Errorf("invalid character <<%c>> %s", c, context)
return scanError
}

View File

@@ -0,0 +1,464 @@
package json
import (
"bytes"
"sync"
)
const (
QueryNone = "json"
QueryGeo = "geo"
QueryHAR = "har"
QueryGLTF = "gltf"
maxRecursion = 4096
)
var queries = map[string][]query{
QueryNone: nil,
QueryGeo: {{
SearchPath: [][]byte{[]byte("type")},
SearchVals: [][]byte{
[]byte(`"Feature"`),
[]byte(`"FeatureCollection"`),
[]byte(`"Point"`),
[]byte(`"LineString"`),
[]byte(`"Polygon"`),
[]byte(`"MultiPoint"`),
[]byte(`"MultiLineString"`),
[]byte(`"MultiPolygon"`),
[]byte(`"GeometryCollection"`),
},
}},
QueryHAR: {{
SearchPath: [][]byte{[]byte("log"), []byte("version")},
}, {
SearchPath: [][]byte{[]byte("log"), []byte("creator")},
}, {
SearchPath: [][]byte{[]byte("log"), []byte("entries")},
}},
QueryGLTF: {{
SearchPath: [][]byte{[]byte("asset"), []byte("version")},
SearchVals: [][]byte{[]byte(`"1.0"`), []byte(`"2.0"`)},
}},
}
var parserPool = sync.Pool{
New: func() any {
return &parserState{maxRecursion: maxRecursion}
},
}
// parserState holds the state of JSON parsing. The number of inspected bytes,
// the current path inside the JSON object, etc.
type parserState struct {
// ib represents the number of inspected bytes.
// Because mimetype limits itself to only reading the header of the file,
// it means sometimes the input JSON can be truncated. In that case, we want
// to still detect it as JSON, even if it's invalid/truncated.
// When ib == len(input) it means the JSON was valid (at least the header).
ib int
maxRecursion int
// currPath keeps a track of the JSON keys parsed up.
// It works only for JSON objects. JSON arrays are ignored
// mainly because the functionality is not needed.
currPath [][]byte
// firstToken stores the first JSON token encountered in input.
// TODO: performance would be better if we would stop parsing as soon
// as we see that first token is not what we are interested in.
firstToken int
// querySatisfied is true if both path and value of any queries passed to
// consumeAny are satisfied.
querySatisfied bool
}
// query holds information about a combination of {"key": "val"} that we're trying
// to search for inside the JSON.
type query struct {
// SearchPath represents the whole path to look for inside the JSON.
// ex: [][]byte{[]byte("foo"), []byte("bar")} matches {"foo": {"bar": "baz"}}
SearchPath [][]byte
// SearchVals represents values to look for when the SearchPath is found.
// Each SearchVal element is tried until one of them matches (logical OR.)
SearchVals [][]byte
}
func eq(path1, path2 [][]byte) bool {
if len(path1) != len(path2) {
return false
}
for i := range path1 {
if !bytes.Equal(path1[i], path2[i]) {
return false
}
}
return true
}
// LooksLikeObjectOrArray reports if first non white space character from raw
// is either { or [. Parsing raw as JSON is a heavy operation. When receiving some
// text input we can skip parsing if the input does not even look like JSON.
func LooksLikeObjectOrArray(raw []byte) bool {
for i := range raw {
if isSpace(raw[i]) {
continue
}
return raw[i] == '{' || raw[i] == '['
}
return false
}
// Parse will take out a parser from the pool depending on queryType and tries
// to parse raw bytes as JSON.
func Parse(queryType string, raw []byte) (parsed, inspected, firstToken int, querySatisfied bool) {
p := parserPool.Get().(*parserState)
defer func() {
// Avoid hanging on to too much memory in extreme input cases.
if len(p.currPath) > 128 {
p.currPath = nil
}
parserPool.Put(p)
}()
p.reset()
qs := queries[queryType]
got := p.consumeAny(raw, qs, 0)
return got, p.ib, p.firstToken, p.querySatisfied
}
func (p *parserState) reset() {
p.ib = 0
p.currPath = p.currPath[0:0]
p.firstToken = TokInvalid
p.querySatisfied = false
}
func (p *parserState) consumeSpace(b []byte) (n int) {
for len(b) > 0 && isSpace(b[0]) {
b = b[1:]
n++
p.ib++
}
return n
}
func (p *parserState) consumeConst(b, cnst []byte) int {
lb := len(b)
for i, c := range cnst {
if lb > i && b[i] == c {
p.ib++
} else {
return 0
}
}
return len(cnst)
}
func (p *parserState) consumeString(b []byte) (n int) {
var c byte
for len(b[n:]) > 0 {
c, n = b[n], n+1
p.ib++
switch c {
case '\\':
if len(b[n:]) == 0 {
return 0
}
switch b[n] {
case '"', '\\', '/', 'b', 'f', 'n', 'r', 't':
n++
p.ib++
continue
case 'u':
n++
p.ib++
for j := 0; j < 4 && len(b[n:]) > 0; j++ {
if !isXDigit(b[n]) {
return 0
}
n++
p.ib++
}
continue
default:
return 0
}
case '"':
return n
default:
continue
}
}
return 0
}
func (p *parserState) consumeNumber(b []byte) (n int) {
got := false
var i int
if len(b) == 0 {
goto out
}
if b[0] == '-' {
b, i = b[1:], i+1
p.ib++
}
for len(b) > 0 {
if !isDigit(b[0]) {
break
}
got = true
b, i = b[1:], i+1
p.ib++
}
if len(b) == 0 {
goto out
}
if b[0] == '.' {
b, i = b[1:], i+1
p.ib++
}
for len(b) > 0 {
if !isDigit(b[0]) {
break
}
got = true
b, i = b[1:], i+1
p.ib++
}
if len(b) == 0 {
goto out
}
if got && (b[0] == 'e' || b[0] == 'E') {
b, i = b[1:], i+1
p.ib++
got = false
if len(b) == 0 {
goto out
}
if b[0] == '+' || b[0] == '-' {
b, i = b[1:], i+1
p.ib++
}
for len(b) > 0 {
if !isDigit(b[0]) {
break
}
got = true
b, i = b[1:], i+1
p.ib++
}
}
out:
if got {
return i
}
return 0
}
func (p *parserState) consumeArray(b []byte, qs []query, lvl int) (n int) {
p.currPath = append(p.currPath, []byte{'['})
if len(b) == 0 {
return 0
}
for n < len(b) {
n += p.consumeSpace(b[n:])
if len(b[n:]) == 0 {
return 0
}
if b[n] == ']' {
p.ib++
p.currPath = p.currPath[:len(p.currPath)-1]
return n + 1
}
innerParsed := p.consumeAny(b[n:], qs, lvl)
if innerParsed == 0 {
return 0
}
n += innerParsed
if len(b[n:]) == 0 {
return 0
}
switch b[n] {
case ',':
n += 1
p.ib++
continue
case ']':
p.ib++
return n + 1
default:
return 0
}
}
return 0
}
func queryPathMatch(qs []query, path [][]byte) int {
for i := range qs {
if eq(qs[i].SearchPath, path) {
return i
}
}
return -1
}
func (p *parserState) consumeObject(b []byte, qs []query, lvl int) (n int) {
for n < len(b) {
n += p.consumeSpace(b[n:])
if len(b[n:]) == 0 {
return 0
}
if b[n] == '}' {
p.ib++
return n + 1
}
if b[n] != '"' {
return 0
} else {
n += 1
p.ib++
}
// queryMatched stores the index of the query satisfying the current path.
queryMatched := -1
if keyLen := p.consumeString(b[n:]); keyLen == 0 {
return 0
} else {
p.currPath = append(p.currPath, b[n:n+keyLen-1])
if !p.querySatisfied {
queryMatched = queryPathMatch(qs, p.currPath)
}
n += keyLen
}
n += p.consumeSpace(b[n:])
if len(b[n:]) == 0 {
return 0
}
if b[n] != ':' {
return 0
} else {
n += 1
p.ib++
}
n += p.consumeSpace(b[n:])
if len(b[n:]) == 0 {
return 0
}
if valLen := p.consumeAny(b[n:], qs, lvl); valLen == 0 {
return 0
} else {
if queryMatched != -1 {
q := qs[queryMatched]
if len(q.SearchVals) == 0 {
p.querySatisfied = true
}
for _, val := range q.SearchVals {
if bytes.Equal(val, bytes.TrimSpace(b[n:n+valLen])) {
p.querySatisfied = true
}
}
}
n += valLen
}
if len(b[n:]) == 0 {
return 0
}
switch b[n] {
case ',':
p.currPath = p.currPath[:len(p.currPath)-1]
n++
p.ib++
continue
case '}':
p.currPath = p.currPath[:len(p.currPath)-1]
p.ib++
return n + 1
default:
return 0
}
}
return 0
}
func (p *parserState) consumeAny(b []byte, qs []query, lvl int) (n int) {
// Avoid too much recursion.
if p.maxRecursion != 0 && lvl > p.maxRecursion {
return 0
}
n += p.consumeSpace(b)
if len(b[n:]) == 0 {
return 0
}
var t, rv int
switch b[n] {
case '"':
n++
p.ib++
rv = p.consumeString(b[n:])
t = TokString
case '[':
n++
p.ib++
rv = p.consumeArray(b[n:], qs, lvl+1)
t = TokArray
case '{':
n++
p.ib++
rv = p.consumeObject(b[n:], qs, lvl+1)
t = TokObject
case 't':
rv = p.consumeConst(b[n:], []byte("true"))
t = TokTrue
case 'f':
rv = p.consumeConst(b[n:], []byte("false"))
t = TokFalse
case 'n':
rv = p.consumeConst(b[n:], []byte("null"))
t = TokNull
default:
rv = p.consumeNumber(b[n:])
t = TokNumber
}
if lvl == 0 {
p.firstToken = t
}
if len(qs) == 0 {
p.querySatisfied = true
}
if rv <= 0 {
return n
}
n += rv
n += p.consumeSpace(b[n:])
return n
}
func isSpace(c byte) bool {
return c == ' ' || c == '\t' || c == '\r' || c == '\n'
}
func isDigit(c byte) bool {
return '0' <= c && c <= '9'
}
func isXDigit(c byte) bool {
if isDigit(c) {
return true
}
return ('a' <= c && c <= 'f') || ('A' <= c && c <= 'F')
}
const (
TokInvalid = 0
TokNull = 1 << iota
TokTrue
TokFalse
TokNumber
TokString
TokArray
TokObject
TokComma
)

View File

@@ -137,7 +137,7 @@ func tarParseOctal(b []byte) int64 {
if b == 0 {
break
}
if !(b >= '0' && b <= '7') {
if b < '0' || b > '7' {
return -1
}
ret = (ret << 3) | int64(b-'0')

View File

@@ -71,7 +71,7 @@ func Dbf(raw []byte, limit uint32) bool {
}
// 3rd and 4th bytes contain the last update month and day of month.
if !(0 < raw[2] && raw[2] < 13 && 0 < raw[3] && raw[3] < 32) {
if raw[2] == 0 || raw[2] > 12 || raw[3] == 0 || raw[3] > 31 {
return false
}
@@ -153,7 +153,7 @@ func Marc(raw []byte, limit uint32) bool {
return bytes.Contains(raw[:min(2048, len(raw))], []byte{0x1E})
}
// Glb matches a glTF model format file.
// GLB matches a glTF model format file.
// GLB is the binary file format representation of 3D models saved in
// the GL transmission Format (glTF).
// GLB uses little endian and its header structure is as follows:
@@ -168,7 +168,7 @@ func Marc(raw []byte, limit uint32) bool {
//
// [glTF specification]: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html
// [IANA glTF entry]: https://www.iana.org/assignments/media-types/model/gltf-binary
var Glb = prefix([]byte("\x67\x6C\x54\x46\x02\x00\x00\x00"),
var GLB = prefix([]byte("\x67\x6C\x54\x46\x02\x00\x00\x00"),
[]byte("\x67\x6C\x54\x46\x01\x00\x00\x00"))
// TzIf matches a Time Zone Information Format (TZif) file.

View File

@@ -12,13 +12,13 @@ func Shp(raw []byte, limit uint32) bool {
return false
}
if !(binary.BigEndian.Uint32(raw[0:4]) == 9994 &&
binary.BigEndian.Uint32(raw[4:8]) == 0 &&
binary.BigEndian.Uint32(raw[8:12]) == 0 &&
binary.BigEndian.Uint32(raw[12:16]) == 0 &&
binary.BigEndian.Uint32(raw[16:20]) == 0 &&
binary.BigEndian.Uint32(raw[20:24]) == 0 &&
binary.LittleEndian.Uint32(raw[28:32]) == 1000) {
if binary.BigEndian.Uint32(raw[0:4]) != 9994 ||
binary.BigEndian.Uint32(raw[4:8]) != 0 ||
binary.BigEndian.Uint32(raw[8:12]) != 0 ||
binary.BigEndian.Uint32(raw[12:16]) != 0 ||
binary.BigEndian.Uint32(raw[16:20]) != 0 ||
binary.BigEndian.Uint32(raw[20:24]) != 0 ||
binary.LittleEndian.Uint32(raw[28:32]) != 1000 {
return false
}

View File

@@ -2,7 +2,6 @@ package magic
import (
"bytes"
"strings"
"time"
"github.com/gabriel-vasile/mimetype/internal/charset"
@@ -154,145 +153,75 @@ func Php(raw []byte, limit uint32) bool {
// JSON matches a JavaScript Object Notation file.
func JSON(raw []byte, limit uint32) bool {
raw = trimLWS(raw)
// #175 A single JSON string, number or bool is not considered JSON.
// JSON objects and arrays are reported as JSON.
if len(raw) < 2 || (raw[0] != '[' && raw[0] != '{') {
return false
}
parsed, err := json.Scan(raw)
// If the full file content was provided, check there is no error.
if limit == 0 || len(raw) < int(limit) {
return err == nil
}
// If a section of the file was provided, check if all of it was parsed.
return parsed == len(raw) && len(raw) > 0
return jsonHelper(raw, limit, json.QueryNone, json.TokObject|json.TokArray)
}
// GeoJSON matches a RFC 7946 GeoJSON file.
//
// GeoJSON detection implies searching for key:value pairs like: `"type": "Feature"`
// in the input.
// BUG(gabriel-vasile): The "type" key should be searched for in the root object.
func GeoJSON(raw []byte, limit uint32) bool {
raw = trimLWS(raw)
if len(raw) == 0 {
return jsonHelper(raw, limit, json.QueryGeo, json.TokObject)
}
// HAR matches a HAR Spec file.
// Spec: http://www.softwareishard.com/blog/har-12-spec/
func HAR(raw []byte, limit uint32) bool {
return jsonHelper(raw, limit, json.QueryHAR, json.TokObject)
}
// GLTF matches a GL Transmission Format (JSON) file.
// Visit [glTF specification] and [IANA glTF entry] for more details.
//
// [glTF specification]: https://registry.khronos.org/glTF/specs/2.0/glTF-2.0.html
// [IANA glTF entry]: https://www.iana.org/assignments/media-types/model/gltf+json
func GLTF(raw []byte, limit uint32) bool {
return jsonHelper(raw, limit, json.QueryGLTF, json.TokObject)
}
func jsonHelper(raw []byte, limit uint32, q string, wantTok int) bool {
if !json.LooksLikeObjectOrArray(raw) {
return false
}
// GeoJSON is always a JSON object, not a JSON array or any other JSON value.
if raw[0] != '{' {
lraw := len(raw)
parsed, inspected, firstToken, querySatisfied := json.Parse(q, raw)
if !querySatisfied || firstToken&wantTok == 0 {
return false
}
s := []byte(`"type"`)
si, sl := bytes.Index(raw, s), len(s)
if si == -1 {
return false
// If the full file content was provided, check that the whole input was parsed.
if limit == 0 || lraw < int(limit) {
return parsed == lraw
}
// If the "type" string is the suffix of the input,
// there is no need to search for the value of the key.
if si+sl == len(raw) {
return false
}
// Skip the "type" part.
raw = raw[si+sl:]
// Skip any whitespace before the colon.
raw = trimLWS(raw)
// Check for colon.
if len(raw) == 0 || raw[0] != ':' {
return false
}
// Skip any whitespace after the colon.
raw = trimLWS(raw[1:])
geoJSONTypes := [][]byte{
[]byte(`"Feature"`),
[]byte(`"FeatureCollection"`),
[]byte(`"Point"`),
[]byte(`"LineString"`),
[]byte(`"Polygon"`),
[]byte(`"MultiPoint"`),
[]byte(`"MultiLineString"`),
[]byte(`"MultiPolygon"`),
[]byte(`"GeometryCollection"`),
}
for _, t := range geoJSONTypes {
if bytes.HasPrefix(raw, t) {
return true
}
}
return false
// If a section of the file was provided, check if all of it was inspected.
// In other words, check that if there was a problem parsing, that problem
// occured at the last byte in the input.
return inspected == lraw && lraw > 0
}
// NdJSON matches a Newline delimited JSON file. All complete lines from raw
// must be valid JSON documents meaning they contain one of the valid JSON data
// types.
func NdJSON(raw []byte, limit uint32) bool {
lCount, hasObjOrArr := 0, false
lCount, objOrArr := 0, 0
raw = dropLastLine(raw, limit)
var l []byte
for len(raw) != 0 {
l, raw = scanLine(raw)
// Empty lines are allowed in NDJSON.
if l = trimRWS(trimLWS(l)); len(l) == 0 {
continue
}
_, err := json.Scan(l)
if err != nil {
_, inspected, firstToken, _ := json.Parse(json.QueryNone, l)
if len(l) != inspected {
return false
}
if l[0] == '[' || l[0] == '{' {
hasObjOrArr = true
if firstToken == json.TokArray || firstToken == json.TokObject {
objOrArr++
}
lCount++
}
return lCount > 1 && hasObjOrArr
}
// HAR matches a HAR Spec file.
// Spec: http://www.softwareishard.com/blog/har-12-spec/
func HAR(raw []byte, limit uint32) bool {
s := []byte(`"log"`)
si, sl := bytes.Index(raw, s), len(s)
if si == -1 {
return false
}
// If the "log" string is the suffix of the input,
// there is no need to search for the value of the key.
if si+sl == len(raw) {
return false
}
// Skip the "log" part.
raw = raw[si+sl:]
// Skip any whitespace before the colon.
raw = trimLWS(raw)
// Check for colon.
if len(raw) == 0 || raw[0] != ':' {
return false
}
// Skip any whitespace after the colon.
raw = trimLWS(raw[1:])
harJSONTypes := [][]byte{
[]byte(`"version"`),
[]byte(`"creator"`),
[]byte(`"entries"`),
}
for _, t := range harJSONTypes {
si := bytes.Index(raw, t)
if si > -1 {
return true
}
}
return false
return lCount > 1 && objOrArr > 0
}
// Svg matches a SVG file.
@@ -305,32 +234,31 @@ func Srt(raw []byte, _ uint32) bool {
line, raw := scanLine(raw)
// First line must be 1.
if string(line) != "1" {
if len(line) != 1 || line[0] != '1' {
return false
}
line, raw = scanLine(raw)
secondLine := string(line)
// Timestamp format (e.g: 00:02:16,612 --> 00:02:19,376) limits secondLine
// Timestamp format (e.g: 00:02:16,612 --> 00:02:19,376) limits second line
// length to exactly 29 characters.
if len(secondLine) != 29 {
if len(line) != 29 {
return false
}
// Decimal separator of fractional seconds in the timestamps must be a
// comma, not a period.
if strings.Contains(secondLine, ".") {
if bytes.IndexByte(line, '.') != -1 {
return false
}
// Second line must be a time range.
ts := strings.Split(secondLine, " --> ")
if len(ts) != 2 {
sep := []byte(" --> ")
i := bytes.Index(line, sep)
if i == -1 {
return false
}
const layout = "15:04:05,000"
t0, err := time.Parse(layout, ts[0])
t0, err := time.Parse(layout, string(line[:i]))
if err != nil {
return false
}
t1, err := time.Parse(layout, ts[1])
t1, err := time.Parse(layout, string(line[i+len(sep):]))
if err != nil {
return false
}

View File

@@ -1,4 +1,4 @@
## 178 Supported MIME types
## 179 Supported MIME types
This file is automatically generated when running tests. Do not edit manually.
Extension | MIME type | Aliases
@@ -171,6 +171,7 @@ Extension | MIME type | Aliases
**.json** | application/json | -
**.geojson** | application/geo+json | -
**.har** | application/json | -
**.gltf** | model/gltf+json | -
**.ndjson** | application/x-ndjson | -
**.rtf** | text/rtf | application/rtf
**.srt** | application/x-subrip | application/x-srt, text/x-srt

View File

@@ -83,7 +83,7 @@ var (
text = newMIME("text/plain", ".txt", magic.Text, html, svg, xml, php, js, lua, perl, python, json, ndJSON, rtf, srt, tcl, csv, tsv, vCard, iCalendar, warc, vtt)
xml = newMIME("text/xml", ".xml", magic.XML, rss, atom, x3d, kml, xliff, collada, gml, gpx, tcx, amf, threemf, xfdf, owl2).
alias("application/xml")
json = newMIME("application/json", ".json", magic.JSON, geoJSON, har)
json = newMIME("application/json", ".json", magic.JSON, geoJSON, har, gltf)
har = newMIME("application/json", ".har", magic.HAR)
csv = newMIME("text/csv", ".csv", magic.Csv)
tsv = newMIME("text/tab-separated-values", ".tsv", magic.Tsv)
@@ -262,7 +262,8 @@ var (
pat = newMIME("image/x-gimp-pat", ".pat", magic.Pat)
gbr = newMIME("image/x-gimp-gbr", ".gbr", magic.Gbr)
xfdf = newMIME("application/vnd.adobe.xfdf", ".xfdf", magic.Xfdf)
glb = newMIME("model/gltf-binary", ".glb", magic.Glb)
glb = newMIME("model/gltf-binary", ".glb", magic.GLB)
gltf = newMIME("model/gltf+json", ".gltf", magic.GLTF)
jxr = newMIME("image/jxr", ".jxr", magic.Jxr).alias("image/vnd.ms-photo")
parquet = newMIME("application/vnd.apache.parquet", ".parquet", magic.Par1).
alias("application/x-parquet")

View File

@@ -1,26 +1,28 @@
version: "2"
run:
timeout: 1m
tests: true
linters:
disable-all: true
enable:
default: none
enable: # please keep this alphabetized
- asasalint
- asciicheck
- copyloopvar
- dupl
- errcheck
- forcetypeassert
- goconst
- gocritic
- gofmt
- goimports
- gosimple
- govet
- ineffassign
- misspell
- musttag
- revive
- staticcheck
- typecheck
- unused
issues:
exclude-use-default: false
max-issues-per-linter: 0
max-same-issues: 10

View File

@@ -77,7 +77,7 @@ func newSink(fn func(prefix, args string), formatter Formatter) logr.LogSink {
write: fn,
}
// For skipping fnlogger.Info and fnlogger.Error.
l.Formatter.AddCallDepth(1)
l.AddCallDepth(1) // via Formatter
return l
}
@@ -164,17 +164,17 @@ type fnlogger struct {
}
func (l fnlogger) WithName(name string) logr.LogSink {
l.Formatter.AddName(name)
l.AddName(name) // via Formatter
return &l
}
func (l fnlogger) WithValues(kvList ...any) logr.LogSink {
l.Formatter.AddValues(kvList)
l.AddValues(kvList) // via Formatter
return &l
}
func (l fnlogger) WithCallDepth(depth int) logr.LogSink {
l.Formatter.AddCallDepth(depth)
l.AddCallDepth(depth) // via Formatter
return &l
}

View File

@@ -2,101 +2,53 @@ version: "2"
linters:
default: all
disable:
- asasalint
- asciicheck
- bidichk
- bodyclose
- canonicalheader
- containedctx
- contextcheck
- noinlineerr
- wsl_v5
- copyloopvar
- cyclop
- decorder
- depguard
- dogsled
- dupl
- dupword
- durationcheck
- err113
- errcheck
- errchkjson
- errname
- errorlint
- exhaustive
- exhaustruct
- exptostd
- fatcontext
- forbidigo
- forcetypeassert
- funlen
- ginkgolinter
- gocheckcompilerdirectives
- gochecknoglobals
- gochecknoinits
- gochecksumtype
- gocognit
- goconst
- gocritic
- gocyclo
- godot
- godox
- goheader
- gomoddirectives
- gomodguard
- goprintffuncname
- gosec
- gosmopolitan
- govet
- grouper
- iface
- importas
- inamedparam
- ineffassign
- interfacebloat
- intrange
- ireturn
- lll
- loggercheck
- maintidx
- makezero
- mirror
- misspell
- mnd
- musttag
- nakedret
- nestif
- nilerr
- nilnesserr
- nilnil
- nlreturn
- noctx
- nolintlint
- nonamedreturns
- nosprintfhostport
- paralleltest
- perfsprint
- prealloc
- predeclared
- promlinter
- protogetter
- reassign
- recvcheck
- revive
- rowserrcheck
- sloglint
- spancheck
- sqlclosecheck
- staticcheck
- tagalign
- tagliatelle
- testableexamples
- testifylint
- testpackage
- thelper
- tparallel
- unparam
- varnamelen
- whitespace
- wrapcheck
- wsl
- zerologlint

View File

@@ -1,6 +1,6 @@
Package validator
=================
<img align="right" src="logo.png">![Project status](https://img.shields.io/badge/version-10.25.0-green.svg)
<img align="right" src="logo.png">[![GitHub release (latest SemVer)](https://img.shields.io/github/v/release/go-playground/validator)](https://github.com/go-playground/validator/releases)
[![Build Status](https://github.com/go-playground/validator/actions/workflows/workflow.yml/badge.svg)](https://github.com/go-playground/validator/actions)
[![Coverage Status](https://coveralls.io/repos/go-playground/validator/badge.svg?branch=master&service=github)](https://coveralls.io/github/go-playground/validator?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/go-playground/validator)](https://goreportcard.com/report/github.com/go-playground/validator)
@@ -262,6 +262,8 @@ validate := validator.New(validator.WithRequiredStructEnabled())
| excluded_without | Excluded Without |
| excluded_without_all | Excluded Without All |
| unique | Unique |
| validateFn | Verify if the method `Validate() error` does not return an error (or any specified method) |
#### Aliases:
| Tag | Description |

View File

@@ -2,10 +2,12 @@ package validator
import (
"bytes"
"cmp"
"context"
"crypto/sha256"
"encoding/hex"
"encoding/json"
"errors"
"fmt"
"io/fs"
"net"
@@ -244,6 +246,7 @@ var (
"cron": isCron,
"spicedb": isSpiceDB,
"ein": isEIN,
"validateFn": isValidateFn,
}
)
@@ -294,7 +297,7 @@ func isOneOf(fl FieldLevel) bool {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
v = strconv.FormatUint(field.Uint(), 10)
default:
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
for i := 0; i < len(vals); i++ {
if vals[i] == v {
@@ -310,7 +313,7 @@ func isOneOfCI(fl FieldLevel) bool {
field := fl.Field()
if field.Kind() != reflect.String {
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
v := field.String()
for _, val := range vals {
@@ -384,13 +387,13 @@ func isUnique(fl FieldLevel) bool {
}
if uniqueField.Kind() != field.Kind() {
panic(fmt.Sprintf("Bad field type %T:%T", field.Interface(), uniqueField.Interface()))
panic(fmt.Sprintf("Bad field type %s:%s", field.Type(), uniqueField.Type()))
}
return field.Interface() != uniqueField.Interface()
return getValue(field) != getValue(uniqueField)
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
}
@@ -471,7 +474,7 @@ func isLongitude(fl FieldLevel) bool {
case reflect.Float64:
v = strconv.FormatFloat(field.Float(), 'f', -1, 64)
default:
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
return longitudeRegex().MatchString(v)
@@ -494,7 +497,7 @@ func isLatitude(fl FieldLevel) bool {
case reflect.Float64:
v = strconv.FormatFloat(field.Float(), 'f', -1, 64)
default:
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
return latitudeRegex().MatchString(v)
@@ -945,7 +948,6 @@ func isNeField(fl FieldLevel) bool {
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() != currentField.Int()
@@ -966,9 +968,8 @@ func isNeField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
t := currentField.Interface().(time.Time)
fieldTime := field.Interface().(time.Time)
t := getValue(currentField).(time.Time)
fieldTime := getValue(field).(time.Time)
return !fieldTime.Equal(t)
}
@@ -1005,7 +1006,6 @@ func isLteCrossStructField(fl FieldLevel) bool {
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() <= topField.Int()
@@ -1023,9 +1023,8 @@ func isLteCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
fieldTime := field.Convert(timeType).Interface().(time.Time)
topTime := topField.Convert(timeType).Interface().(time.Time)
fieldTime := getValue(field.Convert(timeType)).(time.Time)
topTime := getValue(topField.Convert(timeType)).(time.Time)
return fieldTime.Before(topTime) || fieldTime.Equal(topTime)
}
@@ -1052,7 +1051,6 @@ func isLtCrossStructField(fl FieldLevel) bool {
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() < topField.Int()
@@ -1070,9 +1068,8 @@ func isLtCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
fieldTime := field.Convert(timeType).Interface().(time.Time)
topTime := topField.Convert(timeType).Interface().(time.Time)
fieldTime := getValue(field.Convert(timeType)).(time.Time)
topTime := getValue(topField.Convert(timeType)).(time.Time)
return fieldTime.Before(topTime)
}
@@ -1098,7 +1095,6 @@ func isGteCrossStructField(fl FieldLevel) bool {
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() >= topField.Int()
@@ -1116,9 +1112,8 @@ func isGteCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
fieldTime := field.Convert(timeType).Interface().(time.Time)
topTime := topField.Convert(timeType).Interface().(time.Time)
fieldTime := getValue(field.Convert(timeType)).(time.Time)
topTime := getValue(topField.Convert(timeType)).(time.Time)
return fieldTime.After(topTime) || fieldTime.Equal(topTime)
}
@@ -1144,7 +1139,6 @@ func isGtCrossStructField(fl FieldLevel) bool {
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() > topField.Int()
@@ -1162,9 +1156,8 @@ func isGtCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
fieldTime := field.Convert(timeType).Interface().(time.Time)
topTime := topField.Convert(timeType).Interface().(time.Time)
fieldTime := getValue(field.Convert(timeType)).(time.Time)
topTime := getValue(topField.Convert(timeType)).(time.Time)
return fieldTime.After(topTime)
}
@@ -1190,7 +1183,6 @@ func isNeCrossStructField(fl FieldLevel) bool {
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return topField.Int() != field.Int()
@@ -1211,9 +1203,8 @@ func isNeCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
t := field.Convert(timeType).Interface().(time.Time)
fieldTime := topField.Convert(timeType).Interface().(time.Time)
t := getValue(field.Convert(timeType)).(time.Time)
fieldTime := getValue(topField.Convert(timeType)).(time.Time)
return !fieldTime.Equal(t)
}
@@ -1239,7 +1230,6 @@ func isEqCrossStructField(fl FieldLevel) bool {
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return topField.Int() == field.Int()
@@ -1260,9 +1250,8 @@ func isEqCrossStructField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && topField.Type().ConvertibleTo(timeType) {
t := field.Convert(timeType).Interface().(time.Time)
fieldTime := topField.Convert(timeType).Interface().(time.Time)
t := getValue(field.Convert(timeType)).(time.Time)
fieldTime := getValue(topField.Convert(timeType)).(time.Time)
return fieldTime.Equal(t)
}
@@ -1288,7 +1277,6 @@ func isEqField(fl FieldLevel) bool {
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() == currentField.Int()
@@ -1309,9 +1297,8 @@ func isEqField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
t := getValue(currentField.Convert(timeType)).(time.Time)
fieldTime := getValue(field.Convert(timeType)).(time.Time)
return fieldTime.Equal(t)
}
@@ -1332,7 +1319,6 @@ func isEq(fl FieldLevel) bool {
param := fl.Param()
switch field.Kind() {
case reflect.String:
return field.String() == param
@@ -1367,7 +1353,7 @@ func isEq(fl FieldLevel) bool {
return field.Bool() == p
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isEqIgnoreCase is the validation function for validating if the current field's string value is
@@ -1382,7 +1368,7 @@ func isEqIgnoreCase(fl FieldLevel) bool {
return strings.EqualFold(field.String(), param)
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isPostcodeByIso3166Alpha2 validates by value which is country code in iso 3166 alpha 2
@@ -1416,7 +1402,7 @@ func isPostcodeByIso3166Alpha2Field(fl FieldLevel) bool {
}
if kind != reflect.String {
panic(fmt.Sprintf("Bad field type %T", currentField.Interface()))
panic(fmt.Sprintf("Bad field type %s", currentField.Type()))
}
postcodeRegexInit.Do(initPostcodes)
@@ -1472,16 +1458,7 @@ func isURI(fl FieldLevel) bool {
return err == nil
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
}
// isFileURL is the helper function for validating if the `path` valid file URL as per RFC8089
func isFileURL(path string) bool {
if !strings.HasPrefix(path, "file:/") {
return false
}
_, err := url.ParseRequestURI(path)
return err == nil
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isURL is the validation function for validating if the current field's value is a valid URL.
@@ -1497,23 +1474,20 @@ func isURL(fl FieldLevel) bool {
return false
}
if isFileURL(s) {
return true
}
url, err := url.Parse(s)
if err != nil || url.Scheme == "" {
return false
}
isFileScheme := url.Scheme == "file"
if url.Host == "" && url.Fragment == "" && url.Opaque == "" {
if (isFileScheme && (len(url.Path) == 0 || url.Path == "/")) || (!isFileScheme && len(url.Host) == 0 && len(url.Fragment) == 0 && len(url.Opaque) == 0) {
return false
}
return true
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isHttpURL is the validation function for validating if the current field's value is a valid HTTP(s) URL.
@@ -1536,7 +1510,7 @@ func isHttpURL(fl FieldLevel) bool {
return url.Scheme == "http" || url.Scheme == "https"
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isUrnRFC2141 is the validation function for validating if the current field's value is a valid URN as per RFC 2141.
@@ -1553,7 +1527,7 @@ func isUrnRFC2141(fl FieldLevel) bool {
return match
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isFile is the validation function for validating if the current field's value is a valid existing file path.
@@ -1570,7 +1544,7 @@ func isFile(fl FieldLevel) bool {
return !fileInfo.IsDir()
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isImage is the validation function for validating if the current field's value contains the path to a valid image file
@@ -1632,7 +1606,8 @@ func isImage(fl FieldLevel) bool {
return true
}
}
return false
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isFilePath is the validation function for validating if the current field's value is a valid file path.
@@ -1686,7 +1661,7 @@ func isFilePath(fl FieldLevel) bool {
}
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isE164 is the validation function for validating if the current field's value is a valid e.164 formatted phone number.
@@ -1796,7 +1771,7 @@ func hasValue(fl FieldLevel) bool {
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
return !field.IsNil()
default:
if fl.(*validate).fldIsPointer && field.Interface() != nil {
if fl.(*validate).fldIsPointer && getValue(field) != nil {
return true
}
return field.IsValid() && !field.IsZero()
@@ -1807,10 +1782,13 @@ func hasValue(fl FieldLevel) bool {
func hasNotZeroValue(fl FieldLevel) bool {
field := fl.Field()
switch field.Kind() {
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
case reflect.Slice, reflect.Map:
// For slices and maps, consider them "not zero" only if they're both non-nil AND have elements
return !field.IsNil() && field.Len() > 0
case reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
return !field.IsNil()
default:
if fl.(*validate).fldIsPointer && field.Interface() != nil {
if fl.(*validate).fldIsPointer && getValue(field) != nil {
return !field.IsZero()
}
return field.IsValid() && !field.IsZero()
@@ -1834,7 +1812,7 @@ func requireCheckFieldKind(fl FieldLevel, param string, defaultNotFoundValue boo
case reflect.Slice, reflect.Map, reflect.Ptr, reflect.Interface, reflect.Chan, reflect.Func:
return field.IsNil()
default:
if nullable && field.Interface() != nil {
if nullable && getValue(field) != nil {
return false
}
return field.IsValid() && field.IsZero()
@@ -1851,7 +1829,6 @@ func requireCheckFieldValue(
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() == asInt(value)
@@ -1864,7 +1841,13 @@ func requireCheckFieldValue(
case reflect.Float64:
return field.Float() == asFloat64(value)
case reflect.Slice, reflect.Map, reflect.Array:
case reflect.Slice, reflect.Map:
if value == "nil" {
return field.IsNil()
}
return int64(field.Len()) == asInt(value)
case reflect.Array:
// Arrays can't be nil, so only compare lengths
return int64(field.Len()) == asInt(value)
case reflect.Bool:
@@ -2019,9 +2002,12 @@ func excludedWithout(fl FieldLevel) bool {
// requiredWithout is the validation function
// The field under validation must be present and not empty only when any of the other specified fields are not present.
func requiredWithout(fl FieldLevel) bool {
if requireCheckFieldKind(fl, strings.TrimSpace(fl.Param()), true) {
params := parseOneOfParam2(fl.Param())
for _, param := range params {
if requireCheckFieldKind(fl, param, true) {
return hasValue(fl)
}
}
return true
}
@@ -2060,7 +2046,6 @@ func isGteField(fl FieldLevel) bool {
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() >= currentField.Int()
@@ -2078,9 +2063,8 @@ func isGteField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
t := getValue(currentField.Convert(timeType)).(time.Time)
fieldTime := getValue(field.Convert(timeType)).(time.Time)
return fieldTime.After(t) || fieldTime.Equal(t)
}
@@ -2106,7 +2090,6 @@ func isGtField(fl FieldLevel) bool {
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() > currentField.Int()
@@ -2124,9 +2107,8 @@ func isGtField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
t := getValue(currentField.Convert(timeType)).(time.Time)
fieldTime := getValue(field.Convert(timeType)).(time.Time)
return fieldTime.After(t)
}
@@ -2147,7 +2129,6 @@ func isGte(fl FieldLevel) bool {
param := fl.Param()
switch field.Kind() {
case reflect.String:
p := asInt(param)
@@ -2181,15 +2162,14 @@ func isGte(fl FieldLevel) bool {
case reflect.Struct:
if field.Type().ConvertibleTo(timeType) {
now := time.Now().UTC()
t := field.Convert(timeType).Interface().(time.Time)
t := getValue(field.Convert(timeType)).(time.Time)
return t.After(now) || t.Equal(now)
}
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isGt is the validation function for validating if the current field's value is greater than the param's value.
@@ -2198,7 +2178,6 @@ func isGt(fl FieldLevel) bool {
param := fl.Param()
switch field.Kind() {
case reflect.String:
p := asInt(param)
@@ -2232,11 +2211,11 @@ func isGt(fl FieldLevel) bool {
case reflect.Struct:
if field.Type().ConvertibleTo(timeType) {
return field.Convert(timeType).Interface().(time.Time).After(time.Now().UTC())
return getValue(field.Convert(timeType)).(time.Time).After(time.Now().UTC())
}
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// hasLengthOf is the validation function for validating if the current field's value is equal to the param's value.
@@ -2245,7 +2224,6 @@ func hasLengthOf(fl FieldLevel) bool {
param := fl.Param()
switch field.Kind() {
case reflect.String:
p := asInt(param)
@@ -2277,7 +2255,7 @@ func hasLengthOf(fl FieldLevel) bool {
return field.Float() == p
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// hasMinOf is the validation function for validating if the current field's value is greater than or equal to the param's value.
@@ -2296,7 +2274,6 @@ func isLteField(fl FieldLevel) bool {
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() <= currentField.Int()
@@ -2314,9 +2291,8 @@ func isLteField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
t := getValue(currentField.Convert(timeType)).(time.Time)
fieldTime := getValue(field.Convert(timeType)).(time.Time)
return fieldTime.Before(t) || fieldTime.Equal(t)
}
@@ -2342,7 +2318,6 @@ func isLtField(fl FieldLevel) bool {
}
switch kind {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return field.Int() < currentField.Int()
@@ -2360,9 +2335,8 @@ func isLtField(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(timeType) && currentField.Type().ConvertibleTo(timeType) {
t := currentField.Convert(timeType).Interface().(time.Time)
fieldTime := field.Convert(timeType).Interface().(time.Time)
t := getValue(currentField.Convert(timeType)).(time.Time)
fieldTime := getValue(field.Convert(timeType)).(time.Time)
return fieldTime.Before(t)
}
@@ -2383,7 +2357,6 @@ func isLte(fl FieldLevel) bool {
param := fl.Param()
switch field.Kind() {
case reflect.String:
p := asInt(param)
@@ -2417,15 +2390,14 @@ func isLte(fl FieldLevel) bool {
case reflect.Struct:
if field.Type().ConvertibleTo(timeType) {
now := time.Now().UTC()
t := field.Convert(timeType).Interface().(time.Time)
t := getValue(field.Convert(timeType)).(time.Time)
return t.Before(now) || t.Equal(now)
}
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isLt is the validation function for validating if the current field's value is less than the param's value.
@@ -2434,7 +2406,6 @@ func isLt(fl FieldLevel) bool {
param := fl.Param()
switch field.Kind() {
case reflect.String:
p := asInt(param)
@@ -2468,11 +2439,11 @@ func isLt(fl FieldLevel) bool {
case reflect.Struct:
if field.Type().ConvertibleTo(timeType) {
return field.Convert(timeType).Interface().(time.Time).Before(time.Now().UTC())
return getValue(field.Convert(timeType)).(time.Time).Before(time.Now().UTC())
}
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// hasMaxOf is the validation function for validating if the current field's value is less than or equal to the param's value.
@@ -2642,7 +2613,7 @@ func isDir(fl FieldLevel) bool {
return fileInfo.IsDir()
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isDirPath is the validation function for validating if the current field's value is a valid directory.
@@ -2699,7 +2670,7 @@ func isDirPath(fl FieldLevel) bool {
}
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isJSON is the validation function for validating if the current field's value is a valid json string.
@@ -2714,12 +2685,12 @@ func isJSON(fl FieldLevel) bool {
fieldType := field.Type()
if fieldType.ConvertibleTo(byteSliceType) {
b := field.Convert(byteSliceType).Interface().([]byte)
b := getValue(field.Convert(byteSliceType)).([]byte)
return json.Valid(b)
}
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isJWT is the validation function for validating if the current field's value is a valid JWT string.
@@ -2766,7 +2737,7 @@ func isLowercase(fl FieldLevel) bool {
return field.String() == strings.ToLower(field.String())
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isUppercase is the validation function for validating if the current field's value is an uppercase string.
@@ -2780,7 +2751,7 @@ func isUppercase(fl FieldLevel) bool {
return field.String() == strings.ToUpper(field.String())
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isDatetime is the validation function for validating if the current field's value is a valid datetime string.
@@ -2794,7 +2765,7 @@ func isDatetime(fl FieldLevel) bool {
return err == nil
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isTimeZone is the validation function for validating if the current field's value is a valid time zone string.
@@ -2816,7 +2787,7 @@ func isTimeZone(fl FieldLevel) bool {
return err == nil
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isIso3166Alpha2 is the validation function for validating if the current field's value is a valid iso3166-1 alpha-2 country code.
@@ -2860,7 +2831,7 @@ func isIso3166AlphaNumeric(fl FieldLevel) bool {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
code = int(field.Uint() % 1000)
default:
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
_, ok := iso3166_1_alpha_numeric[code]
@@ -2884,7 +2855,7 @@ func isIso3166AlphaNumericEU(fl FieldLevel) bool {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
code = int(field.Uint() % 1000)
default:
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
_, ok := iso3166_1_alpha_numeric_eu[code]
@@ -2914,7 +2885,7 @@ func isIso4217Numeric(fl FieldLevel) bool {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
code = int(field.Uint())
default:
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
_, ok := iso4217_numeric[code]
@@ -2930,7 +2901,7 @@ func isBCP47LanguageTag(fl FieldLevel) bool {
return err == nil
}
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
// isIsoBicFormat is the validation function for validating if the current field's value is a valid Business Identifier Code (SWIFT code), defined in ISO 9362
@@ -3053,7 +3024,7 @@ func hasLuhnChecksum(fl FieldLevel) bool {
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
str = strconv.FormatUint(field.Uint(), 10)
default:
panic(fmt.Sprintf("Bad field type %T", field.Interface()))
panic(fmt.Sprintf("Bad field type %s", field.Type()))
}
size := len(str)
if size < 2 { // there has to be at least one digit that carries a meaning + the checksum
@@ -3079,3 +3050,60 @@ func isEIN(fl FieldLevel) bool {
return einRegex().MatchString(field.String())
}
func isValidateFn(fl FieldLevel) bool {
const defaultParam = `Validate`
field := fl.Field()
validateFn := cmp.Or(fl.Param(), defaultParam)
ok, err := tryCallValidateFn(field, validateFn)
if err != nil {
return false
}
return ok
}
var (
errMethodNotFound = errors.New(`method not found`)
errMethodReturnNoValues = errors.New(`method return o values (void)`)
errMethodReturnInvalidType = errors.New(`method should return invalid type`)
)
func tryCallValidateFn(field reflect.Value, validateFn string) (bool, error) {
method := field.MethodByName(validateFn)
if field.CanAddr() && !method.IsValid() {
method = field.Addr().MethodByName(validateFn)
}
if !method.IsValid() {
return false, fmt.Errorf("unable to call %q on type %q: %w",
validateFn, field.Type().String(), errMethodNotFound)
}
returnValues := method.Call([]reflect.Value{})
if len(returnValues) == 0 {
return false, fmt.Errorf("unable to use result of method %q on type %q: %w",
validateFn, field.Type().String(), errMethodReturnNoValues)
}
firstReturnValue := returnValues[0]
switch firstReturnValue.Kind() {
case reflect.Bool:
return firstReturnValue.Bool(), nil
case reflect.Interface:
errorType := reflect.TypeOf((*error)(nil)).Elem()
if firstReturnValue.Type().Implements(errorType) {
return firstReturnValue.IsNil(), nil
}
return false, fmt.Errorf("unable to use result of method %q on type %q: %w (got interface %v expect error)",
validateFn, field.Type().String(), errMethodReturnInvalidType, firstReturnValue.Type().String())
default:
return false, fmt.Errorf("unable to use result of method %q on type %q: %w (got %v expect error or bool)",
validateFn, field.Type().String(), errMethodReturnInvalidType, firstReturnValue.Type().String())
}
}

View File

@@ -124,7 +124,6 @@ func (v *Validate) extractStructCache(current reflect.Value, sName string) *cStr
var customName string
for i := 0; i < numFields; i++ {
fld = typ.Field(i)
if !v.privateFieldValidation && !fld.Anonymous && len(fld.PkgPath) > 0 {
@@ -191,7 +190,6 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
} else {
next, curr := v.parseFieldTagsRecursive(tagsVal, fieldName, t, true)
current.next, current = next, curr
}
continue
}
@@ -210,7 +208,6 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
switch t {
case diveTag:
current.typeof = typeDive
continue
case keysTag:
current.typeof = typeKeys
@@ -219,8 +216,6 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
panic(fmt.Sprintf("'%s' tag must be immediately preceded by the '%s' tag", keysTag, diveTag))
}
current.typeof = typeKeys
// need to pass along only keys tag
// need to increment i to skip over the keys tags
b := make([]byte, 0, 64)
@@ -228,7 +223,6 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
i++
for ; i < len(tags); i++ {
b = append(b, tags[i]...)
b = append(b, ',')
@@ -238,7 +232,6 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
}
current.keys, _ = v.parseFieldTagsRecursive(string(b[:len(b)-1]), fieldName, "", false)
continue
case endKeysTag:
current.typeof = typeEndKeys
@@ -256,19 +249,15 @@ func (v *Validate) parseFieldTagsRecursive(tag string, fieldName string, alias s
case omitempty:
current.typeof = typeOmitEmpty
continue
case omitnil:
current.typeof = typeOmitNil
continue
case structOnlyTag:
current.typeof = typeStructOnly
continue
case noStructLevelTag:
current.typeof = typeNoStructLevel
continue
default:
if t == isdefault {

View File

@@ -188,7 +188,7 @@ Same as structonly tag except that any struct level validations will not run.
# Omit Empty
Allows conditional validation, for example if a field is not set with
Allows conditional validation, for example, if a field is not set with
a value (Determined by the "required" validator) then other validation
such as min or max won't run, but if a value is set validation will run.
@@ -756,6 +756,20 @@ in a field of the struct specified via a parameter.
// For slices of struct:
Usage: unique=field
# ValidateFn
This validates that an object responds to a method that can return error or bool.
By default it expects an interface `Validate() error` and check that the method
does not return an error. Other methods can be specified using two signatures:
If the method returns an error, it check if the return value is nil.
If the method returns a boolean, it checks if the value is true.
// to use the default method Validate() error
Usage: validateFn
// to use the custom method IsValid() bool (or error)
Usage: validateFn=IsValid
# Alpha Only
This validates that a string value contains ASCII alpha characters only

View File

@@ -24,7 +24,6 @@ type InvalidValidationError struct {
// Error returns InvalidValidationError message
func (e *InvalidValidationError) Error() string {
if e.Type == nil {
return "validator: (nil)"
}
@@ -41,11 +40,9 @@ type ValidationErrors []FieldError
// All information to create an error message specific to your application is contained within
// the FieldError found within the ValidationErrors array
func (ve ValidationErrors) Error() string {
buff := bytes.NewBufferString("")
for i := 0; i < len(ve); i++ {
buff.WriteString(ve[i].Error())
buff.WriteString("\n")
}
@@ -55,7 +52,6 @@ func (ve ValidationErrors) Error() string {
// Translate translates all of the ValidationErrors
func (ve ValidationErrors) Translate(ut ut.Translator) ValidationErrorsTranslations {
trans := make(ValidationErrorsTranslations)
var fe *fieldError
@@ -109,22 +105,24 @@ type FieldError interface {
// StructNamespace returns the namespace for the field error, with the field's
// actual name.
//
// eq. "User.FirstName" see Namespace for comparison
// eg. "User.FirstName" see Namespace for comparison
//
// NOTE: this field can be blank when validating a single primitive field
// using validate.Field(...) as there is no way to extract its name
StructNamespace() string
// Field returns the fields name with the tag name taking precedence over the
// Field returns the field's name with the tag name taking precedence over the
// field's actual name.
//
// eq. JSON name "fname"
// `RegisterTagNameFunc` must be registered to get tag value.
//
// eg. JSON name "fname"
// see StructField for comparison
Field() string
// StructField returns the field's actual name from the struct, when able to determine.
//
// eq. "FirstName"
// eg. "FirstName"
// see Field for comparison
StructField() string
@@ -204,7 +202,6 @@ func (fe *fieldError) StructNamespace() string {
// Field returns the field's name with the tag name taking precedence over the
// field's actual name.
func (fe *fieldError) Field() string {
return fe.ns[len(fe.ns)-int(fe.fieldLen):]
// // return fe.field
// fld := fe.ns[len(fe.ns)-int(fe.fieldLen):]

View File

@@ -107,7 +107,6 @@ func (v *validate) ExtractType(field reflect.Value) (reflect.Value, reflect.Kind
// ReportError reports an error just by passing the field and tag information
func (v *validate) ReportError(field interface{}, fieldName, structFieldName, tag, param string) {
fv, kind, _ := v.extractTypeInternal(reflect.ValueOf(field), false)
if len(structFieldName) == 0 {
@@ -123,7 +122,6 @@ func (v *validate) ReportError(field interface{}, fieldName, structFieldName, ta
}
if kind == reflect.Invalid {
v.errs = append(v.errs,
&fieldError{
v: v.v,
@@ -149,7 +147,7 @@ func (v *validate) ReportError(field interface{}, fieldName, structFieldName, ta
structNs: v.str2,
fieldLen: uint8(len(fieldName)),
structfieldLen: uint8(len(structFieldName)),
value: fv.Interface(),
value: getValue(fv),
param: param,
kind: kind,
typ: fv.Type(),
@@ -161,11 +159,9 @@ func (v *validate) ReportError(field interface{}, fieldName, structFieldName, ta
//
// NOTE: this function prepends the current namespace to the relative ones.
func (v *validate) ReportValidationErrors(relativeNamespace, relativeStructNamespace string, errs ValidationErrors) {
var err *fieldError
for i := 0; i < len(errs); i++ {
err = errs[i].(*fieldError)
err.ns = string(append(append(v.ns, relativeNamespace...), err.ns...))
err.structNs = string(append(append(v.actualNs, relativeStructNamespace...), err.structNs...))

View File

@@ -1229,6 +1229,11 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be a valid ISSN number",
override: false,
},
{
tag: "urn_rfc2141",
translation: "{0} must be a valid RFC 2141 URN",
override: false,
},
{
tag: "uuid",
translation: "{0} must be a valid UUID",
@@ -1484,10 +1489,14 @@ func RegisterDefaultTranslations(v *validator.Validate, trans ut.Translator) (er
translation: "{0} must be a valid cve identifier",
override: false,
},
{
tag: "validateFn",
translation: "{0} must be a valid object",
override: false,
},
}
for _, t := range translations {
if t.customTransFunc != nil && t.customRegisFunc != nil {
err = v.RegisterTranslation(t.tag, trans, t.customRegisFunc, t.customTransFunc)
} else if t.customTransFunc != nil && t.customRegisFunc == nil {

View File

@@ -13,7 +13,6 @@ import (
// It will dive into pointers, customTypes and return you the
// underlying value and it's kind.
func (v *validate) extractTypeInternal(current reflect.Value, nullable bool) (reflect.Value, reflect.Kind, bool) {
BEGIN:
switch current.Kind() {
case reflect.Ptr:
@@ -44,7 +43,6 @@ BEGIN:
default:
if v.v.hasCustomFuncs {
if fn, ok := v.v.customFuncs[current.Type()]; ok {
current = reflect.ValueOf(fn(current))
goto BEGIN
@@ -61,7 +59,6 @@ BEGIN:
// NOTE: when not successful ok will be false, this can happen when a nested struct is nil and so the field
// could not be retrieved because it didn't exist.
func (v *validate) getStructFieldOKInternal(val reflect.Value, namespace string) (current reflect.Value, kind reflect.Kind, nullable bool, found bool) {
BEGIN:
current, kind, nullable = v.ExtractType(val)
if kind == reflect.Invalid {
@@ -74,7 +71,6 @@ BEGIN:
}
switch kind {
case reflect.Ptr, reflect.Interface:
return
@@ -85,7 +81,6 @@ BEGIN:
var ns string
if !typ.ConvertibleTo(timeType) {
idx := strings.Index(namespace, namespaceSeparator)
if idx != -1 {
@@ -222,7 +217,7 @@ BEGIN:
panic("Invalid field namespace")
}
// asInt returns the parameter as a int64
// asInt returns the parameter as an int64
// or panics if it can't convert
func asInt(param string) int64 {
i, err := strconv.ParseInt(param, 0, 64)
@@ -256,7 +251,6 @@ func asIntFromType(t reflect.Type, param string) int64 {
// asUint returns the parameter as a uint64
// or panics if it can't convert
func asUint(param string) uint64 {
i, err := strconv.ParseUint(param, 0, 64)
panicIf(err)
@@ -282,7 +276,6 @@ func asFloat32(param string) float64 {
// asBool returns the parameter as a bool
// or panics if it can't convert
func asBool(param string) bool {
i, err := strconv.ParseBool(param)
panicIf(err)
@@ -303,7 +296,7 @@ func fieldMatchesRegexByStringerValOrString(regexFn func() *regexp.Regexp, fl Fi
case reflect.String:
return regex.MatchString(fl.Field().String())
default:
if stringer, ok := fl.Field().Interface().(fmt.Stringer); ok {
if stringer, ok := getValue(fl.Field()).(fmt.Stringer); ok {
return regex.MatchString(stringer.String())
} else {
return regex.MatchString(fl.Field().String())

View File

@@ -32,14 +32,12 @@ type validate struct {
// parent and current will be the same the first run of validateStruct
func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, current reflect.Value, typ reflect.Type, ns []byte, structNs []byte, ct *cTag) {
cs, ok := v.v.structCache.Get(typ)
if !ok {
cs = v.v.extractStructCache(current, typ.Name())
}
if len(ns) == 0 && len(cs.name) != 0 {
ns = append(ns, cs.name...)
ns = append(ns, '.')
@@ -50,21 +48,17 @@ func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, cur
// ct is nil on top level struct, and structs as fields that have no tag info
// so if nil or if not nil and the structonly tag isn't present
if ct == nil || ct.typeof != typeStructOnly {
var f *cField
for i := 0; i < len(cs.fields); i++ {
f = cs.fields[i]
if v.isPartial {
if v.ffn != nil {
// used with StructFiltered
if v.ffn(append(structNs, f.name...)) {
continue
}
} else {
// used with StructPartial & StructExcept
_, ok = v.includeExclude[string(append(structNs, f.name...))]
@@ -83,7 +77,6 @@ func (v *validate) validateStruct(ctx context.Context, parent reflect.Value, cur
// first iteration will have no info about nostructlevel tag, and is checked prior to
// calling the next iteration of validateStruct called from traverseField.
if cs.fn != nil {
v.slflParent = parent
v.slCurrent = current
v.ns = ns
@@ -267,7 +260,7 @@ OUTER:
return
}
default:
if v.fldIsPointer && field.Interface() == nil {
if v.fldIsPointer && getValue(field) == nil {
return
}
}
@@ -291,7 +284,6 @@ OUTER:
reusableCF := &cField{}
for i := 0; i < current.Len(); i++ {
i64 = int64(i)
v.misc = append(v.misc[0:0], cf.name...)
@@ -304,7 +296,6 @@ OUTER:
if cf.namesEqual {
reusableCF.altName = reusableCF.name
} else {
v.misc = append(v.misc[0:0], cf.altName...)
v.misc = append(v.misc, '[')
v.misc = strconv.AppendInt(v.misc, i64, 10)
@@ -321,8 +312,7 @@ OUTER:
reusableCF := &cField{}
for _, key := range current.MapKeys() {
pv = fmt.Sprintf("%v", key.Interface())
pv = fmt.Sprintf("%v", key)
v.misc = append(v.misc[0:0], cf.name...)
v.misc = append(v.misc, '[')
@@ -347,6 +337,18 @@ OUTER:
// can be nil when just keys being validated
if ct.next != nil {
v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct.next)
} else {
// Struct fallback when map values are structs
val := current.MapIndex(key)
switch val.Kind() {
case reflect.Ptr:
if val.Elem().Kind() == reflect.Struct {
// Dive into the struct so its own tags run
v.traverseField(ctx, parent, val, ns, structNs, reusableCF, nil)
}
case reflect.Struct:
v.traverseField(ctx, parent, val, ns, structNs, reusableCF, nil)
}
}
} else {
v.traverseField(ctx, parent, current.MapIndex(key), ns, structNs, reusableCF, ct)
@@ -366,7 +368,6 @@ OUTER:
v.misc = v.misc[0:0]
for {
// set Field Level fields
v.slflParent = parent
v.flField = current
@@ -381,7 +382,6 @@ OUTER:
// drain rest of the 'or' values, then continue or leave
for {
ct = ct.next
if ct == nil {
@@ -418,7 +418,6 @@ OUTER:
}
if ct.hasAlias {
v.errs = append(v.errs,
&fieldError{
v: v.v,
@@ -434,9 +433,7 @@ OUTER:
typ: typ,
},
)
} else {
tVal := string(v.misc)[1:]
v.errs = append(v.errs,
@@ -500,7 +497,6 @@ OUTER:
ct = ct.next
}
}
}
func getValue(val reflect.Value) interface{} {

View File

@@ -104,7 +104,6 @@ type Validate struct {
// in essence only parsing your validation tags once per struct type.
// Using multiple instances neglects the benefit of caching.
func New(options ...Option) *Validate {
tc := new(tagCache)
tc.m.Store(make(map[string]*cTag))
@@ -126,7 +125,6 @@ func New(options ...Option) *Validate {
// must copy validators for separate validations to be used in each instance
for k, val := range bakedInValidators {
switch k {
// these require that even if the value is nil that the validation should run, omitempty still overrides this behaviour
case requiredIfTag, requiredUnlessTag, requiredWithTag, requiredWithAllTag, requiredWithoutTag, requiredWithoutAllTag,
@@ -233,30 +231,12 @@ func (v *Validate) RegisterValidationCtx(tag string, fn FuncCtx, callValidationE
return v.registerValidation(tag, fn, false, nilCheckable)
}
func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error {
if len(tag) == 0 {
return errors.New("function Key cannot be empty")
}
if fn == nil {
return errors.New("function cannot be empty")
}
_, ok := restrictedTags[tag]
if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
panic(fmt.Sprintf(restrictedTagErr, tag))
}
v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidationOnNil: nilCheckable}
return nil
}
// RegisterAlias registers a mapping of a single validation tag that
// defines a common or complex set of validation(s) to simplify adding validation
// to structs.
//
// NOTE: this function is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterAlias(alias, tags string) {
_, ok := restrictedTags[alias]
if ok || strings.ContainsAny(alias, restrictedTagChars) {
@@ -280,7 +260,6 @@ func (v *Validate) RegisterStructValidation(fn StructLevelFunc, types ...interfa
// NOTE:
// - this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterStructValidationCtx(fn StructLevelFuncCtx, types ...interface{}) {
if v.structLevelFuncs == nil {
v.structLevelFuncs = make(map[reflect.Type]StructLevelFuncCtx)
}
@@ -327,7 +306,6 @@ func (v *Validate) RegisterStructValidationMapRules(rules map[string]string, typ
//
// NOTE: this method is not thread-safe it is intended that these all be registered prior to any validation
func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{}) {
if v.customFuncs == nil {
v.customFuncs = make(map[reflect.Type]CustomTypeFunc)
}
@@ -341,7 +319,6 @@ func (v *Validate) RegisterCustomTypeFunc(fn CustomTypeFunc, types ...interface{
// RegisterTranslation registers translations against the provided tag.
func (v *Validate) RegisterTranslation(tag string, trans ut.Translator, registerFn RegisterTranslationsFunc, translationFn TranslationFunc) (err error) {
if v.transTagFunc == nil {
v.transTagFunc = make(map[ut.Translator]map[string]TranslationFunc)
}
@@ -375,7 +352,6 @@ func (v *Validate) Struct(s interface{}) error {
// It returns InvalidValidationError for bad values passed in and nil or ValidationErrors as error otherwise.
// You will need to assert the error if it's not nil eg. err.(validator.ValidationErrors) to access the array of errors.
func (v *Validate) StructCtx(ctx context.Context, s interface{}) (err error) {
val := reflect.ValueOf(s)
top := val
@@ -492,10 +468,8 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
name := typ.Name()
for _, k := range fields {
flds := strings.Split(k, namespaceSeparator)
if len(flds) > 0 {
vd.misc = append(vd.misc[0:0], name...)
// Don't append empty name for unnamed structs
if len(vd.misc) != 0 {
@@ -503,7 +477,6 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
}
for _, s := range flds {
idx := strings.Index(s, leftBracket)
if idx != -1 {
@@ -519,7 +492,6 @@ func (v *Validate) StructPartialCtx(ctx context.Context, s interface{}, fields .
idx = strings.Index(s, leftBracket)
}
} else {
vd.misc = append(vd.misc, s...)
vd.includeExclude[string(vd.misc)] = struct{}{}
}
@@ -582,7 +554,6 @@ func (v *Validate) StructExceptCtx(ctx context.Context, s interface{}, fields ..
name := typ.Name()
for _, key := range fields {
vd.misc = vd.misc[0:0]
if len(name) > 0 {
@@ -709,3 +680,20 @@ func (v *Validate) VarWithValueCtx(ctx context.Context, field interface{}, other
v.pool.Put(vd)
return
}
func (v *Validate) registerValidation(tag string, fn FuncCtx, bakedIn bool, nilCheckable bool) error {
if len(tag) == 0 {
return errors.New("function Key cannot be empty")
}
if fn == nil {
return errors.New("function cannot be empty")
}
_, ok := restrictedTags[tag]
if !bakedIn && (ok || strings.ContainsAny(tag, restrictedTagChars)) {
panic(fmt.Sprintf(restrictedTagErr, tag))
}
v.validations[tag] = internalValidationFuncWrapper{fn: fn, runValidationOnNil: nilCheckable}
return nil
}

View File

@@ -5,6 +5,7 @@ import (
"fmt"
"reflect"
"strings"
"sync"
"sync/atomic"
"unicode"
"unsafe"
@@ -17,22 +18,27 @@ var (
typeAddr *runtime.TypeAddr
cachedDecoderMap unsafe.Pointer // map[uintptr]decoder
cachedDecoder []Decoder
initOnce sync.Once
)
func init() {
func initDecoder() {
initOnce.Do(func() {
typeAddr = runtime.AnalyzeTypeAddr()
if typeAddr == nil {
typeAddr = &runtime.TypeAddr{}
}
cachedDecoder = make([]Decoder, typeAddr.AddrRange>>typeAddr.AddrShift+1)
})
}
func loadDecoderMap() map[uintptr]Decoder {
initDecoder()
p := atomic.LoadPointer(&cachedDecoderMap)
return *(*map[uintptr]Decoder)(unsafe.Pointer(&p))
}
func storeDecoder(typ uintptr, dec Decoder, m map[uintptr]Decoder) {
initDecoder()
newDecoderMap := make(map[uintptr]Decoder, len(m)+1)
newDecoderMap[typ] = dec

View File

@@ -10,6 +10,7 @@ import (
)
func CompileToGetDecoder(typ *runtime.Type) (Decoder, error) {
initDecoder()
typeptr := uintptr(unsafe.Pointer(typ))
if typeptr > typeAddr.MaxTypeAddr {
return compileToGetDecoderSlowPath(typeptr, typ)

View File

@@ -13,6 +13,7 @@ import (
var decMu sync.RWMutex
func CompileToGetDecoder(typ *runtime.Type) (Decoder, error) {
initDecoder()
typeptr := uintptr(unsafe.Pointer(typ))
if typeptr > typeAddr.MaxTypeAddr {
return compileToGetDecoderSlowPath(typeptr, typ)

View File

@@ -5,6 +5,7 @@ import (
"encoding"
"encoding/json"
"reflect"
"sync"
"sync/atomic"
"unsafe"
@@ -24,14 +25,17 @@ var (
cachedOpcodeSets []*OpcodeSet
cachedOpcodeMap unsafe.Pointer // map[uintptr]*OpcodeSet
typeAddr *runtime.TypeAddr
initEncoderOnce sync.Once
)
func init() {
func initEncoder() {
initEncoderOnce.Do(func() {
typeAddr = runtime.AnalyzeTypeAddr()
if typeAddr == nil {
typeAddr = &runtime.TypeAddr{}
}
cachedOpcodeSets = make([]*OpcodeSet, typeAddr.AddrRange>>typeAddr.AddrShift+1)
})
}
func loadOpcodeMap() map[uintptr]*OpcodeSet {

View File

@@ -4,6 +4,7 @@
package encoder
func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, error) {
initEncoder()
if typeptr > typeAddr.MaxTypeAddr || typeptr < typeAddr.BaseTypeAddr {
codeSet, err := compileToGetCodeSetSlowPath(typeptr)
if err != nil {

View File

@@ -10,6 +10,7 @@ import (
var setsMu sync.RWMutex
func CompileToGetCodeSet(ctx *RuntimeContext, typeptr uintptr) (*OpcodeSet, error) {
initEncoder()
if typeptr > typeAddr.MaxTypeAddr || typeptr < typeAddr.BaseTypeAddr {
codeSet, err := compileToGetCodeSetSlowPath(typeptr)
if err != nil {

View File

@@ -406,6 +406,11 @@ func AppendMarshalJSON(ctx *RuntimeContext, code *Opcode, b []byte, v interface{
rv = newV
}
}
if rv.Kind() == reflect.Ptr && rv.IsNil() {
return AppendNull(ctx, b), nil
}
v = rv.Interface()
var bb []byte
if (code.Flags & MarshalerContextFlags) != 0 {

View File

@@ -2,6 +2,7 @@ package runtime
import (
"reflect"
"sync"
"unsafe"
)
@@ -24,7 +25,7 @@ type TypeAddr struct {
var (
typeAddr *TypeAddr
alreadyAnalyzed bool
once sync.Once
)
//go:linkname typelinks reflect.typelinks
@@ -34,18 +35,13 @@ func typelinks() ([]unsafe.Pointer, [][]int32)
func rtypeOff(unsafe.Pointer, int32) unsafe.Pointer
func AnalyzeTypeAddr() *TypeAddr {
defer func() {
alreadyAnalyzed = true
}()
if alreadyAnalyzed {
return typeAddr
}
once.Do(func() {
sections, offsets := typelinks()
if len(sections) != 1 {
return nil
return
}
if len(offsets) != 1 {
return nil
return
}
section := sections[0]
offset := offsets[0]
@@ -78,7 +74,7 @@ func AnalyzeTypeAddr() *TypeAddr {
}
addrRange := max - min
if addrRange == 0 {
return nil
return
}
var addrShift uintptr
if isAligned64 {
@@ -88,7 +84,7 @@ func AnalyzeTypeAddr() *TypeAddr {
}
cacheSize := addrRange >> addrShift
if cacheSize > maxAcceptableTypeAddrRange {
return nil
return
}
typeAddr = &TypeAddr{
BaseTypeAddr: min,
@@ -96,5 +92,7 @@ func AnalyzeTypeAddr() *TypeAddr {
AddrRange: addrRange,
AddrShift: addrShift,
}
})
return typeAddr
}

View File

@@ -7,7 +7,6 @@ import (
"encoding/base64"
"fmt"
"io"
"io/ioutil"
"math"
"os"
"path/filepath"
@@ -16,11 +15,12 @@ import (
"strconv"
"time"
"golang.org/x/xerrors"
"github.com/goccy/go-yaml/ast"
"github.com/goccy/go-yaml/internal/errors"
"github.com/goccy/go-yaml/parser"
"github.com/goccy/go-yaml/token"
"golang.org/x/xerrors"
)
// Decoder reads and decodes YAML values from an input stream.
@@ -488,6 +488,21 @@ func (d *Decoder) fileToNode(f *ast.File) ast.Node {
func (d *Decoder) convertValue(v reflect.Value, typ reflect.Type, src ast.Node) (reflect.Value, error) {
if typ.Kind() != reflect.String {
if !v.Type().ConvertibleTo(typ) {
// Special case for "strings -> floats" aka scientific notation
// If the destination type is a float and the source type is a string, check if we can
// use strconv.ParseFloat to convert the string to a float.
if (typ.Kind() == reflect.Float32 || typ.Kind() == reflect.Float64) &&
v.Type().Kind() == reflect.String {
if f, err := strconv.ParseFloat(v.String(), 64); err == nil {
if typ.Kind() == reflect.Float32 {
return reflect.ValueOf(float32(f)), nil
} else if typ.Kind() == reflect.Float64 {
return reflect.ValueOf(f), nil
}
// else, fall through to the error below
}
}
return reflect.Zero(typ), errTypeMismatch(typ, v.Type(), src.GetToken())
}
return v.Convert(typ), nil
@@ -877,6 +892,15 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
dst.SetInt(int64(vv))
return nil
}
case string: // handle scientific notation
if i, err := strconv.ParseFloat(vv, 64); err == nil {
if 0 <= i && i <= math.MaxUint64 && !dst.OverflowInt(int64(i)) {
dst.SetInt(int64(i))
return nil
}
} else { // couldn't be parsed as float
return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
default:
return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
@@ -899,6 +923,16 @@ func (d *Decoder) decodeValue(ctx context.Context, dst reflect.Value, src ast.No
dst.SetUint(uint64(vv))
return nil
}
case string: // handle scientific notation
if i, err := strconv.ParseFloat(vv, 64); err == nil {
if 0 <= i && i <= math.MaxUint64 && !dst.OverflowUint(uint64(i)) {
dst.SetUint(uint64(i))
return nil
}
} else { // couldn't be parsed as float
return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
default:
return errTypeMismatch(valueType, reflect.TypeOf(v), src.GetToken())
}
@@ -1501,10 +1535,19 @@ func (d *Decoder) decodeMap(ctx context.Context, dst reflect.Value, src ast.Node
}
continue
}
k := reflect.ValueOf(d.nodeToValue(key))
k := d.createDecodableValue(keyType)
if d.canDecodeByUnmarshaler(k) {
if err := d.decodeByUnmarshaler(ctx, k, key); err != nil {
return errors.Wrapf(err, "failed to decode by unmarshaler")
}
} else {
k = reflect.ValueOf(d.nodeToValue(key))
if k.IsValid() && k.Type().ConvertibleTo(keyType) {
k = k.Convert(keyType)
}
}
if k.IsValid() {
if err := d.validateDuplicateKey(keyMap, k.Interface(), key); err != nil {
return errors.Wrapf(err, "invalid map key")
@@ -1621,7 +1664,7 @@ func (d *Decoder) resolveReference() error {
}
}
for _, reader := range d.referenceReaders {
bytes, err := ioutil.ReadAll(reader)
bytes, err := io.ReadAll(reader)
if err != nil {
return errors.Wrapf(err, "failed to read buffer")
}

View File

@@ -2,7 +2,7 @@ package parser
import (
"fmt"
"io/ioutil"
"os"
"strings"
"github.com/goccy/go-yaml/ast"
@@ -730,7 +730,7 @@ func Parse(tokens token.Tokens, mode Mode) (*ast.File, error) {
// Parse parse from filename, and returns ast.File
func ParseFile(filename string, mode Mode) (*ast.File, error) {
file, err := ioutil.ReadFile(filename)
file, err := os.ReadFile(filename)
if err != nil {
return nil, errors.Wrapf(err, "failed to read file: %s", filename)
}

View File

@@ -468,7 +468,7 @@ func (n *rootNode) String() string {
func (n *rootNode) filter(node ast.Node) (ast.Node, error) {
if n.child == nil {
return nil, nil
return node, nil
}
filtered, err := n.child.filter(node)
if err != nil {

View File

@@ -196,9 +196,16 @@ func (c *Context) existsBuffer() bool {
func (c *Context) bufferedSrc() []rune {
src := c.buf[:c.notSpaceCharPos]
if len(src) > 0 && src[len(src)-1] == '\n' && c.isDocument() && c.literalOpt == "-" {
// remove end '\n' character
if c.isDocument() && c.literalOpt == "-" {
// remove end '\n' character and trailing empty lines
// https://yaml.org/spec/1.2.2/#8112-block-chomping-indicator
for {
if len(src) > 0 && src[len(src)-1] == '\n' {
src = src[:len(src)-1]
continue
}
break
}
}
return src
}

View File

@@ -4,8 +4,9 @@ import (
"io"
"strings"
"github.com/goccy/go-yaml/token"
"golang.org/x/xerrors"
"github.com/goccy/go-yaml/token"
)
// IndentState state for indent
@@ -316,100 +317,93 @@ func (s *Scanner) scanDoubleQuote(ctx *Context) (tk *token.Token, pos int) {
continue
} else if c == '\\' {
isFirstLineChar = false
if idx+1 < size {
if idx+1 >= size {
value = append(value, c)
continue
}
nextChar := src[idx+1]
progress := 0
switch nextChar {
case 'b':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, '\b')
idx++
continue
case 'e':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, '\x1B')
idx++
continue
case 'f':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, '\f')
idx++
continue
case 'n':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, '\n')
idx++
continue
case 'r':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, '\r')
idx++
continue
case 'v':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, '\v')
idx++
continue
case 'L': // LS (#x2028)
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, []rune{'\xE2', '\x80', '\xA8'}...)
idx++
continue
case 'N': // NEL (#x85)
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, []rune{'\xC2', '\x85'}...)
idx++
continue
case 'P': // PS (#x2029)
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, []rune{'\xE2', '\x80', '\xA9'}...)
idx++
continue
case '_': // #xA0
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, []rune{'\xC2', '\xA0'}...)
idx++
continue
case '"':
progress = 1
ctx.addOriginBuf(nextChar)
value = append(value, nextChar)
idx++
continue
case 'x':
if idx+3 >= size {
progress = 3
if idx+progress >= size {
// TODO: need to return error
//err = xerrors.New("invalid escape character \\x")
return
}
codeNum := hexRunesToInt(src[idx+2 : idx+4])
codeNum := hexRunesToInt(src[idx+2 : idx+progress+1])
value = append(value, rune(codeNum))
idx += 3
continue
case 'u':
if idx+5 >= size {
progress = 5
if idx+progress >= size {
// TODO: need to return error
//err = xerrors.New("invalid escape character \\u")
return
}
codeNum := hexRunesToInt(src[idx+2 : idx+6])
codeNum := hexRunesToInt(src[idx+2 : idx+progress+1])
value = append(value, rune(codeNum))
idx += 5
continue
case 'U':
if idx+9 >= size {
progress = 9
if idx+progress >= size {
// TODO: need to return error
//err = xerrors.New("invalid escape character \\U")
return
}
codeNum := hexRunesToInt(src[idx+2 : idx+10])
codeNum := hexRunesToInt(src[idx+2 : idx+progress+1])
value = append(value, rune(codeNum))
idx += 9
continue
case '\\':
progress = 1
ctx.addOriginBuf(nextChar)
idx++
}
}
value = append(value, c)
default:
value = append(value, c)
}
idx += progress
s.progressColumn(ctx, progress)
continue
} else if c != '"' {
value = append(value, c)
@@ -621,6 +615,16 @@ func (s *Scanner) scanNewLine(ctx *Context, c rune) {
}
}
// There is no problem that we ignore CR which followed by LF and normalize it to LF, because of following YAML1.2 spec.
// > Line breaks inside scalar content must be normalized by the YAML processor. Each such line break must be parsed into a single line feed character.
// > Outside scalar content, YAML allows any line break to be used to terminate lines.
// > -- https://yaml.org/spec/1.2/spec.html
if c == '\r' && ctx.nextChar() == '\n' {
ctx.addOriginBuf('\r')
ctx.progress(1)
c = '\n'
}
if ctx.isEOS() {
s.addBufferedTokenIfExists(ctx)
} else if s.isAnchor {
@@ -840,15 +844,6 @@ func (s *Scanner) scan(ctx *Context) (pos int) {
return
}
case '\r', '\n':
// There is no problem that we ignore CR which followed by LF and normalize it to LF, because of following YAML1.2 spec.
// > Line breaks inside scalar content must be normalized by the YAML processor. Each such line break must be parsed into a single line feed character.
// > Outside scalar content, YAML allows any line break to be used to terminate lines.
// > -- https://yaml.org/spec/1.2/spec.html
if c == '\r' && ctx.nextChar() == '\n' {
ctx.addOriginBuf('\r')
ctx.progress(1)
c = '\n'
}
s.scanNewLine(ctx, c)
continue
case ' ':

View File

@@ -623,7 +623,7 @@ func IsNeedQuoted(value string) bool {
}
first := value[0]
switch first {
case '*', '&', '[', '{', '}', ']', ',', '!', '|', '>', '%', '\'', '"', '@', ' ':
case '*', '&', '[', '{', '}', ']', ',', '!', '|', '>', '%', '\'', '"', '@', ' ', '`':
return true
}
last := value[len(value)-1]

View File

@@ -447,6 +447,7 @@ const (
CmdClear tpmutil.Command = 0x00000126
CmdHierarchyChangeAuth tpmutil.Command = 0x00000129
CmdDefineSpace tpmutil.Command = 0x0000012A
CmdPCRAllocate tpmutil.Command = 0x0000012B
CmdCreatePrimary tpmutil.Command = 0x00000131
CmdIncrementNVCounter tpmutil.Command = 0x00000134
CmdWriteNV tpmutil.Command = 0x00000137

View File

@@ -51,6 +51,10 @@ func encodeTPMLPCRSelection(sel ...PCRSelection) ([]byte, error) {
return tpmutil.Pack(uint32(0))
}
if len(sel) == 1 && len(sel[0].PCRs) == 0 && sel[0].Hash == 0 {
return tpmutil.Pack(uint32(0))
}
// PCR selection is a variable-size bitmask, where position of a set bit is
// the selected PCR index.
// Size of the bitmask in bytes is pre-pended. It should be at least
@@ -61,10 +65,6 @@ func encodeTPMLPCRSelection(sel ...PCRSelection) ([]byte, error) {
// 00000011 00000000 00000001 00000100
var retBytes []byte
for _, s := range sel {
if len(s.PCRs) == 0 {
return tpmutil.Pack(uint32(0))
}
ts := tpmsPCRSelection{
Hash: s.Hash,
Size: sizeOfPCRSelect,
@@ -1153,6 +1153,34 @@ func Clear(rw io.ReadWriter, handle tpmutil.Handle, auth AuthCommand) error {
return err
}
func encodePCRAllocate(handle tpmutil.Handle, auth AuthCommand, pcrSelection []PCRSelection) ([]byte, error) {
ah, err := tpmutil.Pack(handle)
if err != nil {
return nil, err
}
authEncoded, err := encodeAuthArea(auth)
if err != nil {
return nil, err
}
pcrEncoded, err := encodeTPMLPCRSelection(pcrSelection...)
if err != nil {
return nil, err
}
return concat(ah, authEncoded, pcrEncoded)
}
// PCRAllocate sets the desired PCR allocation of PCR and algorithms.
// The changes take effect once the TPM is restarted.
func PCRAllocate(rw io.ReadWriter, handle tpmutil.Handle, auth AuthCommand, pcrSelection []PCRSelection) error {
Cmd, err := encodePCRAllocate(handle, auth, pcrSelection)
if err != nil {
return err
}
_, err = runCommand(rw, TagSessions, CmdPCRAllocate, tpmutil.RawBytes(Cmd))
return err
}
func encodeHierarchyChangeAuth(handle tpmutil.Handle, auth AuthCommand, newAuth string) ([]byte, error) {
ah, err := tpmutil.Pack(handle)
if err != nil {

View File

@@ -1,20 +0,0 @@
*.log
*.swp
.idea
*.patch
### Go template
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, build with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
.DS_Store
app
demo

View File

View File

@@ -1,20 +0,0 @@
The MIT License (MIT)
Copyright (c) 2016 inhere
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.

View File

@@ -1,580 +0,0 @@
# CLI Color
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/color?style=flat-square)
[![Actions Status](https://github.com/gookit/color/workflows/action-tests/badge.svg)](https://github.com/gookit/color/actions)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/7fef8d74c1d64afc99ce0f2c6d3f8af1)](https://www.codacy.com/gh/gookit/color/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=gookit/color&amp;utm_campaign=Badge_Grade)
[![GoDoc](https://godoc.org/github.com/gookit/color?status.svg)](https://pkg.go.dev/github.com/gookit/color?tab=overview)
[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/color)](https://github.com/gookit/color)
[![Build Status](https://travis-ci.org/gookit/color.svg?branch=master)](https://travis-ci.org/gookit/color)
[![Coverage Status](https://coveralls.io/repos/github/gookit/color/badge.svg?branch=master)](https://coveralls.io/github/gookit/color?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/gookit/color)](https://goreportcard.com/report/github.com/gookit/color)
A command-line color library with 16/256/True color support, universal API methods and Windows support.
> **[中文说明](README.zh-CN.md)**
Basic color preview:
![basic-color](_examples/images/basic-color2.png)
Now, 256 colors and RGB colors have also been supported to work in Windows CMD and PowerShell:
![color-on-cmd-pwsh](_examples/images/color-on-cmd-pwsh.jpg)
## Features
- Simple to use, zero dependencies
- Supports rich color output: 16-color (4-bit), 256-color (8-bit), true color (24-bit, RGB)
- 16-color output is the most commonly used and most widely supported, working on any Windows version
- Since `v1.2.4` **the 256-color (8-bit), true color (24-bit) support windows CMD and PowerShell**
- See [this gist](https://gist.github.com/XVilka/8346728) for information on true color support
- Support converts `HEX` `HSL` value to RGB color
- Generic API methods: `Print`, `Printf`, `Println`, `Sprint`, `Sprintf`
- Supports HTML tag-style color rendering, such as `<green>message</> <fg=red;bg=blue>text</>`.
- In addition to using built-in tags, it also supports custom color attributes
- Custom color attributes support the use of 16 color names, 256 color values, rgb color values and hex color values
- Support working on Windows `cmd` and `powerShell` terminal
- Basic colors: `Bold`, `Black`, `White`, `Gray`, `Red`, `Green`, `Yellow`, `Blue`, `Magenta`, `Cyan`
- Additional styles: `Info`, `Note`, `Light`, `Error`, `Danger`, `Notice`, `Success`, `Comment`, `Primary`, `Warning`, `Question`, `Secondary`
- Support by set `NO_COLOR` for disable color or use `FORCE_COLOR` for force open color render.
- Support Rgb, 256, 16 color conversion
## GoDoc
- [godoc for gopkg](https://pkg.go.dev/gopkg.in/gookit/color.v1)
- [godoc for github](https://pkg.go.dev/github.com/gookit/color)
## Install
```bash
go get github.com/gookit/color
```
## Quick start
```go
package main
import (
"fmt"
"github.com/gookit/color"
)
func main() {
// quick use package func
color.Redp("Simple to use color")
color.Redln("Simple to use color")
color.Greenp("Simple to use color\n")
color.Cyanln("Simple to use color")
color.Yellowln("Simple to use color")
// quick use like fmt.Print*
color.Red.Println("Simple to use color")
color.Green.Print("Simple to use color\n")
color.Cyan.Printf("Simple to use %s\n", "color")
color.Yellow.Printf("Simple to use %s\n", "color")
// use like func
red := color.FgRed.Render
green := color.FgGreen.Render
fmt.Printf("%s line %s library\n", red("Command"), green("color"))
// custom color
color.New(color.FgWhite, color.BgBlack).Println("custom color style")
// can also:
color.Style{color.FgCyan, color.OpBold}.Println("custom color style")
// internal theme/style:
color.Info.Tips("message")
color.Info.Prompt("message")
color.Info.Println("message")
color.Warn.Println("message")
color.Error.Println("message")
// use style tag
color.Print("<suc>he</><comment>llo</>, <cyan>wel</><red>come</>\n")
// Custom label attr: Supports the use of 16 color names, 256 color values, rgb color values and hex color values
color.Println("<fg=11aa23>he</><bg=120,35,156>llo</>, <fg=167;bg=232>wel</><fg=red>come</>")
// apply a style tag
color.Tag("info").Println("info style text")
// prompt message
color.Info.Prompt("prompt style message")
color.Warn.Prompt("prompt style message")
// tips message
color.Info.Tips("tips style message")
color.Warn.Tips("tips style message")
}
```
Run demo: `go run ./_examples/demo.go`
![colored-out](_examples/images/color-demo.jpg)
## Basic/16 color
Supported on any Windows version. Provide generic API methods: `Print`, `Printf`, `Println`, `Sprint`, `Sprintf`
```go
color.Bold.Println("bold message")
color.Cyan.Println("yellow message")
color.Yellow.Println("yellow message")
color.Magenta.Println("yellow message")
// Only use foreground color
color.FgCyan.Printf("Simple to use %s\n", "color")
// Only use background color
color.BgRed.Printf("Simple to use %s\n", "color")
```
Run demo: `go run ./_examples/color_16.go`
![basic-color](_examples/images/basic-color.png)
### Custom build color
```go
// Full custom: foreground, background, option
myStyle := color.New(color.FgWhite, color.BgBlack, color.OpBold)
myStyle.Println("custom color style")
// can also:
color.Style{color.FgCyan, color.OpBold}.Println("custom color style")
```
custom set console settings:
```go
// set console color
color.Set(color.FgCyan)
// print message
fmt.Print("message")
// reset console settings
color.Reset()
```
### Additional styles
provide generic API methods: `Print`, `Printf`, `Println`, `Sprint`, `Sprintf`
print message use defined style:
```go
color.Info.Println("Info message")
color.Notice.Println("Notice message")
color.Error.Println("Error message")
// ...
```
Run demo: `go run ./_examples/theme_basic.go`
![theme-basic](_examples/images/theme-basic.png)
**Tips style**
```go
color.Info.Tips("Info tips message")
color.Notice.Tips("Notice tips message")
color.Error.Tips("Error tips message")
color.Secondary.Tips("Secondary tips message")
```
Run demo: `go run ./_examples/theme_tips.go`
![theme-tips](_examples/images/theme-tips.png)
**Prompt Style**
```go
color.Info.Prompt("Info prompt message")
color.Notice.Prompt("Notice prompt message")
color.Error.Prompt("Error prompt message")
// ...
```
Run demo: `go run ./_examples/theme_prompt.go`
![theme-prompt](_examples/images/theme-prompt.png)
**Block Style**
```go
color.Danger.Block("Danger block message")
color.Warn.Block("Warn block message")
// ...
```
Run demo: `go run ./_examples/theme_block.go`
![theme-block](_examples/images/theme-block.png)
## 256-color usage
> 256 colors support Windows CMD, PowerShell environment after `v1.2.4`
### Set the foreground or background color
- `color.C256(val uint8, isBg ...bool) Color256`
```go
c := color.C256(132) // fg color
c.Println("message")
c.Printf("format %s", "message")
c := color.C256(132, true) // bg color
c.Println("message")
c.Printf("format %s", "message")
```
### 256-color style
Can be used to set foreground and background colors at the same time.
- `S256(fgAndBg ...uint8) *Style256`
```go
s := color.S256(32, 203)
s.Println("message")
s.Printf("format %s", "message")
```
with options:
```go
s := color.S256(32, 203)
s.SetOpts(color.Opts{color.OpBold})
s.Println("style with options")
s.Printf("style with %s\n", "options")
```
Run demo: `go run ./_examples/color_256.go`
![color-tags](_examples/images/color-256.png)
## RGB/True color
> RGB colors support Windows `CMD`, `PowerShell` environment after `v1.2.4`
**Preview:**
> Run demo: `Run demo: go run ./_examples/color_rgb.go`
![color-rgb](_examples/images/color-rgb.png)
example:
```go
color.RGB(30, 144, 255).Println("message. use RGB number")
color.HEX("#1976D2").Println("blue-darken")
color.HEX("#D50000", true).Println("red-accent. use HEX style")
color.RGBStyleFromString("213,0,0").Println("red-accent. use RGB number")
color.HEXStyle("eee", "D50000").Println("deep-purple color")
```
### Set the foreground or background color
- `color.RGB(r, g, b uint8, isBg ...bool) RGBColor`
```go
c := color.RGB(30,144,255) // fg color
c.Println("message")
c.Printf("format %s", "message")
c := color.RGB(30,144,255, true) // bg color
c.Println("message")
c.Printf("format %s", "message")
```
Create a style from an hexadecimal color string:
- `color.HEX(hex string, isBg ...bool) RGBColor`
```go
c := color.HEX("ccc") // can also: "cccccc" "#cccccc"
c.Println("message")
c.Printf("format %s", "message")
c = color.HEX("aabbcc", true) // as bg color
c.Println("message")
c.Printf("format %s", "message")
```
### RGB color style
Can be used to set the foreground and background colors at the same time.
- `color.NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle`
```go
s := color.NewRGBStyle(RGB(20, 144, 234), RGB(234, 78, 23))
s.Println("message")
s.Printf("format %s", "message")
```
Create a style from an hexadecimal color string:
- `color.HEXStyle(fg string, bg ...string) *RGBStyle`
```go
s := color.HEXStyle("11aa23", "eee")
s.Println("message")
s.Printf("format %s", "message")
```
with options:
```go
s := color.HEXStyle("11aa23", "eee")
s.SetOpts(color.Opts{color.OpBold})
s.Println("style with options")
s.Printf("style with %s\n", "options")
```
## HTML-like tag usage
`Print,Printf,Println` functions support auto parse and render color tags.
```go
text := `
<mga1>gookit/color:</>
A <green>command-line</>
<cyan>color library</> with <fg=167;bg=232>256-color</>
and <fg=11aa23;op=bold>True-color</> support,
<fg=mga;op=i>universal API</> methods
and <cyan>Windows</> support.
`
color.Print(text)
```
Preview, code please see [_examples/demo_tag.go](_examples/demo_tag.go):
![demo_tag](_examples/images/demo_tag.png)
**Tag formats:**
- Use built in tags: `<TAG_NAME>CONTENT</>` e.g: `<info>message</>`
- Custom tag attributes: `<fg=VALUE;bg=VALUE;op=VALUES>CONTENT</>` e.g: `<fg=167;bg=232>wel</>`
> **Supported** on Windows `cmd.exe` `PowerShell`.
Examples:
```go
// use style tag
color.Print("<suc>he</><comment>llo</>, <cyan>wel</><red>come</>")
color.Println("<suc>hello</>")
color.Println("<error>hello</>")
color.Println("<warning>hello</>")
// custom color attributes
color.Print("<fg=yellow;bg=black;op=underscore;>hello, welcome</>\n")
// Custom label attr: Supports the use of 16 color names, 256 color values, rgb color values and hex color values
color.Println("<fg=11aa23>he</><bg=120,35,156>llo</>, <fg=167;bg=232>wel</><fg=red>come</>")
```
### Tag attributes
tag attributes format:
```text
attr format:
// VALUE please see var: FgColors, BgColors, AllOptions
"fg=VALUE;bg=VALUE;op=VALUE"
16 color:
"fg=yellow"
"bg=red"
"op=bold,underscore" // option is allow multi value
"fg=white;bg=blue;op=bold"
"fg=white;op=bold,underscore"
256 color:
"fg=167"
"fg=167;bg=23"
"fg=167;bg=23;op=bold"
True color:
// hex
"fg=fc1cac"
"fg=fc1cac;bg=c2c3c4"
// r,g,b
"fg=23,45,214"
"fg=23,45,214;bg=109,99,88"
```
> tag attributes parse please see `func ParseCodeFromAttr()`
### Built-in tags
Built-in tags please see var `colorTags` in [color_tag.go](color_tag.go)
```go
// use style tag
color.Print("<suc>he</><comment>llo</>, <cyan>wel</><red>come</>")
color.Println("<suc>hello</>")
color.Println("<error>hello</>")
```
Run demo: `go run ./_examples/color_tag.go`
![color-tags](_examples/images/color-tags.png)
**Use `color.Tag` build message**:
```go
// set a style tag
color.Tag("info").Print("info style text")
color.Tag("info").Printf("%s style text", "info")
color.Tag("info").Println("info style text")
```
## Color convert
Supports conversion between Rgb, 256, 16 colors, `Rgb <=> 256 <=> 16`
```go
basic := color.Red
basic.Println("basic color")
c256 := color.Red.C256()
c256.Println("256 color")
c256.C16().Println("basic color")
rgb := color.Red.RGB()
rgb.Println("rgb color")
rgb.C256().Println("256 color")
```
### Convert utils
`color` has many built-in color conversion utility functions.
```go
func Basic2hex(val uint8) string
func Bg2Fg(val uint8) uint8
func Fg2Bg(val uint8) uint8
func C256ToRgb(val uint8) (rgb []uint8)
func C256ToRgbV1(val uint8) (rgb []uint8)
func Hex2basic(hex string, asBg ...bool) uint8
func Hex2rgb(hex string) []int
func HexToRGB(hex string) []int
func HexToRgb(hex string) (rgb []int)
func HslIntToRgb(h, s, l int) (rgb []uint8)
func HslToRgb(h, s, l float64) (rgb []uint8)
func HsvToRgb(h, s, v int) (rgb []uint8)
func Rgb2ansi(r, g, b uint8, isBg bool) uint8
func Rgb2basic(r, g, b uint8, isBg bool) uint8
func Rgb2hex(rgb []int) string
func Rgb2short(r, g, b uint8) uint8
func RgbTo256(r, g, b uint8) uint8
func RgbTo256Table() map[string]uint8
func RgbToAnsi(r, g, b uint8, isBg bool) uint8
func RgbToHex(rgb []int) string
func RgbToHsl(r, g, b uint8) []float64
func RgbToHslInt(r, g, b uint8) []int
```
**Convert to `RGBColor`**:
- `func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor`
- `func RGBFromString(rgb string, isBg ...bool) RGBColor`
- `func HEX(hex string, isBg ...bool) RGBColor`
- `func HSL(h, s, l float64, isBg ...bool) RGBColor`
- `func HSLInt(h, s, l int, isBg ...bool) RGBColor`
## Util functions
There are some useful functions reference
- `Disable()` disable color render
- `SetOutput(io.Writer)` custom set the colored text output writer
- `ForceOpenColor()` force open color render
- `Colors2code(colors ...Color) string` Convert colors to code. return like "32;45;3"
- `ClearCode(str string) string` Use for clear color codes
- `ClearTag(s string) string` clear all color html-tag for a string
- `IsConsole(w io.Writer)` Determine whether w is one of stderr, stdout, stdin
> More useful func please see https://pkg.go.dev/github.com/gookit/color
### Detect color level
`color` automatically checks the color levels supported by the current environment.
```go
// Level is the color level supported by a terminal.
type Level = terminfo.ColorLevel
// terminal color available level alias of the terminfo.ColorLevel*
const (
LevelNo = terminfo.ColorLevelNone // not support color.
Level16 = terminfo.ColorLevelBasic // basic - 3/4 bit color supported
Level256 = terminfo.ColorLevelHundreds // hundreds - 8-bit color supported
LevelRgb = terminfo.ColorLevelMillions // millions - (24 bit)true color supported
)
```
- `func SupportColor() bool` Whether the current environment supports color output
- `func Support256Color() bool` Whether the current environment supports 256-color output
- `func SupportTrueColor() bool` Whether the current environment supports (RGB)True-color output
- `func TermColorLevel() Level` Get the currently supported color level
## Projects using color
Check out these projects, which use https://github.com/gookit/color :
- https://github.com/Delta456/box-cli-maker Make Highly Customized Boxes for your CLI
- https://github.com/flipped-aurora/gin-vue-admin 基于gin+vue搭建的后台系统框架
- https://github.com/JanDeDobbeleer/oh-my-posh A prompt theme engine for any shell.
- https://github.com/jesseduffield/lazygit Simple terminal UI for git commands
- https://github.com/olivia-ai/olivia 💁Your new best friend powered by an artificial neural network
- https://github.com/pterm/pterm PTerm is a modern Go module to beautify console output. Featuring charts, progressbars, tables, trees, etc.
- https://github.com/securego/gosec Golang security checker
- https://github.com/TNK-Studio/lazykube ⎈ The lazier way to manage kubernetes.
- [+ See More](https://pkg.go.dev/github.com/gookit/color?tab=importedby)
## Gookit packages
- [gookit/ini](https://github.com/gookit/ini) Go config management, use INI files
- [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP
- [gookit/gcli](https://github.com/gookit/gcli) build CLI application, tool library, running CLI commands
- [gookit/slog](https://github.com/gookit/slog) Concise and extensible go log library
- [gookit/event](https://github.com/gookit/event) Lightweight event manager and dispatcher implements by Go
- [gookit/cache](https://github.com/gookit/cache) Generic cache use and cache manager for golang. support File, Memory, Redis, Memcached.
- [gookit/config](https://github.com/gookit/config) Go config management. support JSON, YAML, TOML, INI, HCL, ENV and Flags
- [gookit/color](https://github.com/gookit/color) A command-line color library with true color support, universal API methods and Windows support
- [gookit/filter](https://github.com/gookit/filter) Provide filtering, sanitizing, and conversion of golang data
- [gookit/validate](https://github.com/gookit/validate) Use for data validation and filtering. support Map, Struct, Form data
- [gookit/goutil](https://github.com/gookit/goutil) Some utils for the Go: string, array/slice, map, format, cli, env, filesystem, test and more
- More, please see https://github.com/gookit
## See also
- [inhere/console](https://github.com/inhere/php-console)
- [xo/terminfo](https://github.com/xo/terminfo)
- [beego/bee](https://github.com/beego/bee)
- [issue9/term](https://github.com/issue9/term)
- [muesli/termenv](https://github.com/muesli/termenv)
- [ANSI escape code](https://en.wikipedia.org/wiki/ANSI_escape_code)
- [Standard ANSI color map](https://conemu.github.io/en/AnsiEscapeCodes.html#Standard_ANSI_color_map)
- [Terminal Colors](https://gist.github.com/XVilka/8346728)
## License
[MIT](/LICENSE)

View File

@@ -1,591 +0,0 @@
# CLI Color
![GitHub go.mod Go version](https://img.shields.io/github/go-mod/go-version/gookit/color?style=flat-square)
[![Actions Status](https://github.com/gookit/color/workflows/action-tests/badge.svg)](https://github.com/gookit/color/actions)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/7fef8d74c1d64afc99ce0f2c6d3f8af1)](https://www.codacy.com/gh/gookit/color/dashboard?utm_source=github.com&amp;utm_medium=referral&amp;utm_content=gookit/color&amp;utm_campaign=Badge_Grade)
[![GoDoc](https://godoc.org/github.com/gookit/color?status.svg)](https://pkg.go.dev/github.com/gookit/color?tab=overview)
[![GitHub tag (latest SemVer)](https://img.shields.io/github/tag/gookit/color)](https://github.com/gookit/color)
[![Build Status](https://travis-ci.org/gookit/color.svg?branch=master)](https://travis-ci.org/gookit/color)
[![Coverage Status](https://coveralls.io/repos/github/gookit/color/badge.svg?branch=master)](https://coveralls.io/github/gookit/color?branch=master)
[![Go Report Card](https://goreportcard.com/badge/github.com/gookit/color)](https://goreportcard.com/report/github.com/gookit/color)
Golang下的命令行色彩使用库, 拥有丰富的色彩(16/256/True)渲染输出通用的API方法兼容Windows系统
> **[EN README](README.md)**
基本颜色预览:
![basic-color](_examples/images/basic-color2.png)
现在256色和RGB色彩也已经支持windows CMD和PowerShell中工作
![color-on-cmd-pwsh](_examples/images/color-on-cmd-pwsh.jpg)
## 功能特色
- 使用简单方便
- 支持丰富的颜色输出, 16色(4bit)256色(8bit)RGB色彩(24bit, RGB)
- 16色(4bit)是最常用和支持最广的支持Windows `cmd.exe`
-`v1.2.4`**256色(8bit)RGB色彩(24bit)均支持Windows CMD和PowerShell终端**
- 请查看 [this gist](https://gist.github.com/XVilka/8346728) 了解支持RGB色彩的终端
- 支持转换 `HEX` `HSL` 等为RGB色彩
- 提供通用的API方法`Print` `Printf` `Println` `Sprint` `Sprintf`
- 同时支持html标签式的颜色渲染除了使用内置标签同时支持自定义颜色属性
- 例如: `this an <green>message</> <fg=red;bg=blue>text</>` 标签内部文本将会渲染对应色彩
- 自定义颜色属性: 支持使用16色彩名称256色彩值rgb色彩值以及hex色彩值
- 基础色彩: `Bold` `Black` `White` `Gray` `Red` `Green` `Yellow` `Blue` `Magenta` `Cyan`
- 扩展风格: `Info` `Note` `Light` `Error` `Danger` `Notice` `Success` `Comment` `Primary` `Warning` `Question` `Secondary`
- 支持通过设置环境变量 `NO_COLOR` 来禁用色彩,或者使用 `FORCE_COLOR` 来强制使用色彩渲染.
- 支持 Rgb, 256, 16 色彩之间的互相转换
- 支持Linux、Mac同时兼容Windows系统环境
## GoDoc
- [godoc for gopkg](https://pkg.go.dev/gopkg.in/gookit/color.v1)
- [godoc for github](https://pkg.go.dev/github.com/gookit/color)
## 安装
```bash
go get github.com/gookit/color
```
## 快速开始
如下,引入当前包就可以快速的使用
```go
package main
import (
"fmt"
"github.com/gookit/color"
)
func main() {
// 简单快速的使用,跟 fmt.Print* 类似
color.Redp("Simple to use color")
color.Redln("Simple to use color")
color.Greenp("Simple to use color\n")
color.Cyanln("Simple to use color")
color.Yellowln("Simple to use color")
// 简单快速的使用,跟 fmt.Print* 类似
color.Red.Println("Simple to use color")
color.Green.Print("Simple to use color\n")
color.Cyan.Printf("Simple to use %s\n", "color")
color.Yellow.Printf("Simple to use %s\n", "color")
// use like func
red := color.FgRed.Render
green := color.FgGreen.Render
fmt.Printf("%s line %s library\n", red("Command"), green("color"))
// 自定义颜色
color.New(color.FgWhite, color.BgBlack).Println("custom color style")
// 也可以:
color.Style{color.FgCyan, color.OpBold}.Println("custom color style")
// internal style:
color.Info.Println("message")
color.Warn.Println("message")
color.Error.Println("message")
// 使用内置颜色标签
color.Print("<suc>he</><comment>llo</>, <cyan>wel</><red>come</>\n")
// 自定义标签: 支持使用16色彩名称256色彩值rgb色彩值以及hex色彩值
color.Println("<fg=11aa23>he</><bg=120,35,156>llo</>, <fg=167;bg=232>wel</><fg=red>come</>")
// apply a style tag
color.Tag("info").Println("info style text")
// prompt message
color.Info.Prompt("prompt style message")
color.Warn.Prompt("prompt style message")
// tips message
color.Info.Tips("tips style message")
color.Warn.Tips("tips style message")
}
```
> 运行 demo: `go run ./_examples/demo.go`
![colored-out](_examples/images/color-demo.jpg)
## 基础颜色(16-color)
提供通用的API方法`Print` `Printf` `Println` `Sprint` `Sprintf`
> 支持在windows `cmd.exe` `powerShell` 等终端使用
```go
color.Bold.Println("bold message")
color.Black.Println("bold message")
color.White.Println("bold message")
// ...
// Only use foreground color
color.FgCyan.Printf("Simple to use %s\n", "color")
// Only use background color
color.BgRed.Printf("Simple to use %s\n", "color")
```
> 运行demo: `go run ./_examples/color_16.go`
![basic-color](_examples/images/basic-color.png)
### 构建风格
```go
// 仅设置前景色
color.FgCyan.Printf("Simple to use %s\n", "color")
// 仅设置背景色
color.BgRed.Printf("Simple to use %s\n", "color")
// 完全自定义: 前景色 背景色 选项
style := color.New(color.FgWhite, color.BgBlack, color.OpBold)
style.Println("custom color style")
// 也可以:
color.Style{color.FgCyan, color.OpBold}.Println("custom color style")
```
直接设置控制台属性:
```go
// 设置console颜色
color.Set(color.FgCyan)
// 输出信息
fmt.Print("message")
// 重置console颜色
color.Reset()
```
> 当然color已经内置丰富的色彩风格支持
### 扩展风格方法
提供通用的API方法`Print` `Printf` `Println` `Sprint` `Sprintf`
> 支持在windows `cmd.exe` `powerShell` 等终端使用
基础使用:
```go
// print message
color.Info.Println("Info message")
color.Note.Println("Note message")
color.Notice.Println("Notice message")
// ...
```
Run demo: `go run ./_examples/theme_basic.go`
![theme-basic](_examples/images/theme-basic.png)
**简约提示风格**
```go
color.Info.Tips("Info tips message")
color.Notice.Tips("Notice tips message")
color.Error.Tips("Error tips message")
// ...
```
Run demo: `go run ./_examples/theme_tips.go`
![theme-tips](_examples/images/theme-tips.png)
**着重提示风格**
```go
color.Info.Prompt("Info prompt message")
color.Error.Prompt("Error prompt message")
color.Danger.Prompt("Danger prompt message")
```
Run demo: `go run ./_examples/theme_prompt.go`
![theme-prompt](_examples/images/theme-prompt.png)
**强调提示风格**
```go
color.Warn.Block("Warn block message")
color.Debug.Block("Debug block message")
color.Question.Block("Question block message")
```
Run demo: `go run ./_examples/theme_block.go`
![theme-block](_examples/images/theme-block.png)
## 256 色彩使用
> 256色彩在 `v1.2.4` 后支持Windows CMD,PowerShell 环境
### 使用前景或后景色
- `color.C256(val uint8, isBg ...bool) Color256`
```go
c := color.C256(132) // fg color
c.Println("message")
c.Printf("format %s", "message")
c := color.C256(132, true) // bg color
c.Println("message")
c.Printf("format %s", "message")
```
### 使用256 色彩风格
> 可同时设置前景和背景色
- `color.S256(fgAndBg ...uint8) *Style256`
```go
s := color.S256(32, 203)
s.Println("message")
s.Printf("format %s", "message")
```
可以同时添加选项设置:
```go
s := color.S256(32, 203)
s.SetOpts(color.Opts{color.OpBold})
s.Println("style with options")
s.Printf("style with %s\n", "options")
```
> 运行 demo: `go run ./_examples/color_256.go`
![color-tags](_examples/images/color-256.png)
## RGB/True色彩使用
> RGB色彩在 `v1.2.4` 后支持 Windows `CMD`, `PowerShell` 环境
**效果预览:**
> 运行 demo: `Run demo: go run ./_examples/color_rgb.go`
![color-rgb](_examples/images/color-rgb.png)
代码示例:
```go
color.RGB(30, 144, 255).Println("message. use RGB number")
color.HEX("#1976D2").Println("blue-darken")
color.HEX("#D50000", true).Println("red-accent. use HEX style")
color.RGBStyleFromString("213,0,0").Println("red-accent. use RGB number")
color.HEXStyle("eee", "D50000").Println("deep-purple color")
```
### 使用前景或后景色
- `color.RGB(r, g, b uint8, isBg ...bool) RGBColor`
```go
c := color.RGB(30,144,255) // fg color
c.Println("message")
c.Printf("format %s", "message")
c := color.RGB(30,144,255, true) // bg color
c.Println("message")
c.Printf("format %s", "message")
```
- `color.HEX(hex string, isBg ...bool) RGBColor` 从16进制颜色创建
```go
c := color.HEX("ccc") // 也可以写为: "cccccc" "#cccccc"
c.Println("message")
c.Printf("format %s", "message")
c = color.HEX("aabbcc", true) // as bg color
c.Println("message")
c.Printf("format %s", "message")
```
### 使用RGB风格
> TIP: 可同时设置前景和背景色
- `color.NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle`
```go
s := color.NewRGBStyle(RGB(20, 144, 234), RGB(234, 78, 23))
s.Println("message")
s.Printf("format %s", "message")
```
- `color.HEXStyle(fg string, bg ...string) *RGBStyle` 从16进制颜色创建
```go
s := color.HEXStyle("11aa23", "eee")
s.Println("message")
s.Printf("format %s", "message")
```
- 可以同时添加选项设置:
```go
s := color.HEXStyle("11aa23", "eee")
s.SetOpts(color.Opts{color.OpBold})
s.Println("style with options")
s.Printf("style with %s\n", "options")
```
## 使用颜色标签
`Print,Printf,Println` 等方法支持自动解析并渲染 HTML 风格的颜色标签
> **支持** 在windows `cmd.exe` `PowerShell` 使用
简单示例:
```go
text := `
<mga1>gookit/color:</>
A <green>command-line</>
<cyan>color library</> with <fg=167;bg=232>256-color</>
and <fg=11aa23;op=bold>True-color</> support,
<fg=mga;op=i>universal API</> methods
and <cyan>Windows</> support.
`
color.Print(text)
```
输出效果, 示例代码请看 [_examples/demo_tag.go](_examples/demo_tag.go):
![demo_tag](_examples/images/demo_tag.png)
**颜色标签格式:**
- 直接使用内置风格标签: `<TAG_NAME>CONTENT</>` e.g: `<info>message</>`
- 自定义标签属性: `<fg=VALUE;bg=VALUE;op=VALUES>CONTENT</>` e.g: `<fg=167;bg=232>wel</>`
使用内置的颜色标签,可以非常方便简单的构建自己需要的任何格式
> 同时支持自定义颜色属性: 支持使用16色彩名称256色彩值rgb色彩值以及hex色彩值
```go
// 使用内置的 color tag
color.Print("<suc>he</><comment>llo</>, <cyan>wel</><red>come</>")
color.Println("<suc>hello</>")
color.Println("<error>hello</>")
color.Println("<warning>hello</>")
// 自定义颜色属性
color.Print("<fg=yellow;bg=black;op=underscore;>hello, welcome</>\n")
// 自定义颜色属性: 支持使用16色彩名称256色彩值rgb色彩值以及hex色彩值
color.Println("<fg=11aa23>he</><bg=120,35,156>llo</>, <fg=167;bg=232>wel</><fg=red>come</>")
```
### 自定义标签属性
标签属性格式:
```text
attr format:
// VALUE please see var: FgColors, BgColors, AllOptions
"fg=VALUE;bg=VALUE;op=VALUE"
16 color:
"fg=yellow"
"bg=red"
"op=bold,underscore" // option is allow multi value
"fg=white;bg=blue;op=bold"
"fg=white;op=bold,underscore"
256 color:
"fg=167"
"fg=167;bg=23"
"fg=167;bg=23;op=bold"
True color:
// hex
"fg=fc1cac"
"fg=fc1cac;bg=c2c3c4"
// r,g,b
"fg=23,45,214"
"fg=23,45,214;bg=109,99,88"
```
> tag attributes parse please see `func ParseCodeFromAttr()`
### 内置标签
内置标签请参见变量 `colorTags` 定义, 源文件 [color_tag.go](color_tag.go)
```go
// use style tag
color.Print("<suc>he</><comment>llo</>, <cyan>wel</><red>come</>")
color.Println("<suc>hello</>")
color.Println("<error>hello</>")
```
> 运行 demo: `go run ./_examples/color_tag.go`
![color-tags](_examples/images/color-tags.png)
**使用 `color.Tag` 包装标签**:
可以使用通用的输出API方法,给后面输出的文本信息加上给定的颜色风格标签
```go
// set a style tag
color.Tag("info").Print("info style text")
color.Tag("info").Printf("%s style text", "info")
color.Tag("info").Println("info style text")
```
## 颜色转换
支持 Rgb, 256, 16 色彩之间的互相转换 `Rgb <=> 256 <=> 16`
```go
basic := color.Red
basic.Println("basic color")
c256 := color.Red.C256()
c256.Println("256 color")
c256.C16().Println("basic color")
rgb := color.Red.RGB()
rgb.Println("rgb color")
rgb.C256().Println("256 color")
```
### 颜色转换方法
`color` 内置了许多颜色转换工具方法
```go
func Basic2hex(val uint8) string
func Bg2Fg(val uint8) uint8
func Fg2Bg(val uint8) uint8
func C256ToRgb(val uint8) (rgb []uint8)
func C256ToRgbV1(val uint8) (rgb []uint8)
func Hex2basic(hex string, asBg ...bool) uint8
func Hex2rgb(hex string) []int
func HexToRGB(hex string) []int
func HexToRgb(hex string) (rgb []int)
func HslIntToRgb(h, s, l int) (rgb []uint8)
func HslToRgb(h, s, l float64) (rgb []uint8)
func HsvToRgb(h, s, v int) (rgb []uint8)
func Rgb2ansi(r, g, b uint8, isBg bool) uint8
func Rgb2basic(r, g, b uint8, isBg bool) uint8
func Rgb2hex(rgb []int) string
func Rgb2short(r, g, b uint8) uint8
func RgbTo256(r, g, b uint8) uint8
func RgbTo256Table() map[string]uint8
func RgbToAnsi(r, g, b uint8, isBg bool) uint8
func RgbToHex(rgb []int) string
func RgbToHsl(r, g, b uint8) []float64
func RgbToHslInt(r, g, b uint8) []int
```
**转换为 `RGBColor`**:
- `func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor`
- `func RGBFromString(rgb string, isBg ...bool) RGBColor`
- `func HEX(hex string, isBg ...bool) RGBColor`
- `func HSL(h, s, l float64, isBg ...bool) RGBColor`
- `func HSLInt(h, s, l int, isBg ...bool) RGBColor`
## 工具方法参考
一些有用的工具方法参考
- `Disable()` 禁用颜色渲染输出
- `SetOutput(io.Writer)` 自定义设置渲染后的彩色文本输出位置
- `ForceOpenColor()` 强制开启颜色渲染
- `ClearCode(str string) string` Use for clear color codes
- `Colors2code(colors ...Color) string` Convert colors to code. return like "32;45;3"
- `ClearTag(s string) string` clear all color html-tag for a string
- `IsConsole(w io.Writer)` Determine whether w is one of stderr, stdout, stdin
- 更多请查看文档 https://pkg.go.dev/github.com/gookit/color
### 检测支持的颜色级别
`color` 会自动检查当前环境支持的颜色级别
```go
// Level is the color level supported by a terminal.
type Level = terminfo.ColorLevel
// terminal color available level alias of the terminfo.ColorLevel*
const (
LevelNo = terminfo.ColorLevelNone // not support color.
Level16 = terminfo.ColorLevelBasic // basic - 3/4 bit color supported
Level256 = terminfo.ColorLevelHundreds // hundreds - 8-bit color supported
LevelRgb = terminfo.ColorLevelMillions // millions - (24 bit)true color supported
)
```
- `func SupportColor() bool` 当前环境是否支持色彩输出
- `func Support256Color() bool` 当前环境是否支持256色彩输出
- `func SupportTrueColor() bool` 当前环境是否支持(RGB)True色彩输出
- `func TermColorLevel() Level` 获取当前支持的颜色级别
## 使用Color的项目
看看这些使用了 https://github.com/gookit/color 的项目:
- https://github.com/Delta456/box-cli-maker Make Highly Customized Boxes for your CLI
- https://github.com/flipped-aurora/gin-vue-admin 基于gin+vue搭建的后台系统框架
- https://github.com/JanDeDobbeleer/oh-my-posh A prompt theme engine for any shell.
- https://github.com/jesseduffield/lazygit Simple terminal UI for git commands
- https://github.com/olivia-ai/olivia 💁Your new best friend powered by an artificial neural network
- https://github.com/pterm/pterm PTerm is a modern Go module to beautify console output. Featuring charts, progressbars, tables, trees, etc.
- https://github.com/securego/gosec Golang security checker
- https://github.com/TNK-Studio/lazykube ⎈ The lazier way to manage kubernetes.
- [+ See More](https://pkg.go.dev/github.com/gookit/color?tab=importedby)
## Gookit 工具包
- [gookit/ini](https://github.com/gookit/ini) INI配置读取管理支持多文件加载数据覆盖合并, 解析ENV变量, 解析变量引用
- [gookit/rux](https://github.com/gookit/rux) Simple and fast request router for golang HTTP
- [gookit/gcli](https://github.com/gookit/gcli) Go的命令行应用工具库运行CLI命令支持命令行色彩用户交互进度显示数据格式化显示
- [gookit/slog](https://github.com/gookit/slog) 简洁易扩展的go日志库
- [gookit/event](https://github.com/gookit/event) Go实现的轻量级的事件管理、调度程序库, 支持设置监听器的优先级, 支持对一组事件进行监听
- [gookit/cache](https://github.com/gookit/cache) 通用的缓存使用包装库通过包装各种常用的驱动来提供统一的使用API
- [gookit/config](https://github.com/gookit/config) Go应用配置管理支持多种格式JSON, YAML, TOML, INI, HCL, ENV, Flags多文件加载远程文件加载数据合并
- [gookit/color](https://github.com/gookit/color) CLI 控制台颜色渲染工具库, 拥有简洁的使用API支持16色256色RGB色彩渲染输出
- [gookit/filter](https://github.com/gookit/filter) 提供对Golang数据的过滤净化转换
- [gookit/validate](https://github.com/gookit/validate) Go通用的数据验证与过滤库使用简单内置大部分常用验证、过滤器
- [gookit/goutil](https://github.com/gookit/goutil) Go 的一些工具函数,格式化,特殊处理,常用信息获取等
- 更多请查看 https://github.com/gookit
## 参考项目
- [inhere/console](https://github.com/inhere/php-console)
- [muesli/termenv](https://github.com/muesli/termenv)
- [xo/terminfo](https://github.com/xo/terminfo)
- [beego/bee](https://github.com/beego/bee)
- [issue9/term](https://github.com/issue9/term)
- [ANSI转义序列](https://zh.wikipedia.org/wiki/ANSI转义序列)
- [Standard ANSI color map](https://conemu.github.io/en/AnsiEscapeCodes.html#Standard_ANSI_color_map)
- [Terminal Colors](https://gist.github.com/XVilka/8346728)
## License
MIT

View File

@@ -1,6 +0,0 @@
//go:build !go1.18
// +build !go1.18
package color
type any = interface{}

View File

@@ -1,251 +0,0 @@
/*
Package color is command line color library.
Support rich color rendering output, universal API method, compatible with Windows system
Source code and other details for the project are available at GitHub:
https://github.com/gookit/color
More usage please see README and tests.
*/
package color
import (
"fmt"
"io"
"os"
"regexp"
"strings"
"github.com/xo/terminfo"
)
// color render templates
//
// ESC 操作的表示:
//
// "\033"(Octal 8进制) = "\x1b"(Hexadecimal 16进制) = 27 (10进制)
const (
// StartSet chars
StartSet = "\x1b["
// ResetSet close all properties.
ResetSet = "\x1b[0m"
// SettingTpl string.
SettingTpl = "\x1b[%sm"
// FullColorTpl for build color code
FullColorTpl = "\x1b[%sm%s\x1b[0m"
// CodeSuffix string for color code.
CodeSuffix = "[0m"
)
// CodeExpr regex to clear color codes eg "\033[1;36mText\x1b[0m"
const CodeExpr = `\033\[[\d;?]+m`
var (
// Enable switch color render and display
//
// NOTICE:
// if ENV: NO_COLOR is not empty, will disable color render.
Enable = os.Getenv("NO_COLOR") == ""
// RenderTag render HTML tag on call color.Xprint, color.PrintX
RenderTag = true
// debug mode for development.
//
// set env:
// COLOR_DEBUG_MODE=on
// or:
// COLOR_DEBUG_MODE=on go run ./_examples/envcheck.go
debugMode = os.Getenv("COLOR_DEBUG_MODE") == "on"
// inner errors record on detect color level
innerErrs []error
// output the default io.Writer message print
output io.Writer = os.Stdout
// mark current env, It's like in `cmd.exe`
// if not in windows, it's always False.
isLikeInCmd bool
// the color support level for current terminal
// needVTP - need enable VTP, only for Windows OS
colorLevel, needVTP = detectTermColorLevel()
// match color codes
codeRegex = regexp.MustCompile(CodeExpr)
// mark current env is support color.
// Always: isLikeInCmd != supportColor
// supportColor = IsSupportColor()
)
// TermColorLevel Get the currently supported color level
func TermColorLevel() Level {
return colorLevel
}
// SupportColor Whether the current environment supports color output
func SupportColor() bool {
return colorLevel > terminfo.ColorLevelNone
}
// Support16Color on the current ENV
// func Support16Color() bool {
// return colorLevel > terminfo.ColorLevelNone
// }
// Support256Color Whether the current environment supports 256-color output
func Support256Color() bool {
return colorLevel > terminfo.ColorLevelBasic
}
// SupportTrueColor Whether the current environment supports (RGB)True-color output
func SupportTrueColor() bool {
return colorLevel > terminfo.ColorLevelHundreds
}
/*************************************************************
* global settings
*************************************************************/
// Set console color attributes
func Set(colors ...Color) (int, error) {
code := Colors2code(colors...)
err := SetTerminal(code)
return 0, err
}
// Reset reset console color attributes
func Reset() (int, error) {
err := ResetTerminal()
return 0, err
}
// Disable disable color output
func Disable() bool {
oldVal := Enable
Enable = false
return oldVal
}
// NotRenderTag on call color.Xprint, color.PrintX
func NotRenderTag() {
RenderTag = false
}
// SetOutput set default colored text output
func SetOutput(w io.Writer) {
output = w
}
// ResetOutput reset output
func ResetOutput() {
output = os.Stdout
}
// ResetOptions reset all package option setting
func ResetOptions() {
RenderTag = true
Enable = true
output = os.Stdout
}
// ForceSetColorLevel force open color render
func ForceSetColorLevel(level terminfo.ColorLevel) terminfo.ColorLevel {
oldLevelVal := colorLevel
colorLevel = level
return oldLevelVal
}
// ForceColor force open color render
func ForceColor() terminfo.ColorLevel {
return ForceOpenColor()
}
// ForceOpenColor force open color render
func ForceOpenColor() terminfo.ColorLevel {
// TODO should set level to ?
return ForceSetColorLevel(terminfo.ColorLevelMillions)
}
// IsLikeInCmd check result
//
// Deprecated: please don't use
func IsLikeInCmd() bool {
return isLikeInCmd
}
// InnerErrs info
func InnerErrs() []error {
return innerErrs
}
/*************************************************************
* render color code
*************************************************************/
// RenderCode render message by color code.
//
// Usage:
//
// msg := RenderCode("3;32;45", "some", "message")
func RenderCode(code string, args ...any) string {
var message string
if ln := len(args); ln == 0 {
return ""
}
message = fmt.Sprint(args...)
if len(code) == 0 {
return message
}
// disabled OR not support color
if !Enable || !SupportColor() {
return ClearCode(message)
}
// return fmt.Sprintf(FullColorTpl, code, message)
return StartSet + code + "m" + message + ResetSet
}
// RenderWithSpaces Render code with spaces.
// If the number of args is > 1, a space will be added between the args
func RenderWithSpaces(code string, args ...any) string {
msg := formatArgsForPrintln(args)
if len(code) == 0 {
return msg
}
// disabled OR not support color
if !Enable || !SupportColor() {
return ClearCode(msg)
}
return StartSet + code + "m" + msg + ResetSet
}
// RenderString render a string with color code.
//
// Usage:
//
// msg := RenderString("3;32;45", "a message")
func RenderString(code string, str string) string {
if len(code) == 0 || str == "" {
return str
}
// disabled OR not support color
if !Enable || !SupportColor() {
return ClearCode(str)
}
// return fmt.Sprintf(FullColorTpl, code, str)
return StartSet + code + "m" + str + ResetSet
}
// ClearCode clear color codes.
//
// eg:
//
// "\033[36;1mText\x1b[0m" -> "Text"
func ClearCode(str string) string {
if !strings.Contains(str, CodeSuffix) {
return str
}
return codeRegex.ReplaceAllString(str, "")
}

View File

@@ -1,303 +0,0 @@
package color
import (
"fmt"
"strconv"
"strings"
)
/*
from wikipedia, 256 color:
ESC[ … 38;5;<n> … m选择前景色
ESC[ … 48;5;<n> … m选择背景色
0- 7标准颜色同 ESC[3037m
8- 15高强度颜色同 ESC[9097m
16-2316 × 6 × 6 立方216色: 16 + 36 × r + 6 × g + b (0 ≤ r, g, b ≤ 5)
232-255从黑到白的24阶灰度色
*/
// tpl for 8 bit 256 color(`2^8`)
//
// format:
//
// ESC[ … 38;5;<n> … m // 选择前景色
// ESC[ … 48;5;<n> … m // 选择背景色
//
// example:
//
// fg "\x1b[38;5;242m"
// bg "\x1b[48;5;208m"
// both "\x1b[38;5;242;48;5;208m"
//
// links:
//
// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#8位
const (
TplFg256 = "38;5;%d"
TplBg256 = "48;5;%d"
Fg256Pfx = "38;5;"
Bg256Pfx = "48;5;"
)
/*************************************************************
* 8bit(256) Color: Bit8Color Color256
*************************************************************/
// Color256 256 color (8 bit), uint8 range at 0 - 255.
// Support 256 color on windows CMD, PowerShell
//
// 颜色值使用10进制和16进制都可 0x98 = 152
//
// The color consists of two uint8:
//
// 0: color value
// 1: color type; Fg=0, Bg=1, >1: unset value
//
// example:
//
// fg color: [152, 0]
// bg color: [152, 1]
//
// lint warn - Name starts with package name
type Color256 [2]uint8
type Bit8Color = Color256 // alias
var emptyC256 = Color256{1: 99}
// Bit8 create a color256
func Bit8(val uint8, isBg ...bool) Color256 {
return C256(val, isBg...)
}
// C256 create a color256
func C256(val uint8, isBg ...bool) Color256 {
bc := Color256{val}
// mark is bg color
if len(isBg) > 0 && isBg[0] {
bc[1] = AsBg
}
return bc
}
// Set terminal by 256 color code
func (c Color256) Set() error {
return SetTerminal(c.String())
}
// Reset terminal. alias of the ResetTerminal()
func (c Color256) Reset() error {
return ResetTerminal()
}
// Print print message
func (c Color256) Print(a ...any) {
doPrintV2(c.String(), fmt.Sprint(a...))
}
// Printf format and print message
func (c Color256) Printf(format string, a ...any) {
doPrintV2(c.String(), fmt.Sprintf(format, a...))
}
// Println print message with newline
func (c Color256) Println(a ...any) {
doPrintlnV2(c.String(), a)
}
// Sprint returns rendered message
func (c Color256) Sprint(a ...any) string {
return RenderCode(c.String(), a...)
}
// Sprintf returns format and rendered message
func (c Color256) Sprintf(format string, a ...any) string {
return RenderString(c.String(), fmt.Sprintf(format, a...))
}
// C16 convert color-256 to 16 color.
func (c Color256) C16() Color {
return c.Basic()
}
// Basic convert color-256 to basic 16 color.
func (c Color256) Basic() Color {
return Color(c[0]) // TODO
}
// RGB convert color-256 to RGB color.
func (c Color256) RGB() RGBColor {
return RGBFromSlice(C256ToRgb(c[0]), c[1] == AsBg)
}
// RGBColor convert color-256 to RGB color.
func (c Color256) RGBColor() RGBColor {
return c.RGB()
}
// Value return color value
func (c Color256) Value() uint8 {
return c[0]
}
// Code convert to color code string. eg: "12"
func (c Color256) Code() string {
return strconv.Itoa(int(c[0]))
}
// FullCode convert to color code string with prefix. eg: "38;5;12"
func (c Color256) FullCode() string {
return c.String()
}
// String convert to color code string with prefix. eg: "38;5;12"
func (c Color256) String() string {
if c[1] == AsFg { // 0 is Fg
return Fg256Pfx + strconv.Itoa(int(c[0]))
}
if c[1] == AsBg { // 1 is Bg
return Bg256Pfx + strconv.Itoa(int(c[0]))
}
return "" // empty
}
// IsFg color
func (c Color256) IsFg() bool { return c[1] == AsFg }
// ToFg 256 color
func (c Color256) ToFg() Color256 {
c[1] = AsFg
return c
}
// IsBg color
func (c Color256) IsBg() bool { return c[1] == AsBg }
// ToBg 256 color
func (c Color256) ToBg() Color256 {
c[1] = AsBg
return c
}
// IsEmpty value
func (c Color256) IsEmpty() bool { return c[1] > 1 }
/*************************************************************
* 8bit(256) Style
*************************************************************/
// Style256 definition
//
// 前/背景色
// 都是由两位uint8组成, 第一位是色彩值;
// 第二位与 Bit8Color 不一样的是,在这里表示是否设置了值 0 未设置 !=0 已设置
type Style256 struct {
// Name of the style
Name string
// color options of the style
opts Opts
// fg and bg color
fg, bg Color256
}
// S256 create a color256 style
//
// Usage:
//
// s := color.S256()
// s := color.S256(132) // fg
// s := color.S256(132, 203) // fg and bg
func S256(fgAndBg ...uint8) *Style256 {
s := &Style256{}
vl := len(fgAndBg)
if vl > 0 { // with fg
s.fg = Color256{fgAndBg[0], 1}
if vl > 1 { // and with bg
s.bg = Color256{fgAndBg[1], 1}
}
}
return s
}
// Set fg and bg color value, can also with color options
func (s *Style256) Set(fgVal, bgVal uint8, opts ...Color) *Style256 {
s.fg = Color256{fgVal, 1}
s.bg = Color256{bgVal, 1}
s.opts.Add(opts...)
return s
}
// SetBg set bg color value
func (s *Style256) SetBg(bgVal uint8) *Style256 {
s.bg = Color256{bgVal, 1}
return s
}
// SetFg set fg color value
func (s *Style256) SetFg(fgVal uint8) *Style256 {
s.fg = Color256{fgVal, 1}
return s
}
// SetOpts set options
func (s *Style256) SetOpts(opts Opts) *Style256 {
s.opts = opts
return s
}
// AddOpts add options
func (s *Style256) AddOpts(opts ...Color) *Style256 {
s.opts.Add(opts...)
return s
}
// Print message
func (s *Style256) Print(a ...any) {
doPrintV2(s.String(), fmt.Sprint(a...))
}
// Printf format and print message
func (s *Style256) Printf(format string, a ...any) {
doPrintV2(s.String(), fmt.Sprintf(format, a...))
}
// Println print message with newline
func (s *Style256) Println(a ...any) {
doPrintlnV2(s.String(), a)
}
// Sprint returns rendered message
func (s *Style256) Sprint(a ...any) string {
return RenderCode(s.Code(), a...)
}
// Sprintf returns format and rendered message
func (s *Style256) Sprintf(format string, a ...any) string {
return RenderString(s.Code(), fmt.Sprintf(format, a...))
}
// Code convert to color code string
func (s *Style256) Code() string {
return s.String()
}
// String convert to color code string
func (s *Style256) String() string {
var ss []string
if s.fg[1] > 0 {
ss = append(ss, Fg256Pfx+strconv.FormatInt(int64(s.fg[0]), 10))
}
if s.bg[1] > 0 {
ss = append(ss, Bg256Pfx+strconv.FormatInt(int64(s.bg[0]), 10))
}
if s.opts.IsValid() {
ss = append(ss, s.opts.String())
}
return strings.Join(ss, ";")
}

View File

@@ -1,443 +0,0 @@
package color
import (
"fmt"
"strconv"
"strings"
)
// 24 bit RGB color
// RGB:
//
// R 0-255 G 0-255 B 0-255
// R 00-FF G 00-FF B 00-FF (16进制)
//
// Format:
//
// ESC[ … 38;2;<r>;<g>;<b> … m // Select RGB foreground color
// ESC[ … 48;2;<r>;<g>;<b> … m // Choose RGB background color
//
// links:
//
// https://zh.wikipedia.org/wiki/ANSI%E8%BD%AC%E4%B9%89%E5%BA%8F%E5%88%97#24位
//
// example:
//
// fg: \x1b[38;2;30;144;255mMESSAGE\x1b[0m
// bg: \x1b[48;2;30;144;255mMESSAGE\x1b[0m
// both: \x1b[38;2;233;90;203;48;2;30;144;255mMESSAGE\x1b[0m
const (
TplFgRGB = "38;2;%d;%d;%d"
TplBgRGB = "48;2;%d;%d;%d"
FgRGBPfx = "38;2;"
BgRGBPfx = "48;2;"
)
// mark color is fg or bg.
const (
AsFg uint8 = iota
AsBg
)
/*************************************************************
* RGB Color(Bit24Color, TrueColor)
*************************************************************/
// RGBColor definition.
// Support RGB color on Windows CMD, PowerShell
//
// The first to third digits represent the color value.
// The last digit represents the foreground(0), background(1), >1 is unset value
//
// Usage:
//
// // 0, 1, 2 is R,G,B.
// // 3rd: Fg=0, Bg=1, >1: unset value
// RGBColor{30,144,255, 0}
// RGBColor{30,144,255, 1}
type RGBColor [4]uint8
// create an empty RGBColor
var emptyRGBColor = RGBColor{3: 99}
// RGB color create.
//
// Usage:
//
// c := RGB(30,144,255)
// c := RGB(30,144,255, true)
// c.Print("message")
func RGB(r, g, b uint8, isBg ...bool) RGBColor {
rgb := RGBColor{r, g, b}
if len(isBg) > 0 && isBg[0] {
rgb[3] = AsBg
}
return rgb
}
// Rgb alias of the RGB()
func Rgb(r, g, b uint8, isBg ...bool) RGBColor { return RGB(r, g, b, isBg...) }
// Bit24 alias of the RGB()
func Bit24(r, g, b uint8, isBg ...bool) RGBColor { return RGB(r, g, b, isBg...) }
// RgbFromInt create instance from int r,g,b value
func RgbFromInt(r, g, b int, isBg ...bool) RGBColor {
return RGB(uint8(r), uint8(g), uint8(b), isBg...)
}
// RgbFromInts create instance from []int r,g,b value
func RgbFromInts(rgb []int, isBg ...bool) RGBColor {
return RGB(uint8(rgb[0]), uint8(rgb[1]), uint8(rgb[2]), isBg...)
}
// HEX create RGB color from a HEX color string.
//
// Usage:
//
// c := HEX("ccc") // rgb: [204 204 204]
// c := HEX("aabbcc") // rgb: [170 187 204]
// c := HEX("#aabbcc")
// c := HEX("0xaabbcc")
// c.Print("message")
func HEX(hex string, isBg ...bool) RGBColor {
if rgb := HexToRgb(hex); len(rgb) > 0 {
return RGB(uint8(rgb[0]), uint8(rgb[1]), uint8(rgb[2]), isBg...)
}
// mark is empty
return emptyRGBColor
}
// Hex alias of the HEX()
func Hex(hex string, isBg ...bool) RGBColor { return HEX(hex, isBg...) }
// RGBFromHEX quick RGBColor from hex string, alias of HEX()
func RGBFromHEX(hex string, isBg ...bool) RGBColor { return HEX(hex, isBg...) }
// HSL create RGB color from a hsl value.
// more see HslToRgb()
func HSL(h, s, l float64, isBg ...bool) RGBColor {
rgb := HslToRgb(h, s, l)
return RGB(rgb[0], rgb[1], rgb[2], isBg...)
}
// Hsl alias of the HSL()
func Hsl(h, s, l float64, isBg ...bool) RGBColor { return HSL(h, s, l, isBg...) }
// HSLInt create RGB color from a hsl int value.
// more see HslIntToRgb()
func HSLInt(h, s, l int, isBg ...bool) RGBColor {
rgb := HslIntToRgb(h, s, l)
return RGB(rgb[0], rgb[1], rgb[2], isBg...)
}
// HslInt alias of the HSLInt()
func HslInt(h, s, l int, isBg ...bool) RGBColor { return HSLInt(h, s, l, isBg...) }
// RGBFromSlice quick RGBColor from slice
func RGBFromSlice(rgb []uint8, isBg ...bool) RGBColor {
return RGB(rgb[0], rgb[1], rgb[2], isBg...)
}
// RGBFromString create RGB color from a string.
// Support use color name in the {namedRgbMap}
//
// Usage:
//
// c := RGBFromString("170,187,204")
// c.Print("message")
//
// c := RGBFromString("brown")
// c.Print("message with color brown")
func RGBFromString(rgb string, isBg ...bool) RGBColor {
// use color name in the {namedRgbMap}
if rgbVal, ok := namedRgbMap[rgb]; ok {
rgb = rgbVal
}
// use rgb string.
ss := stringToArr(rgb, ",")
if len(ss) != 3 {
return emptyRGBColor
}
var ar [3]uint8
for i, val := range ss {
iv, err := strconv.Atoi(val)
if err != nil || !isValidUint8(iv) {
return emptyRGBColor
}
ar[i] = uint8(iv)
}
return RGB(ar[0], ar[1], ar[2], isBg...)
}
// Set terminal by rgb/true color code
func (c RGBColor) Set() error {
return SetTerminal(c.String())
}
// Reset terminal. alias of the ResetTerminal()
func (c RGBColor) Reset() error {
return ResetTerminal()
}
// Print print message
func (c RGBColor) Print(a ...any) {
doPrintV2(c.String(), fmt.Sprint(a...))
}
// Printf format and print message
func (c RGBColor) Printf(format string, a ...any) {
doPrintV2(c.String(), fmt.Sprintf(format, a...))
}
// Println print message with newline
func (c RGBColor) Println(a ...any) {
doPrintlnV2(c.String(), a)
}
// Sprint returns rendered message
func (c RGBColor) Sprint(a ...any) string {
return RenderCode(c.String(), a...)
}
// Sprintf returns format and rendered message
func (c RGBColor) Sprintf(format string, a ...any) string {
return RenderString(c.String(), fmt.Sprintf(format, a...))
}
// Values to RGB values
func (c RGBColor) Values() []int {
return []int{int(c[0]), int(c[1]), int(c[2])}
}
// Code to color code string without prefix. eg: "204;123;56"
func (c RGBColor) Code() string {
return fmt.Sprintf("%d;%d;%d", c[0], c[1], c[2])
}
// Hex color rgb to hex string. as in "ff0080".
func (c RGBColor) Hex() string {
return fmt.Sprintf("%02x%02x%02x", c[0], c[1], c[2])
}
// RgbString to color code string without prefix. eg: "204,123,56"
func (c RGBColor) RgbString() string {
return fmt.Sprintf("%d,%d,%d", c[0], c[1], c[2])
}
// FullCode to color code string with prefix
func (c RGBColor) FullCode() string {
return c.String()
}
// String to color code string with prefix. eg: "38;2;204;123;56"
func (c RGBColor) String() string {
if c[3] == AsFg {
return fmt.Sprintf(TplFgRGB, c[0], c[1], c[2])
}
if c[3] == AsBg {
return fmt.Sprintf(TplBgRGB, c[0], c[1], c[2])
}
// c[3] > 1 is empty
return ""
}
// ToBg convert to background color
func (c RGBColor) ToBg() RGBColor {
c[3] = AsBg
return c
}
// ToFg convert to foreground color
func (c RGBColor) ToFg() RGBColor {
c[3] = AsFg
return c
}
// IsEmpty value
func (c RGBColor) IsEmpty() bool {
return c[3] > AsBg
}
// IsValid value
// func (c RGBColor) IsValid() bool {
// return c[3] <= AsBg
// }
// C256 returns the closest approximate 256 (8 bit) color
func (c RGBColor) C256() Color256 {
return C256(RgbTo256(c[0], c[1], c[2]), c[3] == AsBg)
}
// Basic returns the closest approximate 16 (4 bit) color
func (c RGBColor) Basic() Color {
// return Color(RgbToAnsi(c[0], c[1], c[2], c[3] == AsBg))
return Color(Rgb2basic(c[0], c[1], c[2], c[3] == AsBg))
}
// Color returns the closest approximate 16 (4 bit) color
func (c RGBColor) Color() Color { return c.Basic() }
// C16 returns the closest approximate 16 (4 bit) color
func (c RGBColor) C16() Color { return c.Basic() }
/*************************************************************
* RGB Style
*************************************************************/
// RGBStyle supports set foreground and background color
//
// All are composed of 4 digits uint8, the first three digits are the color value;
// The last bit is different from RGBColor, here it indicates whether the value is set.
//
// 1 Has been set
// ^1 Not set
type RGBStyle struct {
// Name of the style
Name string
// color options of the style
opts Opts
// fg and bg color
fg, bg RGBColor
}
// NewRGBStyle create a RGBStyle.
func NewRGBStyle(fg RGBColor, bg ...RGBColor) *RGBStyle {
s := &RGBStyle{}
if len(bg) > 0 {
s.SetBg(bg[0])
}
return s.SetFg(fg)
}
// HEXStyle create a RGBStyle from HEX color string.
//
// Usage:
//
// s := HEXStyle("aabbcc", "eee")
// s.Print("message")
func HEXStyle(fg string, bg ...string) *RGBStyle {
s := &RGBStyle{}
if len(bg) > 0 {
s.SetBg(HEX(bg[0]))
}
if len(fg) > 0 {
s.SetFg(HEX(fg))
}
return s
}
// RGBStyleFromString create a RGBStyle from color value string.
//
// Usage:
//
// s := RGBStyleFromString("170,187,204", "70,87,4")
// s.Print("message")
func RGBStyleFromString(fg string, bg ...string) *RGBStyle {
s := &RGBStyle{}
if len(bg) > 0 {
s.SetBg(RGBFromString(bg[0]))
}
return s.SetFg(RGBFromString(fg))
}
// Set fg and bg color, can also with color options
func (s *RGBStyle) Set(fg, bg RGBColor, opts ...Color) *RGBStyle {
return s.SetFg(fg).SetBg(bg).SetOpts(opts)
}
// SetFg set fg color
func (s *RGBStyle) SetFg(fg RGBColor) *RGBStyle {
fg[3] = 1 // add fixed value, mark is valid
s.fg = fg
return s
}
// SetBg set bg color
func (s *RGBStyle) SetBg(bg RGBColor) *RGBStyle {
bg[3] = 1 // add fixed value, mark is valid
s.bg = bg
return s
}
// SetOpts set color options
func (s *RGBStyle) SetOpts(opts Opts) *RGBStyle {
s.opts = opts
return s
}
// AddOpts add options
func (s *RGBStyle) AddOpts(opts ...Color) *RGBStyle {
s.opts.Add(opts...)
return s
}
// Print print message
func (s *RGBStyle) Print(a ...any) {
doPrintV2(s.String(), fmt.Sprint(a...))
}
// Printf format and print message
func (s *RGBStyle) Printf(format string, a ...any) {
doPrintV2(s.String(), fmt.Sprintf(format, a...))
}
// Println print message with newline
func (s *RGBStyle) Println(a ...any) {
doPrintlnV2(s.String(), a)
}
// Sprint returns rendered message
func (s *RGBStyle) Sprint(a ...any) string {
return RenderCode(s.String(), a...)
}
// Sprintf returns format and rendered message
func (s *RGBStyle) Sprintf(format string, a ...any) string {
return RenderString(s.String(), fmt.Sprintf(format, a...))
}
// Code convert to color code string
func (s *RGBStyle) Code() string {
return s.String()
}
// FullCode convert to color code string
func (s *RGBStyle) FullCode() string {
return s.String()
}
// String convert to color code string
func (s *RGBStyle) String() string {
var ss []string
// last value ensure is enable.
if s.fg[3] == 1 {
ss = append(ss, fmt.Sprintf(TplFgRGB, s.fg[0], s.fg[1], s.fg[2]))
}
if s.bg[3] == 1 {
ss = append(ss, fmt.Sprintf(TplBgRGB, s.bg[0], s.bg[1], s.bg[2]))
}
if s.opts.IsValid() {
ss = append(ss, s.opts.String())
}
return strings.Join(ss, ";")
}
// IsEmpty style
func (s *RGBStyle) IsEmpty() bool {
return s.fg[3] != 1 && s.bg[3] != 1
}

View File

@@ -1,567 +0,0 @@
package color
import (
"fmt"
"regexp"
"strings"
)
// output colored text like use html tag. (not support windows cmd)
const (
// MatchExpr regex to match color tags
//
// Notice: golang 不支持反向引用. 即不支持使用 \1 引用第一个匹配 ([a-z=;]+)
// MatchExpr = `<([a-z=;]+)>(.*?)<\/\1>`
// 所以调整一下 统一使用 `</>` 来结束标签,例如 "<info>some text</>"
//
// allow custom attrs, eg: "<fg=white;bg=blue;op=bold>content</>"
// (?s:...) s - 让 "." 匹配换行
MatchExpr = `<([0-9a-zA-Z_=,;]+)>(?s:(.*?))<\/>`
// AttrExpr regex to match custom color attributes
// eg: "<fg=white;bg=blue;op=bold>content</>"
AttrExpr = `(fg|bg|op)[\s]*=[\s]*([0-9a-zA-Z,]+);?`
// StripExpr regex used for removing color tags
// StripExpr = `<[\/]?[a-zA-Z=;]+>`
// 随着上面的做一些调整
StripExpr = `<[\/]?[0-9a-zA-Z_=,;]*>`
)
var (
attrRegex = regexp.MustCompile(AttrExpr)
matchRegex = regexp.MustCompile(MatchExpr)
stripRegex = regexp.MustCompile(StripExpr)
)
/*************************************************************
* internal defined color tags
*************************************************************/
// There are internal defined fg color tags
//
// Usage:
//
// <tag>content text</>
//
// @notice 加 0 在前面是为了防止之前的影响到现在的设置
var colorTags = map[string]string{
// basic tags
"red": "0;31",
"red1": "1;31", // with bold
"redB": "1;31",
"red_b": "1;31",
"blue": "0;34",
"blue1": "1;34", // with bold
"blueB": "1;34",
"blue_b": "1;34",
"cyan": "0;36",
"cyan1": "1;36", // with bold
"cyanB": "1;36",
"cyan_b": "1;36",
"green": "0;32",
"green1": "1;32", // with bold
"greenB": "1;32",
"green_b": "1;32",
"black": "0;30",
"white": "1;37",
"default": "0;39", // no color
"normal": "0;39", // no color
"brown": "0;33", // #A52A2A
"yellow": "0;33",
"ylw0": "0;33",
"yellowB": "1;33", // with bold
"ylw1": "1;33",
"ylwB": "1;33",
"magenta": "0;35",
"mga": "0;35", // short name
"magentaB": "1;35", // with bold
"magenta1": "1;35",
"mgb": "1;35",
"mga1": "1;35",
"mgaB": "1;35",
// light/hi tags
"gray": "0;90",
"darkGray": "0;90",
"dark_gray": "0;90",
"lightYellow": "0;93",
"light_yellow": "0;93",
"hiYellow": "0;93",
"hi_yellow": "0;93",
"hiYellowB": "1;93", // with bold
"hi_yellow_b": "1;93",
"lightMagenta": "0;95",
"light_magenta": "0;95",
"hiMagenta": "0;95",
"hi_magenta": "0;95",
"lightMagenta1": "1;95", // with bold
"hiMagentaB": "1;95", // with bold
"hi_magenta_b": "1;95",
"lightRed": "0;91",
"light_red": "0;91",
"hiRed": "0;91",
"hi_red": "0;91",
"lightRedB": "1;91", // with bold
"light_red_b": "1;91",
"hi_red_b": "1;91",
"lightGreen": "0;92",
"light_green": "0;92",
"hiGreen": "0;92",
"hi_green": "0;92",
"lightGreenB": "1;92",
"light_green_b": "1;92",
"hi_green_b": "1;92",
"lightBlue": "0;94",
"light_blue": "0;94",
"hiBlue": "0;94",
"hi_blue": "0;94",
"lightBlueB": "1;94",
"light_blue_b": "1;94",
"hi_blue_b": "1;94",
"lightCyan": "0;96",
"light_cyan": "0;96",
"hiCyan": "0;96",
"hi_cyan": "0;96",
"lightCyanB": "1;96",
"light_cyan_b": "1;96",
"hi_cyan_b": "1;96",
"lightWhite": "0;97;40",
"light_white": "0;97;40",
// option
"bold": "1",
"b": "1",
"italic": "3",
"i": "3", // italic
"underscore": "4",
"us": "4", // short name for 'underscore'
"blink": "5",
"fb": "6", // fast blink
"reverse": "7",
"st": "9", // strikethrough
// alert tags, like bootstrap's alert
"suc": "1;32", // same "green" and "bold"
"success": "1;32",
"info": "0;32", // same "green",
"comment": "0;33", // same "brown"
"note": "36;1",
"notice": "36;4",
"warn": "0;1;33",
"warning": "0;30;43",
"primary": "0;34",
"danger": "1;31", // same "red" but add bold
"err": "97;41",
"error": "97;41", // fg light white; bg red
}
/*************************************************************
* internal defined tag attributes
*************************************************************/
// built-in attributes for fg,bg 16-colors and op codes.
var (
attrFgs = map[string]string{
// basic colors
"black": FgBlack.Code(),
"red": "31",
"green": "32",
"brown": "33", // #A52A2A
"yellow": "33",
"ylw": "33",
"blue": "34",
"cyan": "36",
"magenta": "35",
"mga": "35",
"white": FgWhite.Code(),
"default": "39", // no color
"normal": "39", // no color
// light/hi colors
"darkGray": FgDarkGray.Code(),
"dark_gray": "90",
"gray": "90",
"lightYellow": "93",
"light_yellow": "93",
"hiYellow": "93",
"hi_yellow": "93",
"lightMagenta": "95",
"light_magenta": "95",
"hiMagenta": "95",
"hi_magenta": "95",
"hi_mga": "95",
"lightRed": "91",
"light_red": "91",
"hiRed": "91",
"hi_red": "91",
"lightGreen": "92",
"light_green": "92",
"hiGreen": "92",
"hi_green": "92",
"lightBlue": "94",
"light_blue": "94",
"hiBlue": "94",
"hi_blue": "94",
"lightCyan": "96",
"light_cyan": "96",
"hiCyan": "96",
"hi_cyan": "96",
"lightWhite": "97",
"light_white": "97",
}
attrBgs = map[string]string{
// basic colors
"black": BgBlack.Code(),
"red": "41",
"green": "42",
"brown": "43", // #A52A2A
"yellow": "43",
"ylw": "43",
"blue": "44",
"cyan": "46",
"magenta": "45",
"mga": "45",
"white": FgWhite.Code(),
"default": "49", // no color
"normal": "49", // no color
// light/hi colors
"darkGray": BgDarkGray.Code(),
"dark_gray": "100",
"gray": "100",
"lightYellow": "103",
"light_yellow": "103",
"hiYellow": "103",
"hi_yellow": "103",
"lightMagenta": "105",
"light_magenta": "105",
"hiMagenta": "105",
"hi_magenta": "105",
"hi_mga": "105",
"lightRed": "101",
"light_red": "101",
"hiRed": "101",
"hi_red": "101",
"lightGreen": "102",
"light_green": "102",
"hiGreen": "102",
"hi_green": "102",
"lightBlue": "104",
"light_blue": "104",
"hiBlue": "104",
"hi_blue": "104",
"lightCyan": "106",
"light_cyan": "106",
"hiCyan": "106",
"hi_cyan": "106",
"lightWhite": BgLightWhite.Code(),
"light_white": "107",
}
attrOpts = map[string]string{
"reset": OpReset.Code(),
"bold": OpBold.Code(),
"b": OpBold.Code(),
"fuzzy": OpFuzzy.Code(),
"italic": OpItalic.Code(),
"i": OpItalic.Code(),
"underscore": OpUnderscore.Code(),
"us": OpUnderscore.Code(),
"u": OpUnderscore.Code(),
"blink": OpBlink.Code(),
"fastblink": OpFastBlink.Code(),
"fb": OpFastBlink.Code(),
"reverse": OpReverse.Code(),
"concealed": OpConcealed.Code(),
"strikethrough": OpStrikethrough.Code(),
"st": OpStrikethrough.Code(),
}
)
/*************************************************************
* parse color tags
*************************************************************/
var (
tagParser = TagParser{}
// regex for match color 256 code
rxNumStr = regexp.MustCompile("^[0-9]{1,3}$")
rxHexCode = regexp.MustCompile("^#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$")
)
// TagParser struct
type TagParser struct {
disable bool
}
// NewTagParser create
func NewTagParser() *TagParser {
return &TagParser{}
}
// func (tp *TagParser) Disable() *TagParser {
// tp.disable = true
// return tp
// }
// ParseByEnv parse given string. will check package setting.
func (tp *TagParser) ParseByEnv(str string) string {
// disable handler TAG
if !RenderTag {
return str
}
// disable OR not support color
if !Enable || !SupportColor() {
return ClearTag(str)
}
return tp.Parse(str)
}
// Parse given string, replace color tag and return rendered string
//
// Use built in tags:
//
// <TAG_NAME>CONTENT</>
// // e.g: `<info>message</>`
//
// Custom tag attributes:
//
// `<fg=VALUE;bg=VALUE;op=VALUES>CONTENT</>`
// // e.g: `<fg=167;bg=232>wel</>`
func (tp *TagParser) Parse(str string) string {
// not contains color tag
if !strings.Contains(str, "</>") {
return str
}
// find color tags by regex. str eg: "<fg=white;bg=blue;op=bold>content</>"
matched := matchRegex.FindAllStringSubmatch(str, -1)
// item: 0 full text 1 tag name 2 tag content
for _, item := range matched {
full, tag, body := item[0], item[1], item[2]
// use defined color tag name: "<info>content</>" -> tag: "info"
if !strings.ContainsRune(tag, '=') {
if code := colorTags[tag]; len(code) > 0 {
str = strings.Replace(str, full, RenderString(code, body), 1)
} else if code, ok := namedRgbMap[tag]; ok {
code = strings.Replace(code, ",", ";", -1)
now := RenderString(FgRGBPfx+code, body)
str = strings.Replace(str, full, now, 1)
}
continue
}
// custom color in tag
// - basic: "fg=white;bg=blue;op=bold"
if code := ParseCodeFromAttr(tag); len(code) > 0 {
str = strings.Replace(str, full, RenderString(code, body), 1)
}
}
return str
}
// ReplaceTag parse string, replace color tag and return rendered string
func ReplaceTag(str string) string {
return tagParser.ParseByEnv(str)
}
// ParseCodeFromAttr parse color attributes.
//
// attr format:
//
// // VALUE please see var: FgColors, BgColors, AllOptions
// "fg=VALUE;bg=VALUE;op=VALUE"
//
// 16 color:
//
// "fg=yellow"
// "bg=red"
// "op=bold,underscore" // option is allow multi value
// "fg=white;bg=blue;op=bold"
// "fg=white;op=bold,underscore"
//
// 256 color:
//
// "fg=167"
// "fg=167;bg=23"
// "fg=167;bg=23;op=bold"
//
// True color:
//
// // hex
// "fg=fc1cac"
// "fg=fc1cac;bg=c2c3c4"
// // r,g,b
// "fg=23,45,214"
// "fg=23,45,214;bg=109,99,88"
func ParseCodeFromAttr(attr string) (code string) {
if !strings.ContainsRune(attr, '=') {
return
}
attr = strings.Trim(attr, ";=,")
if len(attr) == 0 {
return
}
var codes []string
matched := attrRegex.FindAllStringSubmatch(attr, -1)
for _, item := range matched {
pos, val := item[1], item[2]
switch pos {
case "fg":
if code, ok := attrFgs[val]; ok { // attr fg
codes = append(codes, code)
} else if code := rgbHex256toCode(val, false); code != "" {
codes = append(codes, code)
}
case "bg":
if code, ok := attrBgs[val]; ok { // attr bg
codes = append(codes, code)
} else if code := rgbHex256toCode(val, true); code != "" {
codes = append(codes, code)
}
case "op": // options allow multi value
if strings.Contains(val, ",") {
ns := strings.Split(val, ",")
for _, n := range ns {
if code, ok := attrOpts[n]; ok { // attr ops
codes = append(codes, code)
}
}
} else if code, ok := attrOpts[val]; ok {
codes = append(codes, code)
}
}
}
return strings.Join(codes, ";")
}
func rgbHex256toCode(val string, isBg bool) (code string) {
if len(val) == 6 && rxHexCode.MatchString(val) { // hex: "fc1cac"
code = HEX(val, isBg).String()
} else if strings.ContainsRune(val, ',') { // rgb: "231,178,161"
code = strings.Replace(val, ",", ";", -1)
if isBg {
code = BgRGBPfx + code
} else {
code = FgRGBPfx + code
}
} else if len(val) < 4 && rxNumStr.MatchString(val) { // 256 code
if isBg {
code = Bg256Pfx + val
} else {
code = Fg256Pfx + val
}
}
return
}
// ClearTag clear all tag for a string
func ClearTag(s string) string {
if !strings.Contains(s, "</>") {
return s
}
return stripRegex.ReplaceAllString(s, "")
}
/*************************************************************
* helper methods
*************************************************************/
// GetTagCode get color code by tag name
func GetTagCode(name string) string { return colorTags[name] }
// ApplyTag for messages
func ApplyTag(tag string, a ...any) string {
return RenderCode(GetTagCode(tag), a...)
}
// WrapTag wrap a tag for a string "<tag>content</>"
func WrapTag(s string, tag string) string {
if s == "" || tag == "" {
return s
}
return fmt.Sprintf("<%s>%s</>", tag, s)
}
// GetColorTags get all internal color tags
func GetColorTags() map[string]string {
return colorTags
}
// IsDefinedTag is defined tag name
func IsDefinedTag(name string) bool {
_, ok := colorTags[name]
return ok
}
/*************************************************************
* Tag extra
*************************************************************/
// Tag value is a defined style name
// Usage:
//
// Tag("info").Println("message")
type Tag string
// Print messages
func (tg Tag) Print(a ...any) {
name := string(tg)
str := fmt.Sprint(a...)
if stl := GetStyle(name); !stl.IsEmpty() {
stl.Print(str)
} else {
doPrintV2(GetTagCode(name), str)
}
}
// Printf format and print messages
func (tg Tag) Printf(format string, a ...any) {
name := string(tg)
str := fmt.Sprintf(format, a...)
if stl := GetStyle(name); !stl.IsEmpty() {
stl.Print(str)
} else {
doPrintV2(GetTagCode(name), str)
}
}
// Println messages line
func (tg Tag) Println(a ...any) {
name := string(tg)
if stl := GetStyle(name); !stl.IsEmpty() {
stl.Println(a...)
} else {
doPrintlnV2(GetTagCode(name), a)
}
}
// Sprint render messages
func (tg Tag) Sprint(a ...any) string {
return RenderCode(GetTagCode(string(tg)), a...)
}
// Sprintf format and render messages
func (tg Tag) Sprintf(format string, a ...any) string {
tag := string(tg)
str := fmt.Sprintf(format, a...)
return RenderString(GetTagCode(tag), str)
}

View File

@@ -1,966 +0,0 @@
package color
import (
"fmt"
"math"
"sort"
"strconv"
"strings"
)
// values from https://github.com/go-terminfo/terminfo
// var (
// RgbaBlack = image_color.RGBA{0, 0, 0, 255}
// Red = color.RGBA{205, 0, 0, 255}
// Green = color.RGBA{0, 205, 0, 255}
// Orange = color.RGBA{205, 205, 0, 255}
// Blue = color.RGBA{0, 0, 238, 255}
// Magenta = color.RGBA{205, 0, 205, 255}
// Cyan = color.RGBA{0, 205, 205, 255}
// LightGrey = color.RGBA{229, 229, 229, 255}
//
// DarkGrey = color.RGBA{127, 127, 127, 255}
// LightRed = color.RGBA{255, 0, 0, 255}
// LightGreen = color.RGBA{0, 255, 0, 255}
// Yellow = color.RGBA{255, 255, 0, 255}
// LightBlue = color.RGBA{92, 92, 255, 255}
// LightMagenta = color.RGBA{255, 0, 255, 255}
// LightCyan = color.RGBA{0, 255, 255, 255}
// White = color.RGBA{255, 255, 255, 255}
// )
var (
// ---------- basic(16) <=> 256 color convert ----------
basicTo256Map = map[uint8]uint8{
30: 0, // black 000000
31: 160, // red c51e14
32: 34, // green 1dc121
33: 184, // yellow c7c329
34: 20, // blue 0a2fc4
35: 170, // magenta c839c5
36: 44, // cyan 20c5c6
37: 188, // white c7c7c7
90: 59, // lightBlack 686868
91: 203, // lightRed fd6f6b
92: 83, // lightGreen 67f86f
93: 227, // lightYellow fffa72
94: 69, // lightBlue 6a76fb
95: 213, // lightMagenta fd7cfc
96: 87, // lightCyan 68fdfe
97: 15, // lightWhite ffffff
}
// ---------- basic(16) <=> RGB color convert ----------
// refer from Hyper app
// Tip: only keep foreground color, background color need convert to foreground color for convert to RGB
basic2hexMap = map[uint8]string{
30: "000000", // black
31: "c51e14", // red
32: "1dc121", // green
33: "c7c329", // yellow
34: "0a2fc4", // blue
35: "c839c5", // magenta
36: "20c5c6", // cyan
37: "c7c7c7", // white
// - don't add bg color, convert to fg color for convert to RGB
// 40: "000000", // black
// 41: "c51e14", // red
// 42: "1dc121", // green
// 43: "c7c329", // yellow
// 44: "0a2fc4", // blue
// 45: "c839c5", // magenta
// 46: "20c5c6", // cyan
// 47: "c7c7c7", // white
90: "686868", // lightBlack/darkGray
91: "fd6f6b", // lightRed
92: "67f86f", // lightGreen
93: "fffa72", // lightYellow
94: "6a76fb", // lightBlue
95: "fd7cfc", // lightMagenta
96: "68fdfe", // lightCyan
97: "ffffff", // lightWhite
// - don't add bg color
// 100: "686868", // lightBlack/darkGray
// 101: "fd6f6b", // lightRed
// 102: "67f86f", // lightGreen
// 103: "fffa72", // lightYellow
// 104: "6a76fb", // lightBlue
// 105: "fd7cfc", // lightMagenta
// 106: "68fdfe", // lightCyan
// 107: "ffffff", // lightWhite
}
// will convert data from basic2hexMap
hex2basicMap = initHex2basicMap()
// ---------- 256 <=> RGB color convert ----------
// adapted from https://gist.github.com/MicahElliott/719710
c256ToHexMap = init256ToHexMap()
// rgb to 256 color look-up table
// RGB hex => 256 code
hexTo256Table = map[string]uint8{
// Primary 3-bit (8 colors). Unique representation!
"000000": 0,
"800000": 1,
"008000": 2,
"808000": 3,
"000080": 4,
"800080": 5,
"008080": 6,
"c0c0c0": 7,
// Equivalent "bright" versions of original 8 colors.
"808080": 8,
"ff0000": 9,
"00ff00": 10,
"ffff00": 11,
"0000ff": 12,
"ff00ff": 13,
"00ffff": 14,
"ffffff": 15,
// values commented out below are duplicates from the prior sections
// Strictly ascending.
// "000000": 16,
"000001": 16, // up: avoid key conflicts, value + 1
"00005f": 17,
"000087": 18,
"0000af": 19,
"0000d7": 20,
// "0000ff": 21,
"0000fe": 21, // up: avoid key conflicts, value - 1
"005f00": 22,
"005f5f": 23,
"005f87": 24,
"005faf": 25,
"005fd7": 26,
"005fff": 27,
"008700": 28,
"00875f": 29,
"008787": 30,
"0087af": 31,
"0087d7": 32,
"0087ff": 33,
"00af00": 34,
"00af5f": 35,
"00af87": 36,
"00afaf": 37,
"00afd7": 38,
"00afff": 39,
"00d700": 40,
"00d75f": 41,
"00d787": 42,
"00d7af": 43,
"00d7d7": 44,
"00d7ff": 45,
// "00ff00": 46,
"00ff01": 46, // up: avoid key conflicts, value + 1
"00ff5f": 47,
"00ff87": 48,
"00ffaf": 49,
"00ffd7": 50,
// "00ffff": 51,
"00fffe": 51, // up: avoid key conflicts, value - 1
"5f0000": 52,
"5f005f": 53,
"5f0087": 54,
"5f00af": 55,
"5f00d7": 56,
"5f00ff": 57,
"5f5f00": 58,
"5f5f5f": 59,
"5f5f87": 60,
"5f5faf": 61,
"5f5fd7": 62,
"5f5fff": 63,
"5f8700": 64,
"5f875f": 65,
"5f8787": 66,
"5f87af": 67,
"5f87d7": 68,
"5f87ff": 69,
"5faf00": 70,
"5faf5f": 71,
"5faf87": 72,
"5fafaf": 73,
"5fafd7": 74,
"5fafff": 75,
"5fd700": 76,
"5fd75f": 77,
"5fd787": 78,
"5fd7af": 79,
"5fd7d7": 80,
"5fd7ff": 81,
"5fff00": 82,
"5fff5f": 83,
"5fff87": 84,
"5fffaf": 85,
"5fffd7": 86,
"5fffff": 87,
"870000": 88,
"87005f": 89,
"870087": 90,
"8700af": 91,
"8700d7": 92,
"8700ff": 93,
"875f00": 94,
"875f5f": 95,
"875f87": 96,
"875faf": 97,
"875fd7": 98,
"875fff": 99,
"878700": 100,
"87875f": 101,
"878787": 102,
"8787af": 103,
"8787d7": 104,
"8787ff": 105,
"87af00": 106,
"87af5f": 107,
"87af87": 108,
"87afaf": 109,
"87afd7": 110,
"87afff": 111,
"87d700": 112,
"87d75f": 113,
"87d787": 114,
"87d7af": 115,
"87d7d7": 116,
"87d7ff": 117,
"87ff00": 118,
"87ff5f": 119,
"87ff87": 120,
"87ffaf": 121,
"87ffd7": 122,
"87ffff": 123,
"af0000": 124,
"af005f": 125,
"af0087": 126,
"af00af": 127,
"af00d7": 128,
"af00ff": 129,
"af5f00": 130,
"af5f5f": 131,
"af5f87": 132,
"af5faf": 133,
"af5fd7": 134,
"af5fff": 135,
"af8700": 136,
"af875f": 137,
"af8787": 138,
"af87af": 139,
"af87d7": 140,
"af87ff": 141,
"afaf00": 142,
"afaf5f": 143,
"afaf87": 144,
"afafaf": 145,
"afafd7": 146,
"afafff": 147,
"afd700": 148,
"afd75f": 149,
"afd787": 150,
"afd7af": 151,
"afd7d7": 152,
"afd7ff": 153,
"afff00": 154,
"afff5f": 155,
"afff87": 156,
"afffaf": 157,
"afffd7": 158,
"afffff": 159,
"d70000": 160,
"d7005f": 161,
"d70087": 162,
"d700af": 163,
"d700d7": 164,
"d700ff": 165,
"d75f00": 166,
"d75f5f": 167,
"d75f87": 168,
"d75faf": 169,
"d75fd7": 170,
"d75fff": 171,
"d78700": 172,
"d7875f": 173,
"d78787": 174,
"d787af": 175,
"d787d7": 176,
"d787ff": 177,
"d7af00": 178,
"d7af5f": 179,
"d7af87": 180,
"d7afaf": 181,
"d7afd7": 182,
"d7afff": 183,
"d7d700": 184,
"d7d75f": 185,
"d7d787": 186,
"d7d7af": 187,
"d7d7d7": 188,
"d7d7ff": 189,
"d7ff00": 190,
"d7ff5f": 191,
"d7ff87": 192,
"d7ffaf": 193,
"d7ffd7": 194,
"d7ffff": 195,
// "ff0000": 196,
"ff0001": 196, // up: avoid key conflicts, value + 1
"ff005f": 197,
"ff0087": 198,
"ff00af": 199,
"ff00d7": 200,
// "ff00ff": 201,
"ff00fe": 201, // up: avoid key conflicts, value - 1
"ff5f00": 202,
"ff5f5f": 203,
"ff5f87": 204,
"ff5faf": 205,
"ff5fd7": 206,
"ff5fff": 207,
"ff8700": 208,
"ff875f": 209,
"ff8787": 210,
"ff87af": 211,
"ff87d7": 212,
"ff87ff": 213,
"ffaf00": 214,
"ffaf5f": 215,
"ffaf87": 216,
"ffafaf": 217,
"ffafd7": 218,
"ffafff": 219,
"ffd700": 220,
"ffd75f": 221,
"ffd787": 222,
"ffd7af": 223,
"ffd7d7": 224,
"ffd7ff": 225,
// "ffff00": 226,
"ffff01": 226, // up: avoid key conflicts, value + 1
"ffff5f": 227,
"ffff87": 228,
"ffffaf": 229,
"ffffd7": 230,
// "ffffff": 231,
"fffffe": 231, // up: avoid key conflicts, value - 1
// Gray-scale range.
"080808": 232,
"121212": 233,
"1c1c1c": 234,
"262626": 235,
"303030": 236,
"3a3a3a": 237,
"444444": 238,
"4e4e4e": 239,
"585858": 240,
"626262": 241,
"6c6c6c": 242,
"767676": 243,
// "808080": 244,
"808081": 244, // up: avoid key conflicts, value + 1
"8a8a8a": 245,
"949494": 246,
"9e9e9e": 247,
"a8a8a8": 248,
"b2b2b2": 249,
"bcbcbc": 250,
"c6c6c6": 251,
"d0d0d0": 252,
"dadada": 253,
"e4e4e4": 254,
"eeeeee": 255,
}
incs = []uint8{0x00, 0x5f, 0x87, 0xaf, 0xd7, 0xff}
)
func initHex2basicMap() map[string]uint8 {
h2b := make(map[string]uint8, len(basic2hexMap))
// ini data map
for u, s := range basic2hexMap {
h2b[s] = u
}
return h2b
}
func init256ToHexMap() map[uint8]string {
c256toh := make(map[uint8]string, len(hexTo256Table))
// ini data map
for hex, c256 := range hexTo256Table {
c256toh[c256] = hex
}
return c256toh
}
// RgbTo256Table mapping data
func RgbTo256Table() map[string]uint8 {
return hexTo256Table
}
// Colors2code convert colors to code. return like "32;45;3"
func Colors2code(colors ...Color) string {
if len(colors) == 0 {
return ""
}
var codes []string
for _, color := range colors {
codes = append(codes, color.String())
}
return strings.Join(codes, ";")
}
/*************************************************************
* HEX code <=> RGB/True color code
*************************************************************/
// Hex2rgb alias of the HexToRgb()
func Hex2rgb(hex string) []int { return HexToRgb(hex) }
// HexToRGB alias of the HexToRgb()
func HexToRGB(hex string) []int { return HexToRgb(hex) }
// HexToRgb convert hex color string to RGB numbers
//
// Usage:
//
// rgb := HexToRgb("ccc") // rgb: [204 204 204]
// rgb := HexToRgb("aabbcc") // rgb: [170 187 204]
// rgb := HexToRgb("#aabbcc") // rgb: [170 187 204]
// rgb := HexToRgb("0xad99c0") // rgb: [170 187 204]
func HexToRgb(hex string) (rgb []int) {
hex = strings.TrimSpace(hex)
if hex == "" {
return
}
// like from css. eg "#ccc" "#ad99c0"
if hex[0] == '#' {
hex = hex[1:]
}
hex = strings.ToLower(hex)
switch len(hex) {
case 3: // "ccc"
hex = string([]byte{hex[0], hex[0], hex[1], hex[1], hex[2], hex[2]})
case 8: // "0xad99c0"
hex = strings.TrimPrefix(hex, "0x")
}
// recheck
if len(hex) != 6 {
return
}
// convert string to int64
if i64, err := strconv.ParseInt(hex, 16, 32); err == nil {
color := int(i64)
// parse int
rgb = make([]int, 3)
rgb[0] = color >> 16
rgb[1] = (color & 0x00FF00) >> 8
rgb[2] = color & 0x0000FF
}
return
}
// Rgb2hex alias of the RgbToHex()
func Rgb2hex(rgb []int) string { return RgbToHex(rgb) }
// RgbToHex convert RGB-code to hex-code
//
// Usage:
//
// hex := RgbToHex([]int{170, 187, 204}) // hex: "aabbcc"
func RgbToHex(rgb []int) string {
hexNodes := make([]string, len(rgb))
for _, v := range rgb {
hexNodes = append(hexNodes, strconv.FormatInt(int64(v), 16))
}
return strings.Join(hexNodes, "")
}
/*************************************************************
* 4bit(16) color <=> RGB/True color
*************************************************************/
// BasicToHex convert basic color to hex string.
func BasicToHex(val uint8) string {
val = Bg2Fg(val)
return basic2hexMap[val]
}
// Basic2hex convert basic color to hex string.
func Basic2hex(val uint8) string {
return BasicToHex(val)
}
// Hex2basic convert hex string to basic color code.
func Hex2basic(hex string, asBg ...bool) uint8 {
val := hex2basicMap[hex]
if len(asBg) > 0 && asBg[0] {
return Fg2Bg(val)
}
return val
}
// Rgb2basic alias of the RgbToAnsi()
func Rgb2basic(r, g, b uint8, isBg bool) uint8 {
// is basic color, direct use static map data.
hex := RgbToHex([]int{int(r), int(g), int(b)})
if val, ok := hex2basicMap[hex]; ok {
if isBg {
return val + 10
}
return val
}
return RgbToAnsi(r, g, b, isBg)
}
// Rgb2ansi convert RGB-code to 16-code, alias of the RgbToAnsi()
func Rgb2ansi(r, g, b uint8, isBg bool) uint8 {
return RgbToAnsi(r, g, b, isBg)
}
// RgbToAnsi convert RGB-code to 16-code
// refer https://github.com/radareorg/radare2/blob/master/libr/cons/rgb.c#L249-L271
func RgbToAnsi(r, g, b uint8, isBg bool) uint8 {
var bright, c, k uint8
base := compareVal(isBg, BgBase, FgBase)
// eco bright-specific
if r == 0x80 && g == 0x80 && b == 0x80 { // 0x80=128
bright = 53
} else if r == 0xff || g == 0xff || b == 0xff { // 0xff=255
bright = 60
} // else bright = 0
if r == g && g == b {
// 0x7f=127
// r = (r > 0x7f) ? 1 : 0;
r = compareVal(r > 0x7f, 1, 0)
g = compareVal(g > 0x7f, 1, 0)
b = compareVal(b > 0x7f, 1, 0)
} else {
k = (r + g + b) / 3
// r = (r >= k) ? 1 : 0;
r = compareVal(r >= k, 1, 0)
g = compareVal(g >= k, 1, 0)
b = compareVal(b >= k, 1, 0)
}
// c = (r ? 1 : 0) + (g ? (b ? 6 : 2) : (b ? 4 : 0))
c = compareVal(r > 0, 1, 0)
if g > 0 {
c += compareVal(b > 0, 6, 2)
} else {
c += compareVal(b > 0, 4, 0)
}
return base + bright + c
}
/*************************************************************
* 8bit(256) color <=> RGB/True color
*************************************************************/
// Rgb2short convert RGB-code to 256-code
func Rgb2short(r, g, b uint8) uint8 {
return RgbTo256(r, g, b)
}
// RgbTo256 convert RGB-code to 256-code
func RgbTo256(r, g, b uint8) uint8 {
res := make([]uint8, 3)
for partI, part := range [3]uint8{r, g, b} {
i := 0
for i < len(incs)-1 {
s, b := incs[i], incs[i+1] // smaller, bigger
if s <= part && part <= b {
s1 := math.Abs(float64(s) - float64(part))
b1 := math.Abs(float64(b) - float64(part))
var closest uint8
if s1 < b1 {
closest = s
} else {
closest = b
}
res[partI] = closest
break
}
i++
}
}
hex := fmt.Sprintf("%02x%02x%02x", res[0], res[1], res[2])
equiv := hexTo256Table[hex]
return equiv
}
// C256ToRgb convert an 256 color code to RGB numbers
func C256ToRgb(val uint8) (rgb []uint8) {
hex := c256ToHexMap[val]
// convert to rgb code
rgbInts := Hex2rgb(hex)
return []uint8{
uint8(rgbInts[0]),
uint8(rgbInts[1]),
uint8(rgbInts[2]),
}
}
// C256ToRgbV1 convert an 256 color code to RGB numbers
// refer https://github.com/torvalds/linux/commit/cec5b2a97a11ade56a701e83044d0a2a984c67b4
func C256ToRgbV1(val uint8) (rgb []uint8) {
var r, g, b uint8
if val < 8 { // Standard colours.
// r = val&1 ? 0xaa : 0x00;
r = compareVal(val&1 == 1, 0xaa, 0x00)
g = compareVal(val&2 == 2, 0xaa, 0x00)
b = compareVal(val&4 == 4, 0xaa, 0x00)
} else if val < 16 {
// r = val & 1 ? 0xff : 0x55;
r = compareVal(val&1 == 1, 0xff, 0x55)
g = compareVal(val&2 == 2, 0xff, 0x55)
b = compareVal(val&4 == 4, 0xff, 0x55)
} else if val < 232 { /* 6x6x6 colour cube. */
r = (val - 16) / 36 * 85 / 2
g = (val - 16) / 6 % 6 * 85 / 2
b = (val - 16) % 6 * 85 / 2
} else { /* Grayscale ramp. */
nv := uint8(int(val)*10 - 2312)
// set value
r, g, b = nv, nv, nv
}
return []uint8{r, g, b}
}
/**************************************************************
* HSL color <=> RGB/True color
************************************************************
* h,s,l = Hue, Saturation, Lightness
*
* refers
* http://en.wikipedia.org/wiki/HSL_color_space
* https://www.w3.org/TR/css-color-3/#hsl-color
* https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
* https://github.com/less/less.js/blob/master/packages/less/src/less/functions/color.js
* https://github.com/d3/d3-color/blob/v3.0.1/README.md#hsl
*
* examples:
* color: hsl(0, 100%, 50%) // red
* color: hsl(120, 100%, 50%) // lime
* color: hsl(120, 100%, 25%) // dark green
* color: hsl(120, 100%, 75%) // light green
* color: hsl(120, 75%, 75%) // pastel green, and so on
*/
// HslIntToRgb Converts an HSL color value to RGB
// Assumes h: 0-360, s: 0-100, l: 0-100
// returns r, g, and b in the set [0, 255].
//
// Usage:
//
// HslIntToRgb(0, 100, 50) // red
// HslIntToRgb(120, 100, 50) // lime
// HslIntToRgb(120, 100, 25) // dark green
// HslIntToRgb(120, 100, 75) // light green
func HslIntToRgb(h, s, l int) (rgb []uint8) {
return HslToRgb(float64(h)/360, float64(s)/100, float64(l)/100)
}
// HslToRgb Converts an HSL color value to RGB. Conversion formula
// adapted from http://en.wikipedia.org/wiki/HSL_color_space.
// Assumes h, s, and l are contained in the set [0, 1]
// returns r, g, and b in the set [0, 255].
//
// Usage:
//
// rgbVals := HslToRgb(0, 1, 0.5) // red
func HslToRgb(h, s, l float64) (rgb []uint8) {
var r, g, b float64
if s == 0 { // achromatic
r, g, b = l, l, l
} else {
var hue2rgb = func(p, q, t float64) float64 {
if t < 0.0 {
t += 1
}
if t > 1.0 {
t -= 1
}
if t < 1.0/6.0 {
return p + (q-p)*6.0*t
}
if t < 1.0/2.0 {
return q
}
if t < 2.0/3.0 {
return p + (q-p)*(2.0/3.0-t)*6.0
}
return p
}
// q = l < 0.5 ? l * (1 + s) : l + s - l*s
var q float64
if l < 0.5 {
q = l * (1.0 + s)
} else {
q = l + s - l*s
}
var p = 2.0*l - q
r = hue2rgb(p, q, h+1.0/3.0)
g = hue2rgb(p, q, h)
b = hue2rgb(p, q, h-1.0/3.0)
}
// return []uint8{uint8(r * 255), uint8(g * 255), uint8(b * 255)}
return []uint8{
uint8(math.Round(r * 255)),
uint8(math.Round(g * 255)),
uint8(math.Round(b * 255)),
}
}
// RgbToHslInt Converts an RGB color value to HSL. Conversion formula
// Assumes r, g, and b are contained in the set [0, 255] and
// returns [h,s,l] h: 0-360, s: 0-100, l: 0-100.
func RgbToHslInt(r, g, b uint8) []int {
f64s := RgbToHsl(r, g, b)
return []int{int(f64s[0] * 360), int(f64s[1] * 100), int(f64s[2] * 100)}
}
// RgbToHsl Converts an RGB color value to HSL. Conversion formula
//
// adapted from http://en.wikipedia.org/wiki/HSL_color_space.
//
// Assumes r, g, and b are contained in the set [0, 255] and
// returns h, s, and l in the set [0, 1].
func RgbToHsl(r, g, b uint8) []float64 {
// to float64
fr, fg, fb := float64(r), float64(g), float64(b)
// percentage
pr, pg, pb := float64(r)/255.0, float64(g)/255.0, float64(b)/255.0
ps := []float64{pr, pg, pb}
sort.Float64s(ps)
min, max := ps[0], ps[2]
// max := math.Max(math.Max(pr, pg), pb)
// min := math.Min(math.Min(pr, pg), pb)
mid := (max + min) / 2
h, s, l := mid, mid, mid
if max == min {
h, s = 0, 0 // achromatic
} else {
var d = max - min
// s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
s = compareF64Val(l > 0.5, d/(2-max-min), d/(max+min))
switch max {
case fr:
// h = (g - b) / d + (g < b ? 6 : 0)
h = (fg - fb) / d
h += compareF64Val(g < b, 6, 0)
case fg:
h = (fb-fr)/d + 2
case fb:
h = (fr-fg)/d + 4
}
h /= 6
}
return []float64{h, s, l}
}
/**************************************************************
* HSV color <=> RGB/True color
************************************************************
* h,s,l = Hue, Saturation, Value(Brightness)
*
* refers
* https://stackoverflow.com/questions/2353211/hsl-to-rgb-color-conversion
* https://github.com/less/less.js/blob/master/packages/less/src/less/functions/color.js
* https://github.com/d3/d3-color/blob/v3.0.1/README.md#hsl
*/
// HsvToRgb Converts an HSL color value to RGB. Conversion formula
// adapted from https://en.wikipedia.org/wiki/HSL_and_HSV#HSV_to_RGB
// Assumes h: 0-360, s: 0-100, l: 0-100
// returns r, g, and b in the set [0, 255].
func HsvToRgb(h, s, v int) (rgb []uint8) {
// TODO ...
return
}
// Named rgb colors
// https://www.w3.org/TR/css-color-3/#svg-color
var namedRgbMap = map[string]string{
"aliceblue": "240,248,255", // #F0F8FF
"antiquewhite": "250,235,215", // #FAEBD7
"aqua": "0,255,255", // #00FFFF
"aquamarine": "127,255,212", // #7FFFD4
"azure": "240,255,255", // #F0FFFF
"beige": "245,245,220", // #F5F5DC
"bisque": "255,228,196", // #FFE4C4
"black": "0,0,0", // #000000
"blanchedalmond": "255,235,205", // #FFEBCD
"blue": "0,0,255", // #0000FF
"blueviolet": "138,43,226", // #8A2BE2
"brown": "165,42,42", // #A52A2A
"burlywood": "222,184,135", // #DEB887
"cadetblue": "95,158,160", // #5F9EA0
"chartreuse": "127,255,0", // #7FFF00
"chocolate": "210,105,30", // #D2691E
"coral": "255,127,80", // #FF7F50
"cornflowerblue": "100,149,237", // #6495ED
"cornsilk": "255,248,220", // #FFF8DC
"crimson": "220,20,60", // #DC143C
"cyan": "0,255,255", // #00FFFF
"darkblue": "0,0,139", // #00008B
"darkcyan": "0,139,139", // #008B8B
"darkgoldenrod": "184,134,11", // #B8860B
"darkgray": "169,169,169", // #A9A9A9
"darkgreen": "0,100,0", // #006400
"darkgrey": "169,169,169", // #A9A9A9
"darkkhaki": "189,183,107", // #BDB76B
"darkmagenta": "139,0,139", // #8B008B
"darkolivegreen": "85,107,47", // #556B2F
"darkorange": "255,140,0", // #FF8C00
"darkorchid": "153,50,204", // #9932CC
"darkred": "139,0,0", // #8B0000
"darksalmon": "233,150,122", // #E9967A
"darkseagreen": "143,188,143", // #8FBC8F
"darkslateblue": "72,61,139", // #483D8B
"darkslategray": "47,79,79", // #2F4F4F
"darkslategrey": "47,79,79", // #2F4F4F
"darkturquoise": "0,206,209", // #00CED1
"darkviolet": "148,0,211", // #9400D3
"deeppink": "255,20,147", // #FF1493
"deepskyblue": "0,191,255", // #00BFFF
"dimgray": "105,105,105", // #696969
"dimgrey": "105,105,105", // #696969
"dodgerblue": "30,144,255", // #1E90FF
"firebrick": "178,34,34", // #B22222
"floralwhite": "255,250,240", // #FFFAF0
"forestgreen": "34,139,34", // #228B22
"fuchsia": "255,0,255", // #FF00FF
"gainsboro": "220,220,220", // #DCDCDC
"ghostwhite": "248,248,255", // #F8F8FF
"gold": "255,215,0", // #FFD700
"goldenrod": "218,165,32", // #DAA520
"gray": "128,128,128", // #808080
"green": "0,128,0", // #008000
"greenyellow": "173,255,47", // #ADFF2F
"grey": "128,128,128", // #808080
"honeydew": "240,255,240", // #F0FFF0
"hotpink": "255,105,180", // #FF69B4
"indianred": "205,92,92", // #CD5C5C
"indigo": "75,0,130", // #4B0082
"ivory": "255,255,240", // #FFFFF0
"khaki": "240,230,140", // #F0E68C
"lavender": "230,230,250", // #E6E6FA
"lavenderblush": "255,240,245", // #FFF0F5
"lawngreen": "124,252,0", // #7CFC00
"lemonchiffon": "255,250,205", // #FFFACD
"lightblue": "173,216,230", // #ADD8E6
"lightcoral": "240,128,128", // #F08080
"lightcyan": "224,255,255", // #E0FFFF
"lightgoldenrodyellow": "250,250,210", // #FAFAD2
"lightgray": "211,211,211", // #D3D3D3
"lightgreen": "144,238,144", // #90EE90
"lightgrey": "211,211,211", // #D3D3D3
"lightpink": "255,182,193", // #FFB6C1
"lightsalmon": "255,160,122", // #FFA07A
"lightseagreen": "32,178,170", // #20B2AA
"lightskyblue": "135,206,250", // #87CEFA
"lightslategray": "119,136,153", // #778899
"lightslategrey": "119,136,153", // #778899
"lightsteelblue": "176,196,222", // #B0C4DE
"lightyellow": "255,255,224", // #FFFFE0
"lime": "0,255,0", // #00FF00
"limegreen": "50,205,50", // #32CD32
"linen": "250,240,230", // #FAF0E6
"magenta": "255,0,255", // #FF00FF
"maroon": "128,0,0", // #800000
"mediumaquamarine": "102,205,170", // #66CDAA
"mediumblue": "0,0,205", // #0000CD
"mediumorchid": "186,85,211", // #BA55D3
"mediumpurple": "147,112,219", // #9370DB
"mediumseagreen": "60,179,113", // #3CB371
"mediumslateblue": "123,104,238", // #7B68EE
"mediumspringgreen": "0,250,154", // #00FA9A
"mediumturquoise": "72,209,204", // #48D1CC
"mediumvioletred": "199,21,133", // #C71585
"midnightblue": "25,25,112", // #191970
"mintcream": "245,255,250", // #F5FFFA
"mistyrose": "255,228,225", // #FFE4E1
"moccasin": "255,228,181", // #FFE4B5
"navajowhite": "255,222,173", // #FFDEAD
"navy": "0,0,128", // #000080
"oldlace": "253,245,230", // #FDF5E6
"olive": "128,128,0", // #808000
"olivedrab": "107,142,35", // #6B8E23
"orange": "255,165,0", // #FFA500
"orangered": "255,69,0", // #FF4500
"orchid": "218,112,214", // #DA70D6
"palegoldenrod": "238,232,170", // #EEE8AA
"palegreen": "152,251,152", // #98FB98
"paleturquoise": "175,238,238", // #AFEEEE
"palevioletred": "219,112,147", // #DB7093
"papayawhip": "255,239,213", // #FFEFD5
"peachpuff": "255,218,185", // #FFDAB9
"peru": "205,133,63", // #CD853F
"pink": "255,192,203", // #FFC0CB
"plum": "221,160,221", // #DDA0DD
"powderblue": "176,224,230", // #B0E0E6
"purple": "128,0,128", // #800080
"red": "255,0,0", // #FF0000
"rosybrown": "188,143,143", // #BC8F8F
"royalblue": "65,105,225", // #4169E1
"saddlebrown": "139,69,19", // #8B4513
"salmon": "250,128,114", // #FA8072
"sandybrown": "244,164,96", // #F4A460
"seagreen": "46,139,87", // #2E8B57
"seashell": "255,245,238", // #FFF5EE
"sienna": "160,82,45", // #A0522D
"silver": "192,192,192", // #C0C0C0
"skyblue": "135,206,235", // #87CEEB
"slateblue": "106,90,205", // #6A5ACD
"slategray": "112,128,144", // #708090
"slategrey": "112,128,144", // #708090
"snow": "255,250,250", // #FFFAFA
"springgreen": "0,255,127", // #00FF7F
"steelblue": "70,130,180", // #4682B4
"tan": "210,180,140", // #D2B48C
"teal": "0,128,128", // #008080
"thistle": "216,191,216", // #D8BFD8
"tomato": "255,99,71", // #FF6347
"turquoise": "64,224,208", // #40E0D0
"violet": "238,130,238", // #EE82EE
"wheat": "245,222,179", // #F5DEB3
"white": "255,255,255", // #FFFFFF
"whitesmoke": "245,245,245", // #F5F5F5
"yellow": "255,255,0", // #FFFF00
"yellowgreen": "154,205,50", // #9ACD32
}

View File

@@ -1,291 +0,0 @@
package color
import (
"io"
"os"
"runtime"
"strconv"
"strings"
"syscall"
"github.com/xo/terminfo"
)
// Level is the color level supported by a terminal.
type Level = terminfo.ColorLevel
// terminal color available level alias of the terminfo.ColorLevel*
const (
LevelNo = terminfo.ColorLevelNone // not support color.
Level16 = terminfo.ColorLevelBasic // basic - 3/4 bit color supported
Level256 = terminfo.ColorLevelHundreds // hundreds - 8-bit color supported
LevelRgb = terminfo.ColorLevelMillions // millions - (24 bit)true color supported
)
/*************************************************************
* helper methods for detect color supports
*************************************************************/
// DetectColorLevel for current env
//
// NOTICE: The method will detect terminal info each times,
// if only want to get current color level, please direct call SupportColor() or TermColorLevel()
func DetectColorLevel() Level {
level, _ := detectTermColorLevel()
return level
}
// detect terminal color support level
//
// refer https://github.com/Delta456/box-cli-maker
func detectTermColorLevel() (level Level, needVTP bool) {
// on windows WSL:
// - runtime.GOOS == "Linux"
// - support true-color
// env:
// WSL_DISTRO_NAME=Debian
if val := os.Getenv("WSL_DISTRO_NAME"); val != "" {
// detect WSL as it has True Color support
if detectWSL() {
debugf("True Color support on WSL environment")
return terminfo.ColorLevelMillions, false
}
}
isWin := runtime.GOOS == "windows"
termVal := os.Getenv("TERM")
// on TERM=screen: not support true-color
if termVal != "screen" {
// On JetBrains Terminal
// - support true-color
// env:
// TERMINAL_EMULATOR=JetBrains-JediTerm
val := os.Getenv("TERMINAL_EMULATOR")
if val == "JetBrains-JediTerm" {
debugf("True Color support on JetBrains-JediTerm, is win: %v", isWin)
return terminfo.ColorLevelMillions, isWin
}
}
// level, err = terminfo.ColorLevelFromEnv()
level = detectColorLevelFromEnv(termVal, isWin)
debugf("color level by detectColorLevelFromEnv: %s", level.String())
// fallback: simple detect by TERM value string.
if level == terminfo.ColorLevelNone {
debugf("level none - fallback check special term color support")
// on Windows: enable VTP as it has True Color support
level, needVTP = detectSpecialTermColor(termVal)
}
return
}
// detectColorFromEnv returns the color level COLORTERM, FORCE_COLOR,
// TERM_PROGRAM, or determined from the TERM environment variable.
//
// refer the terminfo.ColorLevelFromEnv()
// https://en.wikipedia.org/wiki/Terminfo
func detectColorLevelFromEnv(termVal string, isWin bool) Level {
// check for overriding environment variables
colorTerm, termProg, forceColor := os.Getenv("COLORTERM"), os.Getenv("TERM_PROGRAM"), os.Getenv("FORCE_COLOR")
switch {
case strings.Contains(colorTerm, "truecolor") || strings.Contains(colorTerm, "24bit"):
if termVal == "screen" { // on TERM=screen: not support true-color
return terminfo.ColorLevelHundreds
}
return terminfo.ColorLevelMillions
case colorTerm != "" || forceColor != "":
return terminfo.ColorLevelBasic
case termProg == "Apple_Terminal":
return terminfo.ColorLevelHundreds
case termProg == "Terminus" || termProg == "Hyper":
if termVal == "screen" { // on TERM=screen: not support true-color
return terminfo.ColorLevelHundreds
}
return terminfo.ColorLevelMillions
case termProg == "iTerm.app":
if termVal == "screen" { // on TERM=screen: not support true-color
return terminfo.ColorLevelHundreds
}
// check iTerm version
ver := os.Getenv("TERM_PROGRAM_VERSION")
if ver != "" {
i, err := strconv.Atoi(strings.Split(ver, ".")[0])
if err != nil {
saveInternalError(terminfo.ErrInvalidTermProgramVersion)
// return terminfo.ColorLevelNone
return terminfo.ColorLevelHundreds
}
if i == 3 {
return terminfo.ColorLevelMillions
}
}
return terminfo.ColorLevelHundreds
}
// otherwise determine from TERM's max_colors capability
if !isWin && termVal != "" {
debugf("TERM=%s - check color level by load terminfo file", termVal)
ti, err := terminfo.Load(termVal)
if err != nil {
saveInternalError(err)
return terminfo.ColorLevelNone
}
debugf("the loaded term info file is: %s", ti.File)
v, ok := ti.Nums[terminfo.MaxColors]
switch {
case !ok || v <= 16:
return terminfo.ColorLevelNone
case ok && v >= 256:
return terminfo.ColorLevelHundreds
}
return terminfo.ColorLevelBasic
}
// no TERM env value. default return none level
return terminfo.ColorLevelNone
// return terminfo.ColorLevelBasic
}
var detectedWSL bool
var wslContents string
// https://github.com/Microsoft/WSL/issues/423#issuecomment-221627364
func detectWSL() bool {
if !detectedWSL {
detectedWSL = true
b := make([]byte, 1024)
// `cat /proc/version`
// on mac:
// !not the file!
// on linux(debian,ubuntu,alpine):
// Linux version 4.19.121-linuxkit (root@18b3f92ade35) (gcc version 9.2.0 (Alpine 9.2.0)) #1 SMP Thu Jan 21 15:36:34 UTC 2021
// on win git bash, conEmu:
// MINGW64_NT-10.0-19042 version 3.1.7-340.x86_64 (@WIN-N0G619FD3UK) (gcc version 9.3.0 (GCC) ) 2020-10-23 13:08 UTC
// on WSL:
// Linux version 4.4.0-19041-Microsoft (Microsoft@Microsoft.com) (gcc version 5.4.0 (GCC) ) #488-Microsoft Mon Sep 01 13:43:00 PST 2020
f, err := os.Open("/proc/version")
if err == nil {
_, _ = f.Read(b) // ignore error
if err = f.Close(); err != nil {
saveInternalError(err)
}
wslContents = string(b)
return strings.Contains(wslContents, "Microsoft")
}
}
return false
}
/*
// refer
// https://github.com/Delta456/box-cli-maker/blob/7b5a1ad8a016ce181e7d8b05e24b54ff60b4b38a/detect_unix.go#L27-L45
// detect WSL as it has True Color support
func isWSL() bool {
// on windows WSL:
// - runtime.GOOS == "Linux"
// - support true-color
// WSL_DISTRO_NAME=Debian
if val := os.Getenv("WSL_DISTRO_NAME"); val == "" {
return false
}
// `cat /proc/sys/kernel/osrelease`
// on mac:
// !not the file!
// on linux:
// 4.19.121-linuxkit
// on WSL Output:
// 4.4.0-19041-Microsoft
wsl, err := ioutil.ReadFile("/proc/sys/kernel/osrelease")
if err != nil {
saveInternalError(err)
return false
}
// it gives "Microsoft" for WSL and "microsoft" for WSL 2
// it supports True-color
content := strings.ToLower(string(wsl))
return strings.Contains(content, "microsoft")
}
*/
/*************************************************************
* helper methods for check env
*************************************************************/
// IsWindows OS env
func IsWindows() bool {
return runtime.GOOS == "windows"
}
// IsConsole Determine whether w is one of stderr, stdout, stdin
func IsConsole(w io.Writer) bool {
o, ok := w.(*os.File)
if !ok {
return false
}
fd := o.Fd()
// fix: cannot use 'o == os.Stdout' to compare
return fd == uintptr(syscall.Stdout) || fd == uintptr(syscall.Stdin) || fd == uintptr(syscall.Stderr)
}
// IsMSys msys(MINGW64) environment, does not necessarily support color
func IsMSys() bool {
// like "MSYSTEM=MINGW64"
return len(os.Getenv("MSYSTEM")) > 0
}
// IsSupportColor check current console is support color.
//
// NOTICE: The method will detect terminal info each times,
// if only want get current color level, please direct call SupportColor() or TermColorLevel()
func IsSupportColor() bool {
return IsSupport16Color()
}
// IsSupport16Color check current console is support color.
//
// NOTICE: The method will detect terminal info each times,
// if only want get current color level, please direct call SupportColor() or TermColorLevel()
func IsSupport16Color() bool {
level, _ := detectTermColorLevel()
return level > terminfo.ColorLevelNone
}
// IsSupport256Color render check
//
// NOTICE: The method will detect terminal info each times,
// if only want to get current color level, please direct call SupportColor() or TermColorLevel()
func IsSupport256Color() bool {
level, _ := detectTermColorLevel()
return level > terminfo.ColorLevelBasic
}
// IsSupportRGBColor check. alias of the IsSupportTrueColor()
//
// NOTICE: The method will detect terminal info each times,
// if only want get current color level, please direct call SupportColor() or TermColorLevel()
func IsSupportRGBColor() bool {
return IsSupportTrueColor()
}
// IsSupportTrueColor render check.
//
// NOTICE: The method will detect terminal info each times,
// if only want get current color level, please direct call SupportColor() or TermColorLevel()
//
// ENV:
// "COLORTERM=truecolor"
// "COLORTERM=24bit"
func IsSupportTrueColor() bool {
level, _ := detectTermColorLevel()
return level > terminfo.ColorLevelHundreds
}

View File

@@ -1,49 +0,0 @@
//go:build !windows
// +build !windows
// The method in the file has no effect
// Only for compatibility with non-Windows systems
package color
import (
"strings"
"syscall"
"github.com/xo/terminfo"
)
// detect special term color support
func detectSpecialTermColor(termVal string) (Level, bool) {
if termVal == "" {
return terminfo.ColorLevelNone, false
}
debugf("terminfo check fail - fallback detect color by check TERM value")
// on TERM=screen:
// - support 256, not support true-color. test on macOS
if termVal == "screen" {
return terminfo.ColorLevelHundreds, false
}
if strings.Contains(termVal, "256color") {
return terminfo.ColorLevelHundreds, false
}
if strings.Contains(termVal, "xterm") {
return terminfo.ColorLevelHundreds, false
// return terminfo.ColorLevelBasic, false
}
// return terminfo.ColorLevelNone, nil
return terminfo.ColorLevelBasic, false
}
// IsTerminal returns true if the given file descriptor is a terminal.
//
// Usage:
// IsTerminal(os.Stdout.Fd())
func IsTerminal(fd uintptr) bool {
return fd == uintptr(syscall.Stdout) || fd == uintptr(syscall.Stdin) || fd == uintptr(syscall.Stderr)
}

Some files were not shown because too many files have changed in this diff Show More