[chore] update viper version (#2539)
* update viper version * removes our last uses of the slice package * fix tests
This commit is contained in:
parent
c5eced5fd1
commit
906639ad7e
31
go.mod
31
go.mod
|
@ -43,7 +43,7 @@ require (
|
|||
github.com/oklog/ulid v1.3.1
|
||||
github.com/prometheus/client_golang v1.18.0
|
||||
github.com/spf13/cobra v1.8.0
|
||||
github.com/spf13/viper v1.16.0
|
||||
github.com/spf13/viper v1.18.2
|
||||
github.com/stretchr/testify v1.8.4
|
||||
github.com/superseriousbusiness/activity v1.4.0-gts
|
||||
github.com/superseriousbusiness/oauth2/v4 v4.3.2-SSB.0.20230227143000-f4900831d6c8
|
||||
|
@ -66,7 +66,6 @@ require (
|
|||
go.opentelemetry.io/otel/trace v1.21.0
|
||||
go.uber.org/automaxprocs v1.5.3
|
||||
golang.org/x/crypto v0.18.0
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1
|
||||
golang.org/x/image v0.15.0
|
||||
golang.org/x/net v0.20.0
|
||||
golang.org/x/oauth2 v0.16.0
|
||||
|
@ -95,7 +94,7 @@ require (
|
|||
github.com/containerd/cgroups/v3 v3.0.1 // indirect
|
||||
github.com/coreos/go-systemd/v22 v22.3.2 // indirect
|
||||
github.com/cornelk/hashmap v1.0.8 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/docker/go-units v0.4.0 // indirect
|
||||
github.com/dsoprea/go-exif/v3 v3.0.0-20210625224831-a6301f85c82b // indirect
|
||||
github.com/dsoprea/go-iptc v0.0.0-20200610044640-bc9ca208b413 // indirect
|
||||
|
@ -143,21 +142,23 @@ require (
|
|||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/opencontainers/runtime-spec v1.0.2 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_model v0.5.0 // indirect
|
||||
github.com/prometheus/common v0.45.0 // indirect
|
||||
github.com/prometheus/procfs v0.12.0 // indirect
|
||||
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rs/xid v1.5.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/spf13/afero v1.9.5 // indirect
|
||||
github.com/spf13/cast v1.5.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/subosito/gotenv v1.4.2 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/superseriousbusiness/go-jpeg-image-structure/v2 v2.0.0-20220321154430-d89a106fdabe // indirect
|
||||
github.com/superseriousbusiness/go-png-image-structure/v2 v2.0.1-SSB // indirect
|
||||
github.com/tdewolff/parse/v2 v2.7.8 // indirect
|
||||
|
@ -170,14 +171,16 @@ require (
|
|||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.21.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.0.0 // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
golang.org/x/arch v0.5.0 // indirect
|
||||
golang.org/x/mod v0.12.0 // indirect
|
||||
golang.org/x/sync v0.4.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/sync v0.6.0 // indirect
|
||||
golang.org/x/sys v0.16.0 // indirect
|
||||
golang.org/x/tools v0.13.0 // indirect
|
||||
golang.org/x/tools v0.17.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect
|
||||
google.golang.org/grpc v1.59.0 // indirect
|
||||
google.golang.org/protobuf v1.31.0 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
|
|
139
go.sum
139
go.sum
|
@ -3,7 +3,6 @@ cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMT
|
|||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
|
@ -14,9 +13,6 @@ cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKV
|
|||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI=
|
||||
cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk=
|
||||
cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
|
@ -34,7 +30,6 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
|
|||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo=
|
||||
codeberg.org/gruf/go-atomics v1.1.0 h1:ni9QXYoRUFYQMXE3akWaUb1wMcPBDc05Md6Rgml7W58=
|
||||
codeberg.org/gruf/go-atomics v1.1.0/go.mod h1:a/4/y/LgvjxjQVnpoy1VVkOSzLS1W9i1g4SJ0nflAa4=
|
||||
codeberg.org/gruf/go-bitutil v1.0.0/go.mod h1:sb8IjlDnjVTz8zPK/8lmHesKxY0Yb3iqHWjUM/SkphA=
|
||||
|
@ -119,8 +114,6 @@ github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4=
|
|||
github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ=
|
||||
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4=
|
||||
github.com/containerd/cgroups/v3 v3.0.1 h1:4hfGvu8rfGIwVIDd+nLzn/B9ZXx4BcCjzt5ToenJRaE=
|
||||
|
@ -135,8 +128,9 @@ github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
|
|||
github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/disintegration/imaging v1.6.2 h1:w1LecBlG2Lnp8B3jk5zSuNqd7b4DXhcjwek1ei82L+c=
|
||||
github.com/disintegration/imaging v1.6.2/go.mod h1:44/5580QXChDfwIclfc/PCwrr44amcmDAg8hxG0Ewe4=
|
||||
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
|
||||
|
@ -162,14 +156,12 @@ github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+m
|
|||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/frankban/quicktest v1.14.4 h1:g2rn0vABPOOXmZUj+vbmUp0lPoXEMuhTpIluN0XL9UY=
|
||||
github.com/frankban/quicktest v1.14.4/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||
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.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||
|
@ -269,7 +261,6 @@ github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:W
|
|||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
|
@ -283,8 +274,6 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
|
@ -293,7 +282,6 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO
|
|||
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=
|
||||
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
|
@ -301,9 +289,6 @@ github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hf
|
|||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
|
||||
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
|
@ -313,7 +298,6 @@ github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU=
|
|||
github.com/google/uuid v1.5.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/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8=
|
||||
|
@ -339,7 +323,6 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
|||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/imkira/go-interpol v1.1.0 h1:KIiKr0VSG2CUW1hl1jpiyuzuJeKUUpC8iM1AIE7N1Vk=
|
||||
github.com/imkira/go-interpol v1.1.0/go.mod h1:z0h2/2T3XF8kyEPpRgJ3kmNv+C43p+I/CoI+jC3w2iA=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
|
@ -374,7 +357,6 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
|
|||
github.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=
|
||||
github.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=
|
||||
github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk=
|
||||
|
@ -433,14 +415,14 @@ github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/
|
|||
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e h1:s2RNOM/IGdY0Y6qfTeUKhDawdHDpK9RGBdx80qN4Ttw=
|
||||
github.com/orcaman/writerseeker v0.0.0-20200621085525-1d3f536ff85e/go.mod h1:nBdnFKj15wFbf94Rwfq4m30eAcyY9V/IyKAGQFtqkW0=
|
||||
github.com/pelletier/go-toml/v2 v2.0.1/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0 h1:FnwAJ4oYMvbT/34k9zzHuZNrhlz48GB3/s6at6/MHO4=
|
||||
github.com/pelletier/go-toml/v2 v2.1.0/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||
github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
|
||||
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.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/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=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk=
|
||||
|
@ -464,6 +446,10 @@ github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncj
|
|||
github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6keLGt6kNQ=
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw=
|
||||
github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0=
|
||||
github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
|
||||
|
@ -473,25 +459,23 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykE
|
|||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/spf13/afero v1.9.5 h1:stMpOSZFs//0Lv29HduCmli3GUfpFoF3Y1Q/aXj/wVM=
|
||||
github.com/spf13/afero v1.9.5/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ=
|
||||
github.com/spf13/cast v1.5.1 h1:R+kOtfhWQE6TVQzY+4D7wJLBgkdVasCEFxSUBYBYIlA=
|
||||
github.com/spf13/cast v1.5.1/go.mod h1:b9PdjNptOpzXr7Rq1q9gJML/2cdGQAo69NKzQ10KN48=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.6.0 h1:GEiTHELF+vaR5dhz3VqZfFSzZjYbgeKDpBxQVS4GYJ0=
|
||||
github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
|
||||
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.16.0 h1:rGGH0XDZhdUOryiDWjmIvUSWpbNqisK8Wk0Vyefw8hc=
|
||||
github.com/spf13/viper v1.16.0/go.mod h1:yg78JgCJcbrQOvV9YLXgkLaZqUidkY9K+Dd1FofRzQg=
|
||||
github.com/spf13/viper v1.18.2 h1:LUXCnvUvSM6FXAsj6nnfc8Q2tp1dIgUfY9Kc8GsSOiQ=
|
||||
github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMVB+yk=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
|
@ -500,8 +484,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
|||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
|
||||
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
|
||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||
github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365/go.mod h1:dEzdXgvImkQ3WLI+0KQpmEx8T/C/ma9KeS3AfmU899I=
|
||||
github.com/superseriousbusiness/activity v1.4.0-gts h1:9r95sYy80tuGWWpDDNlLwa/k6dKZdyP/k+rhVA+VjdQ=
|
||||
github.com/superseriousbusiness/activity v1.4.0-gts/go.mod h1:AZw0Xb4Oju8rmaJCZ21gc5CPg47MmNgyac+Hx5jo8VM=
|
||||
|
@ -590,7 +574,6 @@ github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZ
|
|||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.6.0 h1:boZcn2GTjpsynOsC0iJHnBWa4Bi0qzfJjthwauItG68=
|
||||
github.com/yuin/goldmark v1.6.0/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
|
@ -599,7 +582,6 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
|||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
|
||||
go.opentelemetry.io/otel v1.21.0 h1:hzLeKBZEL7Okw2mGzZ0cc4k/A7Fta0uoPgaJCr8fsFc=
|
||||
go.opentelemetry.io/otel v1.21.0/go.mod h1:QZzNPQPm1zLX4gZK4cMi+71eaorMSGT3A4znnUvNNEo=
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.21.0 h1:cl5P5/GIfFh4t6xyruOgJP5QiA1pw4fYYdv6nc6CBWw=
|
||||
|
@ -624,6 +606,8 @@ go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
|||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
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.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||
golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
golang.org/x/arch v0.5.0 h1:jpGode6huXQxcskEIpOCvrU+tzo81b6+oFLUYXWtH/Y=
|
||||
golang.org/x/arch v0.5.0/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8=
|
||||
|
@ -633,10 +617,8 @@ golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8U
|
|||
golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4=
|
||||
golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
|
||||
golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
|
@ -649,8 +631,8 @@ 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-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc=
|
||||
golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w=
|
||||
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o=
|
||||
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
|
||||
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.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
|
@ -666,7 +648,6 @@ golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHl
|
|||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
|
@ -675,11 +656,9 @@ golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzB
|
|||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.12.0 h1:rmsUpXtvNzj340zd98LZ4KntptpfRHwpFOHG188oHXc=
|
||||
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
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=
|
||||
|
@ -709,12 +688,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
|
|||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
|
||||
golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
|
||||
|
@ -723,10 +697,6 @@ golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4Iltr
|
|||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/oauth2 v0.16.0 h1:aDkGMBSYxElaoP81NpoUoz2oo2R2wHdZpGToUxfyQrQ=
|
||||
golang.org/x/oauth2 v0.16.0/go.mod h1:hqZ+0LWXsiVoZpeld6jVt06P3adbS2Uu911W1SsJv2o=
|
||||
|
@ -738,11 +708,9 @@ golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJ
|
|||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.4.0 h1:zxkM55ReGkDlKSM+Fu41A+zmbZuaPVbGMzvvdUPznYQ=
|
||||
golang.org/x/sync v0.4.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
@ -774,15 +742,7 @@ golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
|
@ -802,7 +762,6 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ=
|
||||
|
@ -852,16 +811,9 @@ golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roY
|
|||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE=
|
||||
golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.13.0 h1:Iey4qkscZuv0VvIt8E0neZjtPVQFSc870HQ448QgEmQ=
|
||||
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
|
||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
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=
|
||||
|
@ -882,16 +834,12 @@ google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0M
|
|||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg=
|
||||
google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE=
|
||||
google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
|
@ -923,19 +871,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-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY=
|
||||
google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M=
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ=
|
||||
google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17 h1:JpwMPBpFN3uKhdaekDpiNlImDdkUAyiJ6ez/uxGaUSo=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:0xJLfVdJqpAPl8tDg1ujOCGzx6LFLttXT5NhllGOXY4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
|
@ -948,10 +889,6 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
|
|||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
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.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
|
||||
google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
|
||||
google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk=
|
||||
google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"slices"
|
||||
"strconv"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
@ -30,7 +31,6 @@ import (
|
|||
apiutil "github.com/superseriousbusiness/gotosocial/internal/api/util"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// AccountUpdateCredentialsPATCHHandler swagger:operation PATCH /api/v1/accounts/update_credentials accountUpdate
|
||||
|
@ -283,8 +283,16 @@ func parseFieldsAttributesFromJSON(jsonFieldsAttributes *map[string]apimodel.Upd
|
|||
}
|
||||
|
||||
// Sort slice by the key each field was submitted with.
|
||||
slices.SortFunc(fieldsAttributes, func(a, b apimodel.UpdateField) bool {
|
||||
return a.Key < b.Key
|
||||
slices.SortFunc(fieldsAttributes, func(a, b apimodel.UpdateField) int {
|
||||
const k = +1
|
||||
switch {
|
||||
case a.Key > b.Key:
|
||||
return +k
|
||||
case a.Key < b.Key:
|
||||
return -k
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
})
|
||||
|
||||
return &fieldsAttributes, nil
|
||||
|
|
|
@ -19,6 +19,7 @@ package streaming
|
|||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"codeberg.org/gruf/go-kv"
|
||||
|
@ -28,7 +29,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/oauth"
|
||||
streampkg "github.com/superseriousbusiness/gotosocial/internal/stream"
|
||||
"golang.org/x/exp/slices"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/gorilla/websocket"
|
||||
|
|
|
@ -18,8 +18,9 @@
|
|||
package cache
|
||||
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"codeberg.org/gruf/go-cache/v3/simple"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
// SliceCache wraps a simple.Cache to provide simple loader-callback
|
||||
|
|
|
@ -19,13 +19,13 @@ package bundb_test
|
|||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/suite"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type ListTestSuite struct {
|
||||
|
@ -43,8 +43,16 @@ func (suite *ListTestSuite) testStructs() (*gtsmodel.List, *gtsmodel.Account) {
|
|||
}
|
||||
|
||||
// Sort by ID descending (again, as we'd expect from the db).
|
||||
slices.SortFunc(entries, func(a, b *gtsmodel.ListEntry) bool {
|
||||
return b.ID < a.ID
|
||||
slices.SortFunc(entries, func(a, b *gtsmodel.ListEntry) int {
|
||||
const k = -1
|
||||
switch {
|
||||
case a.ID > b.ID:
|
||||
return +k
|
||||
case a.ID < b.ID:
|
||||
return -k
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
})
|
||||
|
||||
testList.ListEntries = entries
|
||||
|
@ -239,8 +247,16 @@ func (suite *ListTestSuite) TestPutListEntries() {
|
|||
// Add these entries to the test list, sort it again
|
||||
// to reflect what we'd expect to get from the db.
|
||||
testList.ListEntries = append(testList.ListEntries, listEntries...)
|
||||
slices.SortFunc(testList.ListEntries, func(a, b *gtsmodel.ListEntry) bool {
|
||||
return b.ID < a.ID
|
||||
slices.SortFunc(testList.ListEntries, func(a, b *gtsmodel.ListEntry) int {
|
||||
const k = -1
|
||||
switch {
|
||||
case a.ID > b.ID:
|
||||
return +k
|
||||
case a.ID < b.ID:
|
||||
return -k
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
})
|
||||
|
||||
// Now get all list entries from the db.
|
||||
|
|
|
@ -21,6 +21,7 @@ import (
|
|||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"slices"
|
||||
"time"
|
||||
|
||||
"github.com/superseriousbusiness/gotosocial/internal/db"
|
||||
|
@ -31,7 +32,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"github.com/uptrace/bun"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
type timelineDB struct {
|
||||
|
@ -311,8 +311,16 @@ func (t *timelineDB) GetFavedTimeline(ctx context.Context, accountID string, max
|
|||
}
|
||||
|
||||
// Sort by favourite ID rather than status ID
|
||||
slices.SortFunc(faves, func(a, b *gtsmodel.StatusFave) bool {
|
||||
return a.ID > b.ID
|
||||
slices.SortFunc(faves, func(a, b *gtsmodel.StatusFave) int {
|
||||
const k = -1
|
||||
switch {
|
||||
case a.ID > b.ID:
|
||||
return +k
|
||||
case a.ID < b.ID:
|
||||
return -k
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
})
|
||||
|
||||
statuses := make([]*gtsmodel.Status, 0, len(faves))
|
||||
|
|
|
@ -19,6 +19,7 @@ package admin
|
|||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
|
@ -26,7 +27,6 @@ import (
|
|||
"github.com/superseriousbusiness/gotosocial/internal/gtsmodel"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/log"
|
||||
"github.com/superseriousbusiness/gotosocial/internal/state"
|
||||
"golang.org/x/exp/slices"
|
||||
)
|
||||
|
||||
func errActionConflict(action *gtsmodel.AdminAction) gtserror.WithCode {
|
||||
|
@ -140,8 +140,16 @@ func (a *Actions) GetRunning() []*gtsmodel.AdminAction {
|
|||
// Order by ID descending (creation date).
|
||||
slices.SortFunc(
|
||||
running,
|
||||
func(a *gtsmodel.AdminAction, b *gtsmodel.AdminAction) bool {
|
||||
return a.ID > b.ID
|
||||
func(a *gtsmodel.AdminAction, b *gtsmodel.AdminAction) int {
|
||||
const k = -1
|
||||
switch {
|
||||
case a.ID > b.ID:
|
||||
return +k
|
||||
case a.ID < b.ID:
|
||||
return -k
|
||||
default:
|
||||
return 0
|
||||
}
|
||||
},
|
||||
)
|
||||
|
||||
|
|
|
@ -3,4 +3,5 @@ fuzz/
|
|||
cmd/tomll/tomll
|
||||
cmd/tomljson/tomljson
|
||||
cmd/tomltestgen/tomltestgen
|
||||
dist
|
||||
dist
|
||||
tests/
|
||||
|
|
|
@ -179,12 +179,12 @@ Execution time speedup compared to other Go TOML libraries:
|
|||
<tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>Marshal/HugoFrontMatter-2</td><td>1.9x</td><td>1.9x</td></tr>
|
||||
<tr><td>Marshal/ReferenceFile/map-2</td><td>1.7x</td><td>1.8x</td></tr>
|
||||
<tr><td>Marshal/ReferenceFile/struct-2</td><td>2.2x</td><td>2.5x</td></tr>
|
||||
<tr><td>Unmarshal/HugoFrontMatter-2</td><td>2.9x</td><td>2.9x</td></tr>
|
||||
<tr><td>Unmarshal/ReferenceFile/map-2</td><td>2.6x</td><td>2.9x</td></tr>
|
||||
<tr><td>Unmarshal/ReferenceFile/struct-2</td><td>4.4x</td><td>5.3x</td></tr>
|
||||
<tr><td>Marshal/HugoFrontMatter-2</td><td>1.9x</td><td>2.2x</td></tr>
|
||||
<tr><td>Marshal/ReferenceFile/map-2</td><td>1.7x</td><td>2.1x</td></tr>
|
||||
<tr><td>Marshal/ReferenceFile/struct-2</td><td>2.2x</td><td>3.0x</td></tr>
|
||||
<tr><td>Unmarshal/HugoFrontMatter-2</td><td>2.9x</td><td>2.7x</td></tr>
|
||||
<tr><td>Unmarshal/ReferenceFile/map-2</td><td>2.6x</td><td>2.7x</td></tr>
|
||||
<tr><td>Unmarshal/ReferenceFile/struct-2</td><td>4.6x</td><td>5.1x</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<details><summary>See more</summary>
|
||||
|
@ -197,17 +197,17 @@ provided for completeness.</p>
|
|||
<tr><th>Benchmark</th><th>go-toml v1</th><th>BurntSushi/toml</th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr><td>Marshal/SimpleDocument/map-2</td><td>1.8x</td><td>2.9x</td></tr>
|
||||
<tr><td>Marshal/SimpleDocument/struct-2</td><td>2.7x</td><td>4.2x</td></tr>
|
||||
<tr><td>Unmarshal/SimpleDocument/map-2</td><td>4.5x</td><td>3.1x</td></tr>
|
||||
<tr><td>Unmarshal/SimpleDocument/struct-2</td><td>6.2x</td><td>3.9x</td></tr>
|
||||
<tr><td>UnmarshalDataset/example-2</td><td>3.1x</td><td>3.5x</td></tr>
|
||||
<tr><td>UnmarshalDataset/code-2</td><td>2.3x</td><td>3.1x</td></tr>
|
||||
<tr><td>UnmarshalDataset/twitter-2</td><td>2.5x</td><td>2.6x</td></tr>
|
||||
<tr><td>UnmarshalDataset/citm_catalog-2</td><td>2.1x</td><td>2.2x</td></tr>
|
||||
<tr><td>UnmarshalDataset/canada-2</td><td>1.6x</td><td>1.3x</td></tr>
|
||||
<tr><td>UnmarshalDataset/config-2</td><td>4.3x</td><td>3.2x</td></tr>
|
||||
<tr><td>[Geo mean]</td><td>2.7x</td><td>2.8x</td></tr>
|
||||
<tr><td>Marshal/SimpleDocument/map-2</td><td>1.8x</td><td>2.7x</td></tr>
|
||||
<tr><td>Marshal/SimpleDocument/struct-2</td><td>2.7x</td><td>3.8x</td></tr>
|
||||
<tr><td>Unmarshal/SimpleDocument/map-2</td><td>3.8x</td><td>3.0x</td></tr>
|
||||
<tr><td>Unmarshal/SimpleDocument/struct-2</td><td>5.6x</td><td>4.1x</td></tr>
|
||||
<tr><td>UnmarshalDataset/example-2</td><td>3.0x</td><td>3.2x</td></tr>
|
||||
<tr><td>UnmarshalDataset/code-2</td><td>2.3x</td><td>2.9x</td></tr>
|
||||
<tr><td>UnmarshalDataset/twitter-2</td><td>2.6x</td><td>2.7x</td></tr>
|
||||
<tr><td>UnmarshalDataset/citm_catalog-2</td><td>2.2x</td><td>2.3x</td></tr>
|
||||
<tr><td>UnmarshalDataset/canada-2</td><td>1.8x</td><td>1.5x</td></tr>
|
||||
<tr><td>UnmarshalDataset/config-2</td><td>4.1x</td><td>2.9x</td></tr>
|
||||
<tr><td>geomean</td><td>2.7x</td><td>2.8x</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p>This table can be generated with <code>./ci.sh benchmark -a -html</code>.</p>
|
||||
|
|
|
@ -2,9 +2,6 @@
|
|||
|
||||
## Supported Versions
|
||||
|
||||
Use this section to tell people about which versions of your project are
|
||||
currently being supported with security updates.
|
||||
|
||||
| Version | Supported |
|
||||
| ---------- | ------------------ |
|
||||
| Latest 2.x | :white_check_mark: |
|
||||
|
|
|
@ -77,7 +77,7 @@ cover() {
|
|||
|
||||
pushd "$dir"
|
||||
go test -covermode=atomic -coverpkg=./... -coverprofile=coverage.out.tmp ./...
|
||||
cat coverage.out.tmp | grep -v fuzz | grep -v testsuite | grep -v tomltestgen | grep -v gotoml-test-decoder > coverage.out
|
||||
grep -Ev '(fuzz|testsuite|tomltestgen|gotoml-test-decoder|gotoml-test-encoder)' coverage.out.tmp > coverage.out
|
||||
go tool cover -func=coverage.out
|
||||
echo "Coverage profile for ${branch}: ${dir}/coverage.out" >&2
|
||||
popd
|
||||
|
@ -152,7 +152,7 @@ bench() {
|
|||
fi
|
||||
|
||||
export GOMAXPROCS=2
|
||||
nice -n -19 taskset --cpu-list 0,1 go test '-bench=^Benchmark(Un)?[mM]arshal' -count=5 -run=Nothing ./... | tee "${out}"
|
||||
go test '-bench=^Benchmark(Un)?[mM]arshal' -count=10 -run=Nothing ./... | tee "${out}"
|
||||
popd
|
||||
|
||||
if [ "${branch}" != "HEAD" ]; then
|
||||
|
@ -161,10 +161,12 @@ bench() {
|
|||
}
|
||||
|
||||
fmktemp() {
|
||||
if mktemp --version|grep GNU >/dev/null; then
|
||||
mktemp --suffix=-$1;
|
||||
if mktemp --version &> /dev/null; then
|
||||
# GNU
|
||||
mktemp --suffix=-$1
|
||||
else
|
||||
mktemp -t $1;
|
||||
# BSD
|
||||
mktemp -t $1
|
||||
fi
|
||||
}
|
||||
|
||||
|
@ -184,12 +186,14 @@ with open(sys.argv[1]) as f:
|
|||
lines.append(line.split(','))
|
||||
|
||||
results = []
|
||||
for line in reversed(lines[1:]):
|
||||
for line in reversed(lines[2:]):
|
||||
if len(line) < 8 or line[0] == "":
|
||||
continue
|
||||
v2 = float(line[1])
|
||||
results.append([
|
||||
line[0].replace("-32", ""),
|
||||
"%.1fx" % (float(line[3])/v2), # v1
|
||||
"%.1fx" % (float(line[5])/v2), # bs
|
||||
"%.1fx" % (float(line[7])/v2), # bs
|
||||
])
|
||||
# move geomean to the end
|
||||
results.append(results[0])
|
||||
|
@ -260,10 +264,10 @@ benchmark() {
|
|||
|
||||
if [ "$1" = "-html" ]; then
|
||||
tmpcsv=`fmktemp csv`
|
||||
benchstat -csv -geomean go-toml-v2.txt go-toml-v1.txt bs-toml.txt > $tmpcsv
|
||||
benchstat -format csv go-toml-v2.txt go-toml-v1.txt bs-toml.txt > $tmpcsv
|
||||
benchstathtml $tmpcsv
|
||||
else
|
||||
benchstat -geomean go-toml-v2.txt go-toml-v1.txt bs-toml.txt
|
||||
benchstat go-toml-v2.txt go-toml-v1.txt bs-toml.txt
|
||||
fi
|
||||
|
||||
rm -f go-toml-v2.txt go-toml-v1.txt bs-toml.txt
|
||||
|
|
|
@ -1097,9 +1097,9 @@ func (d *decoder) handleKeyValuePart(key unstable.Iterator, value *unstable.Node
|
|||
|
||||
f := fieldByIndex(v, path)
|
||||
|
||||
if !f.CanSet() {
|
||||
// If the field is not settable, need to take a slower path and make a copy of
|
||||
// the struct itself to a new location.
|
||||
if !f.CanAddr() {
|
||||
// If the field is not addressable, need to take a slower path and
|
||||
// make a copy of the struct itself to a new location.
|
||||
nvp := reflect.New(v.Type())
|
||||
nvp.Elem().Set(v)
|
||||
v = nvp.Elem()
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[{Makefile,*.mk}]
|
||||
indent_style = tab
|
||||
|
||||
[*.nix]
|
||||
indent_size = 2
|
||||
|
||||
[*.go]
|
||||
indent_style = tab
|
||||
|
||||
[{*.yml,*.yaml}]
|
||||
indent_size = 2
|
|
@ -0,0 +1,8 @@
|
|||
/.devenv/
|
||||
/.direnv/
|
||||
/.task/
|
||||
/bin/
|
||||
/build/
|
||||
/tmp/
|
||||
/var/
|
||||
/vendor/
|
|
@ -0,0 +1,27 @@
|
|||
run:
|
||||
timeout: 10m
|
||||
|
||||
linters-settings:
|
||||
gci:
|
||||
sections:
|
||||
- standard
|
||||
- default
|
||||
- prefix(github.com/sagikazarmark/locafero)
|
||||
goimports:
|
||||
local-prefixes: github.com/sagikazarmark/locafero
|
||||
misspell:
|
||||
locale: US
|
||||
nolintlint:
|
||||
allow-leading-space: false # require machine-readable nolint directives (with no leading space)
|
||||
allow-unused: false # report any unused nolint directives
|
||||
require-specific: false # don't require nolint directives to be specific about which linter is being skipped
|
||||
revive:
|
||||
confidence: 0
|
||||
|
||||
linters:
|
||||
enable:
|
||||
- gci
|
||||
- goimports
|
||||
- misspell
|
||||
- nolintlint
|
||||
- revive
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2023 Márk Sági-Kazár <mark.sagikazar@gmail.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is furnished
|
||||
to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
|
@ -0,0 +1,37 @@
|
|||
# Finder library for [Afero](https://github.com/spf13/afero)
|
||||
|
||||
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/sagikazarmark/locafero/ci.yaml?style=flat-square)](https://github.com/sagikazarmark/locafero/actions/workflows/ci.yaml)
|
||||
[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/sagikazarmark/locafero)
|
||||
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.20-61CFDD.svg?style=flat-square)
|
||||
[![built with nix](https://img.shields.io/badge/builtwith-nix-7d81f7?style=flat-square)](https://builtwithnix.org)
|
||||
|
||||
**Finder library for [Afero](https://github.com/spf13/afero) ported from [go-finder](https://github.com/sagikazarmark/go-finder).**
|
||||
|
||||
> [!WARNING]
|
||||
> This is an experimental library under development.
|
||||
>
|
||||
> **Backwards compatibility is not guaranteed, expect breaking changes.**
|
||||
|
||||
## Installation
|
||||
|
||||
```shell
|
||||
go get github.com/sagikazarmark/locafero
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Check out the [package example](https://pkg.go.dev/github.com/sagikazarmark/locafero#example-package) on go.dev.
|
||||
|
||||
## Development
|
||||
|
||||
**For an optimal developer experience, it is recommended to install [Nix](https://nixos.org/download.html) and [direnv](https://direnv.net/docs/installation.html).**
|
||||
|
||||
Run the test suite:
|
||||
|
||||
```shell
|
||||
just test
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
The project is licensed under the [MIT License](LICENSE).
|
|
@ -0,0 +1,28 @@
|
|||
package locafero
|
||||
|
||||
import "io/fs"
|
||||
|
||||
// FileType represents the kind of entries [Finder] can return.
|
||||
type FileType int
|
||||
|
||||
const (
|
||||
FileTypeAll FileType = iota
|
||||
FileTypeFile
|
||||
FileTypeDir
|
||||
)
|
||||
|
||||
func (ft FileType) matchFileInfo(info fs.FileInfo) bool {
|
||||
switch ft {
|
||||
case FileTypeAll:
|
||||
return true
|
||||
|
||||
case FileTypeFile:
|
||||
return !info.IsDir()
|
||||
|
||||
case FileTypeDir:
|
||||
return info.IsDir()
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
|
@ -0,0 +1,165 @@
|
|||
// Package finder looks for files and directories in an {fs.Fs} filesystem.
|
||||
package locafero
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/sourcegraph/conc/iter"
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
// Finder looks for files and directories in an [afero.Fs] filesystem.
|
||||
type Finder struct {
|
||||
// Paths represents a list of locations that the [Finder] will search in.
|
||||
//
|
||||
// They are essentially the root directories or starting points for the search.
|
||||
//
|
||||
// Examples:
|
||||
// - home/user
|
||||
// - etc
|
||||
Paths []string
|
||||
|
||||
// Names are specific entries that the [Finder] will look for within the given Paths.
|
||||
//
|
||||
// It provides the capability to search for entries with depth,
|
||||
// meaning it can target deeper locations within the directory structure.
|
||||
//
|
||||
// It also supports glob syntax (as defined by [filepat.Match]), offering greater flexibility in search patterns.
|
||||
//
|
||||
// Examples:
|
||||
// - config.yaml
|
||||
// - home/*/config.yaml
|
||||
// - home/*/config.*
|
||||
Names []string
|
||||
|
||||
// Type restricts the kind of entries returned by the [Finder].
|
||||
//
|
||||
// This parameter helps in differentiating and filtering out files from directories or vice versa.
|
||||
Type FileType
|
||||
}
|
||||
|
||||
// Find looks for files and directories in an [afero.Fs] filesystem.
|
||||
func (f Finder) Find(fsys afero.Fs) ([]string, error) {
|
||||
// Arbitrary go routine limit (TODO: make this a parameter)
|
||||
// pool := pool.NewWithResults[[]string]().WithMaxGoroutines(5).WithErrors().WithFirstError()
|
||||
|
||||
type searchItem struct {
|
||||
path string
|
||||
name string
|
||||
}
|
||||
|
||||
var searchItems []searchItem
|
||||
|
||||
for _, searchPath := range f.Paths {
|
||||
searchPath := searchPath
|
||||
|
||||
for _, searchName := range f.Names {
|
||||
searchName := searchName
|
||||
|
||||
searchItems = append(searchItems, searchItem{searchPath, searchName})
|
||||
|
||||
// pool.Go(func() ([]string, error) {
|
||||
// // If the name contains any glob character, perform a glob match
|
||||
// if strings.ContainsAny(searchName, "*?[]\\^") {
|
||||
// return globWalkSearch(fsys, searchPath, searchName, f.Type)
|
||||
// }
|
||||
//
|
||||
// return statSearch(fsys, searchPath, searchName, f.Type)
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
// allResults, err := pool.Wait()
|
||||
// if err != nil {
|
||||
// return nil, err
|
||||
// }
|
||||
|
||||
allResults, err := iter.MapErr(searchItems, func(item *searchItem) ([]string, error) {
|
||||
// If the name contains any glob character, perform a glob match
|
||||
if strings.ContainsAny(item.name, "*?[]\\^") {
|
||||
return globWalkSearch(fsys, item.path, item.name, f.Type)
|
||||
}
|
||||
|
||||
return statSearch(fsys, item.path, item.name, f.Type)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var results []string
|
||||
|
||||
for _, r := range allResults {
|
||||
results = append(results, r...)
|
||||
}
|
||||
|
||||
// Sort results in alphabetical order for now
|
||||
// sort.Strings(results)
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func globWalkSearch(fsys afero.Fs, searchPath string, searchName string, searchType FileType) ([]string, error) {
|
||||
var results []string
|
||||
|
||||
err := afero.Walk(fsys, searchPath, func(p string, fileInfo fs.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Skip the root path
|
||||
if p == searchPath {
|
||||
return nil
|
||||
}
|
||||
|
||||
var result error
|
||||
|
||||
// Stop reading subdirectories
|
||||
// TODO: add depth detection here
|
||||
if fileInfo.IsDir() && filepath.Dir(p) == searchPath {
|
||||
result = fs.SkipDir
|
||||
}
|
||||
|
||||
// Skip unmatching type
|
||||
if !searchType.matchFileInfo(fileInfo) {
|
||||
return result
|
||||
}
|
||||
|
||||
match, err := filepath.Match(searchName, fileInfo.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if match {
|
||||
results = append(results, p)
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
if err != nil {
|
||||
return results, err
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
func statSearch(fsys afero.Fs, searchPath string, searchName string, searchType FileType) ([]string, error) {
|
||||
filePath := filepath.Join(searchPath, searchName)
|
||||
|
||||
fileInfo, err := fsys.Stat(filePath)
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return nil, nil
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Skip unmatching type
|
||||
if !searchType.matchFileInfo(fileInfo) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
return []string{filePath}, nil
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
{
|
||||
"nodes": {
|
||||
"devenv": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"nix": "nix",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1694097209,
|
||||
"narHash": "sha256-gQmBjjxeSyySjbh0yQVBKApo2KWIFqqbRUvG+Fa+QpM=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "7a8e6a91510efe89d8dcb8e43233f93e86f6b189",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1673956053,
|
||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1693611461,
|
||||
"narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1685518550,
|
||||
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1660459072,
|
||||
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"lowdown-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1633514407,
|
||||
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"lowdown-src": "lowdown-src",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1676545802,
|
||||
"narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=",
|
||||
"owner": "domenkozar",
|
||||
"repo": "nix",
|
||||
"rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "domenkozar",
|
||||
"ref": "relaxed-flakes",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1678875422,
|
||||
"narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1693471703,
|
||||
"narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"dir": "lib",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1685801374,
|
||||
"narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c37ca420157f4abc31e26f436c1145f8951ff373",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1694343207,
|
||||
"narHash": "sha256-jWi7OwFxU5Owi4k2JmiL1sa/OuBCQtpaAesuj5LXC8w=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "78058d810644f5ed276804ce7ea9e82d92bee293",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"flake-utils": "flake-utils",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1688056373,
|
||||
"narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
{
|
||||
description = "Finder library for Afero";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
devenv.url = "github:cachix/devenv";
|
||||
};
|
||||
|
||||
outputs = inputs@{ flake-parts, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
imports = [
|
||||
inputs.devenv.flakeModule
|
||||
];
|
||||
|
||||
systems = [ "x86_64-linux" "aarch64-darwin" ];
|
||||
|
||||
perSystem = { config, self', inputs', pkgs, system, ... }: rec {
|
||||
devenv.shells = {
|
||||
default = {
|
||||
languages = {
|
||||
go.enable = true;
|
||||
};
|
||||
|
||||
packages = with pkgs; [
|
||||
just
|
||||
|
||||
golangci-lint
|
||||
];
|
||||
|
||||
# https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
|
||||
containers = pkgs.lib.mkForce { };
|
||||
};
|
||||
|
||||
ci = devenv.shells.default;
|
||||
|
||||
ci_1_20 = {
|
||||
imports = [ devenv.shells.ci ];
|
||||
|
||||
languages = {
|
||||
go.package = pkgs.go_1_20;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package locafero
|
||||
|
||||
import "fmt"
|
||||
|
||||
// NameWithExtensions creates a list of names from a base name and a list of extensions.
|
||||
//
|
||||
// TODO: find a better name for this function.
|
||||
func NameWithExtensions(baseName string, extensions ...string) []string {
|
||||
var names []string
|
||||
|
||||
if baseName == "" {
|
||||
return names
|
||||
}
|
||||
|
||||
for _, ext := range extensions {
|
||||
if ext == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
names = append(names, fmt.Sprintf("%s.%s", baseName, ext))
|
||||
}
|
||||
|
||||
return names
|
||||
}
|
||||
|
||||
// NameWithOptionalExtensions creates a list of names from a base name and a list of extensions,
|
||||
// plus it adds the base name (without any extensions) to the end of the list.
|
||||
//
|
||||
// TODO: find a better name for this function.
|
||||
func NameWithOptionalExtensions(baseName string, extensions ...string) []string {
|
||||
var names []string
|
||||
|
||||
if baseName == "" {
|
||||
return names
|
||||
}
|
||||
|
||||
names = NameWithExtensions(baseName, extensions...)
|
||||
names = append(names, baseName)
|
||||
|
||||
return names
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
default:
|
||||
just --list
|
||||
|
||||
test:
|
||||
go test -race -v ./...
|
||||
|
||||
lint:
|
||||
golangci-lint run
|
||||
|
||||
fmt:
|
||||
golangci-lint run --fix
|
|
@ -0,0 +1,18 @@
|
|||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
end_of_line = lf
|
||||
indent_size = 4
|
||||
indent_style = space
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.nix]
|
||||
indent_size = 2
|
||||
|
||||
[{Makefile,*.mk}]
|
||||
indent_style = tab
|
||||
|
||||
[Taskfile.yaml]
|
||||
indent_size = 2
|
|
@ -0,0 +1,4 @@
|
|||
/.devenv/
|
||||
/.direnv/
|
||||
/.task/
|
||||
/build/
|
|
@ -0,0 +1,27 @@
|
|||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
@ -0,0 +1,81 @@
|
|||
# [slog](https://pkg.go.dev/log/slog) shim
|
||||
|
||||
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/sagikazarmark/slog-shim/ci.yaml?style=flat-square)](https://github.com/sagikazarmark/slog-shim/actions/workflows/ci.yaml)
|
||||
[![go.dev reference](https://img.shields.io/badge/go.dev-reference-007d9c?logo=go&logoColor=white&style=flat-square)](https://pkg.go.dev/mod/github.com/sagikazarmark/slog-shim)
|
||||
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.20-61CFDD.svg?style=flat-square)
|
||||
[![built with nix](https://img.shields.io/badge/builtwith-nix-7d81f7?style=flat-square)](https://builtwithnix.org)
|
||||
|
||||
Go 1.21 introduced a [new structured logging package](https://golang.org/doc/go1.21#slog), `log/slog`, to the standard library.
|
||||
Although it's been eagerly anticipated by many, widespread adoption isn't expected to occur immediately,
|
||||
especially since updating to Go 1.21 is a decision that most libraries won't make overnight.
|
||||
|
||||
Before this package was added to the standard library, there was an _experimental_ version available at [golang.org/x/exp/slog](https://pkg.go.dev/golang.org/x/exp/slog).
|
||||
While it's generally advised against using experimental packages in production,
|
||||
this one served as a sort of backport package for the last few years,
|
||||
incorporating new features before they were added to the standard library (like `slices`, `maps` or `errors`).
|
||||
|
||||
This package serves as a bridge, helping libraries integrate slog in a backward-compatible way without having to immediately update their Go version requirement to 1.21. On Go 1.21 (and above), it acts as a drop-in replacement for `log/slog`, while below 1.21 it falls back to `golang.org/x/exp/slog`.
|
||||
|
||||
**How does it achieve backwards compatibility?**
|
||||
|
||||
Although there's no consensus on whether dropping support for older Go versions is considered backward compatible, a majority seems to believe it is.
|
||||
(I don't have scientific proof for this, but it's based on conversations with various individuals across different channels.)
|
||||
|
||||
This package adheres to that interpretation of backward compatibility. On Go 1.21, the shim uses type aliases to offer the same API as `slog/log`.
|
||||
Once a library upgrades its version requirement to Go 1.21, it should be able to discard this shim and use `log/slog` directly.
|
||||
|
||||
For older Go versions, the library might become unstable after removing the shim.
|
||||
However, since those older versions are no longer supported, the promise of backward compatibility remains intact.
|
||||
|
||||
## Installation
|
||||
|
||||
```shell
|
||||
go get github.com/sagikazarmark/slog-shim
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
Import this package into your library and use it in your public API:
|
||||
|
||||
```go
|
||||
package mylib
|
||||
|
||||
import slog "github.com/sagikazarmark/slog-shim"
|
||||
|
||||
func New(logger *slog.Logger) MyLib {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
When using the library, clients can either use `log/slog` (when on Go 1.21) or `golang.org/x/exp/slog` (below Go 1.21):
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import "log/slog"
|
||||
|
||||
// OR
|
||||
|
||||
import "golang.org/x/exp/slog"
|
||||
|
||||
mylib.New(slog.Default())
|
||||
```
|
||||
|
||||
**Make sure consumers are aware that your API behaves differently on different Go versions.**
|
||||
|
||||
Once you bump your Go version requirement to Go 1.21, you can drop the shim entirely from your code:
|
||||
|
||||
```diff
|
||||
package mylib
|
||||
|
||||
- import slog "github.com/sagikazarmark/slog-shim"
|
||||
+ import "log/slog"
|
||||
|
||||
func New(logger *slog.Logger) MyLib {
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
The project is licensed under a [BSD-style license](LICENSE).
|
|
@ -0,0 +1,74 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"time"
|
||||
)
|
||||
|
||||
// An Attr is a key-value pair.
|
||||
type Attr = slog.Attr
|
||||
|
||||
// String returns an Attr for a string value.
|
||||
func String(key, value string) Attr {
|
||||
return slog.String(key, value)
|
||||
}
|
||||
|
||||
// Int64 returns an Attr for an int64.
|
||||
func Int64(key string, value int64) Attr {
|
||||
return slog.Int64(key, value)
|
||||
}
|
||||
|
||||
// Int converts an int to an int64 and returns
|
||||
// an Attr with that value.
|
||||
func Int(key string, value int) Attr {
|
||||
return slog.Int(key, value)
|
||||
}
|
||||
|
||||
// Uint64 returns an Attr for a uint64.
|
||||
func Uint64(key string, v uint64) Attr {
|
||||
return slog.Uint64(key, v)
|
||||
}
|
||||
|
||||
// Float64 returns an Attr for a floating-point number.
|
||||
func Float64(key string, v float64) Attr {
|
||||
return slog.Float64(key, v)
|
||||
}
|
||||
|
||||
// Bool returns an Attr for a bool.
|
||||
func Bool(key string, v bool) Attr {
|
||||
return slog.Bool(key, v)
|
||||
}
|
||||
|
||||
// Time returns an Attr for a time.Time.
|
||||
// It discards the monotonic portion.
|
||||
func Time(key string, v time.Time) Attr {
|
||||
return slog.Time(key, v)
|
||||
}
|
||||
|
||||
// Duration returns an Attr for a time.Duration.
|
||||
func Duration(key string, v time.Duration) Attr {
|
||||
return slog.Duration(key, v)
|
||||
}
|
||||
|
||||
// Group returns an Attr for a Group Value.
|
||||
// The first argument is the key; the remaining arguments
|
||||
// are converted to Attrs as in [Logger.Log].
|
||||
//
|
||||
// Use Group to collect several key-value pairs under a single
|
||||
// key on a log line, or as the result of LogValue
|
||||
// in order to log a single value as multiple Attrs.
|
||||
func Group(key string, args ...any) Attr {
|
||||
return slog.Group(key, args...)
|
||||
}
|
||||
|
||||
// Any returns an Attr for the supplied value.
|
||||
// See [Value.AnyValue] for how values are treated.
|
||||
func Any(key string, value any) Attr {
|
||||
return slog.Any(key, value)
|
||||
}
|
|
@ -0,0 +1,75 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// An Attr is a key-value pair.
|
||||
type Attr = slog.Attr
|
||||
|
||||
// String returns an Attr for a string value.
|
||||
func String(key, value string) Attr {
|
||||
return slog.String(key, value)
|
||||
}
|
||||
|
||||
// Int64 returns an Attr for an int64.
|
||||
func Int64(key string, value int64) Attr {
|
||||
return slog.Int64(key, value)
|
||||
}
|
||||
|
||||
// Int converts an int to an int64 and returns
|
||||
// an Attr with that value.
|
||||
func Int(key string, value int) Attr {
|
||||
return slog.Int(key, value)
|
||||
}
|
||||
|
||||
// Uint64 returns an Attr for a uint64.
|
||||
func Uint64(key string, v uint64) Attr {
|
||||
return slog.Uint64(key, v)
|
||||
}
|
||||
|
||||
// Float64 returns an Attr for a floating-point number.
|
||||
func Float64(key string, v float64) Attr {
|
||||
return slog.Float64(key, v)
|
||||
}
|
||||
|
||||
// Bool returns an Attr for a bool.
|
||||
func Bool(key string, v bool) Attr {
|
||||
return slog.Bool(key, v)
|
||||
}
|
||||
|
||||
// Time returns an Attr for a time.Time.
|
||||
// It discards the monotonic portion.
|
||||
func Time(key string, v time.Time) Attr {
|
||||
return slog.Time(key, v)
|
||||
}
|
||||
|
||||
// Duration returns an Attr for a time.Duration.
|
||||
func Duration(key string, v time.Duration) Attr {
|
||||
return slog.Duration(key, v)
|
||||
}
|
||||
|
||||
// Group returns an Attr for a Group Value.
|
||||
// The first argument is the key; the remaining arguments
|
||||
// are converted to Attrs as in [Logger.Log].
|
||||
//
|
||||
// Use Group to collect several key-value pairs under a single
|
||||
// key on a log line, or as the result of LogValue
|
||||
// in order to log a single value as multiple Attrs.
|
||||
func Group(key string, args ...any) Attr {
|
||||
return slog.Group(key, args...)
|
||||
}
|
||||
|
||||
// Any returns an Attr for the supplied value.
|
||||
// See [Value.AnyValue] for how values are treated.
|
||||
func Any(key string, value any) Attr {
|
||||
return slog.Any(key, value)
|
||||
}
|
|
@ -0,0 +1,273 @@
|
|||
{
|
||||
"nodes": {
|
||||
"devenv": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"nix": "nix",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1694097209,
|
||||
"narHash": "sha256-gQmBjjxeSyySjbh0yQVBKApo2KWIFqqbRUvG+Fa+QpM=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "7a8e6a91510efe89d8dcb8e43233f93e86f6b189",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1673956053,
|
||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1693611461,
|
||||
"narHash": "sha256-aPODl8vAgGQ0ZYFIRisxYG5MOGSkIczvu2Cd8Gb9+1Y=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "7f53fdb7bdc5bb237da7fefef12d099e4fd611ca",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1685518550,
|
||||
"narHash": "sha256-o2d0KcvaXzTrPRIo0kOLV0/QXHhDQ5DTi+OxcjO8xqY=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "a1720a10a6cfe8234c0e93907ffe81be440f4cef",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1660459072,
|
||||
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"lowdown-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1633514407,
|
||||
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"lowdown-src": "lowdown-src",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1676545802,
|
||||
"narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=",
|
||||
"owner": "domenkozar",
|
||||
"repo": "nix",
|
||||
"rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "domenkozar",
|
||||
"ref": "relaxed-flakes",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1678875422,
|
||||
"narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1693471703,
|
||||
"narHash": "sha256-0l03ZBL8P1P6z8MaSDS/MvuU8E75rVxe5eE1N6gxeTo=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "3e52e76b70d5508f3cec70b882a29199f4d1ee85",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"dir": "lib",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1685801374,
|
||||
"narHash": "sha256-otaSUoFEMM+LjBI1XL/xGB5ao6IwnZOXc47qhIgJe8U=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "c37ca420157f4abc31e26f436c1145f8951ff373",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-23.05",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1694345580,
|
||||
"narHash": "sha256-BbG0NUxQTz1dN/Y87yPWZc/0Kp/coJ0vM3+7sNa5kUM=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "f002de6834fdde9c864f33c1ec51da7df19cd832",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "master",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"flake-utils": "flake-utils",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1688056373,
|
||||
"narHash": "sha256-2+SDlNRTKsgo3LBRiMUcoEUb6sDViRNQhzJquZ4koOI=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "5843cf069272d92b60c3ed9e55b7a8989c01d4c7",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
{
|
||||
inputs = {
|
||||
# nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/master";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
devenv.url = "github:cachix/devenv";
|
||||
};
|
||||
|
||||
outputs = inputs@{ flake-parts, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
imports = [
|
||||
inputs.devenv.flakeModule
|
||||
];
|
||||
|
||||
systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ];
|
||||
|
||||
perSystem = { config, self', inputs', pkgs, system, ... }: rec {
|
||||
devenv.shells = {
|
||||
default = {
|
||||
languages = {
|
||||
go.enable = true;
|
||||
go.package = pkgs.lib.mkDefault pkgs.go_1_21;
|
||||
};
|
||||
|
||||
# https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
|
||||
containers = pkgs.lib.mkForce { };
|
||||
};
|
||||
|
||||
ci = devenv.shells.default;
|
||||
|
||||
ci_1_19 = {
|
||||
imports = [ devenv.shells.ci ];
|
||||
|
||||
languages = {
|
||||
go.package = pkgs.go_1_19;
|
||||
};
|
||||
};
|
||||
|
||||
ci_1_20 = {
|
||||
imports = [ devenv.shells.ci ];
|
||||
|
||||
languages = {
|
||||
go.package = pkgs.go_1_20;
|
||||
};
|
||||
};
|
||||
|
||||
ci_1_21 = {
|
||||
imports = [ devenv.shells.ci ];
|
||||
|
||||
languages = {
|
||||
go.package = pkgs.go_1_21;
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// A Handler handles log records produced by a Logger..
|
||||
//
|
||||
// A typical handler may print log records to standard error,
|
||||
// or write them to a file or database, or perhaps augment them
|
||||
// with additional attributes and pass them on to another handler.
|
||||
//
|
||||
// Any of the Handler's methods may be called concurrently with itself
|
||||
// or with other methods. It is the responsibility of the Handler to
|
||||
// manage this concurrency.
|
||||
//
|
||||
// Users of the slog package should not invoke Handler methods directly.
|
||||
// They should use the methods of [Logger] instead.
|
||||
type Handler = slog.Handler
|
||||
|
||||
// HandlerOptions are options for a TextHandler or JSONHandler.
|
||||
// A zero HandlerOptions consists entirely of default values.
|
||||
type HandlerOptions = slog.HandlerOptions
|
||||
|
||||
// Keys for "built-in" attributes.
|
||||
const (
|
||||
// TimeKey is the key used by the built-in handlers for the time
|
||||
// when the log method is called. The associated Value is a [time.Time].
|
||||
TimeKey = slog.TimeKey
|
||||
// LevelKey is the key used by the built-in handlers for the level
|
||||
// of the log call. The associated value is a [Level].
|
||||
LevelKey = slog.LevelKey
|
||||
// MessageKey is the key used by the built-in handlers for the
|
||||
// message of the log call. The associated value is a string.
|
||||
MessageKey = slog.MessageKey
|
||||
// SourceKey is the key used by the built-in handlers for the source file
|
||||
// and line of the log call. The associated value is a string.
|
||||
SourceKey = slog.SourceKey
|
||||
)
|
|
@ -0,0 +1,45 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// A Handler handles log records produced by a Logger..
|
||||
//
|
||||
// A typical handler may print log records to standard error,
|
||||
// or write them to a file or database, or perhaps augment them
|
||||
// with additional attributes and pass them on to another handler.
|
||||
//
|
||||
// Any of the Handler's methods may be called concurrently with itself
|
||||
// or with other methods. It is the responsibility of the Handler to
|
||||
// manage this concurrency.
|
||||
//
|
||||
// Users of the slog package should not invoke Handler methods directly.
|
||||
// They should use the methods of [Logger] instead.
|
||||
type Handler = slog.Handler
|
||||
|
||||
// HandlerOptions are options for a TextHandler or JSONHandler.
|
||||
// A zero HandlerOptions consists entirely of default values.
|
||||
type HandlerOptions = slog.HandlerOptions
|
||||
|
||||
// Keys for "built-in" attributes.
|
||||
const (
|
||||
// TimeKey is the key used by the built-in handlers for the time
|
||||
// when the log method is called. The associated Value is a [time.Time].
|
||||
TimeKey = slog.TimeKey
|
||||
// LevelKey is the key used by the built-in handlers for the level
|
||||
// of the log call. The associated value is a [Level].
|
||||
LevelKey = slog.LevelKey
|
||||
// MessageKey is the key used by the built-in handlers for the
|
||||
// message of the log call. The associated value is a string.
|
||||
MessageKey = slog.MessageKey
|
||||
// SourceKey is the key used by the built-in handlers for the source file
|
||||
// and line of the log call. The associated value is a string.
|
||||
SourceKey = slog.SourceKey
|
||||
)
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// JSONHandler is a Handler that writes Records to an io.Writer as
|
||||
// line-delimited JSON objects.
|
||||
type JSONHandler = slog.JSONHandler
|
||||
|
||||
// NewJSONHandler creates a JSONHandler that writes to w,
|
||||
// using the given options.
|
||||
// If opts is nil, the default options are used.
|
||||
func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler {
|
||||
return slog.NewJSONHandler(w, opts)
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// JSONHandler is a Handler that writes Records to an io.Writer as
|
||||
// line-delimited JSON objects.
|
||||
type JSONHandler = slog.JSONHandler
|
||||
|
||||
// NewJSONHandler creates a JSONHandler that writes to w,
|
||||
// using the given options.
|
||||
// If opts is nil, the default options are used.
|
||||
func NewJSONHandler(w io.Writer, opts *HandlerOptions) *JSONHandler {
|
||||
return slog.NewJSONHandler(w, opts)
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// A Level is the importance or severity of a log event.
|
||||
// The higher the level, the more important or severe the event.
|
||||
type Level = slog.Level
|
||||
|
||||
// Level numbers are inherently arbitrary,
|
||||
// but we picked them to satisfy three constraints.
|
||||
// Any system can map them to another numbering scheme if it wishes.
|
||||
//
|
||||
// First, we wanted the default level to be Info, Since Levels are ints, Info is
|
||||
// the default value for int, zero.
|
||||
//
|
||||
// Second, we wanted to make it easy to use levels to specify logger verbosity.
|
||||
// Since a larger level means a more severe event, a logger that accepts events
|
||||
// with smaller (or more negative) level means a more verbose logger. Logger
|
||||
// verbosity is thus the negation of event severity, and the default verbosity
|
||||
// of 0 accepts all events at least as severe as INFO.
|
||||
//
|
||||
// Third, we wanted some room between levels to accommodate schemes with named
|
||||
// levels between ours. For example, Google Cloud Logging defines a Notice level
|
||||
// between Info and Warn. Since there are only a few of these intermediate
|
||||
// levels, the gap between the numbers need not be large. Our gap of 4 matches
|
||||
// OpenTelemetry's mapping. Subtracting 9 from an OpenTelemetry level in the
|
||||
// DEBUG, INFO, WARN and ERROR ranges converts it to the corresponding slog
|
||||
// Level range. OpenTelemetry also has the names TRACE and FATAL, which slog
|
||||
// does not. But those OpenTelemetry levels can still be represented as slog
|
||||
// Levels by using the appropriate integers.
|
||||
//
|
||||
// Names for common levels.
|
||||
const (
|
||||
LevelDebug Level = slog.LevelDebug
|
||||
LevelInfo Level = slog.LevelInfo
|
||||
LevelWarn Level = slog.LevelWarn
|
||||
LevelError Level = slog.LevelError
|
||||
)
|
||||
|
||||
// A LevelVar is a Level variable, to allow a Handler level to change
|
||||
// dynamically.
|
||||
// It implements Leveler as well as a Set method,
|
||||
// and it is safe for use by multiple goroutines.
|
||||
// The zero LevelVar corresponds to LevelInfo.
|
||||
type LevelVar = slog.LevelVar
|
||||
|
||||
// A Leveler provides a Level value.
|
||||
//
|
||||
// As Level itself implements Leveler, clients typically supply
|
||||
// a Level value wherever a Leveler is needed, such as in HandlerOptions.
|
||||
// Clients who need to vary the level dynamically can provide a more complex
|
||||
// Leveler implementation such as *LevelVar.
|
||||
type Leveler = slog.Leveler
|
|
@ -0,0 +1,61 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// A Level is the importance or severity of a log event.
|
||||
// The higher the level, the more important or severe the event.
|
||||
type Level = slog.Level
|
||||
|
||||
// Level numbers are inherently arbitrary,
|
||||
// but we picked them to satisfy three constraints.
|
||||
// Any system can map them to another numbering scheme if it wishes.
|
||||
//
|
||||
// First, we wanted the default level to be Info, Since Levels are ints, Info is
|
||||
// the default value for int, zero.
|
||||
//
|
||||
// Second, we wanted to make it easy to use levels to specify logger verbosity.
|
||||
// Since a larger level means a more severe event, a logger that accepts events
|
||||
// with smaller (or more negative) level means a more verbose logger. Logger
|
||||
// verbosity is thus the negation of event severity, and the default verbosity
|
||||
// of 0 accepts all events at least as severe as INFO.
|
||||
//
|
||||
// Third, we wanted some room between levels to accommodate schemes with named
|
||||
// levels between ours. For example, Google Cloud Logging defines a Notice level
|
||||
// between Info and Warn. Since there are only a few of these intermediate
|
||||
// levels, the gap between the numbers need not be large. Our gap of 4 matches
|
||||
// OpenTelemetry's mapping. Subtracting 9 from an OpenTelemetry level in the
|
||||
// DEBUG, INFO, WARN and ERROR ranges converts it to the corresponding slog
|
||||
// Level range. OpenTelemetry also has the names TRACE and FATAL, which slog
|
||||
// does not. But those OpenTelemetry levels can still be represented as slog
|
||||
// Levels by using the appropriate integers.
|
||||
//
|
||||
// Names for common levels.
|
||||
const (
|
||||
LevelDebug Level = slog.LevelDebug
|
||||
LevelInfo Level = slog.LevelInfo
|
||||
LevelWarn Level = slog.LevelWarn
|
||||
LevelError Level = slog.LevelError
|
||||
)
|
||||
|
||||
// A LevelVar is a Level variable, to allow a Handler level to change
|
||||
// dynamically.
|
||||
// It implements Leveler as well as a Set method,
|
||||
// and it is safe for use by multiple goroutines.
|
||||
// The zero LevelVar corresponds to LevelInfo.
|
||||
type LevelVar = slog.LevelVar
|
||||
|
||||
// A Leveler provides a Level value.
|
||||
//
|
||||
// As Level itself implements Leveler, clients typically supply
|
||||
// a Level value wherever a Leveler is needed, such as in HandlerOptions.
|
||||
// Clients who need to vary the level dynamically can provide a more complex
|
||||
// Leveler implementation such as *LevelVar.
|
||||
type Leveler = slog.Leveler
|
|
@ -0,0 +1,98 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// Default returns the default Logger.
|
||||
func Default() *Logger { return slog.Default() }
|
||||
|
||||
// SetDefault makes l the default Logger.
|
||||
// After this call, output from the log package's default Logger
|
||||
// (as with [log.Print], etc.) will be logged at LevelInfo using l's Handler.
|
||||
func SetDefault(l *Logger) {
|
||||
slog.SetDefault(l)
|
||||
}
|
||||
|
||||
// A Logger records structured information about each call to its
|
||||
// Log, Debug, Info, Warn, and Error methods.
|
||||
// For each call, it creates a Record and passes it to a Handler.
|
||||
//
|
||||
// To create a new Logger, call [New] or a Logger method
|
||||
// that begins "With".
|
||||
type Logger = slog.Logger
|
||||
|
||||
// New creates a new Logger with the given non-nil Handler.
|
||||
func New(h Handler) *Logger {
|
||||
return slog.New(h)
|
||||
}
|
||||
|
||||
// With calls Logger.With on the default logger.
|
||||
func With(args ...any) *Logger {
|
||||
return slog.With(args...)
|
||||
}
|
||||
|
||||
// NewLogLogger returns a new log.Logger such that each call to its Output method
|
||||
// dispatches a Record to the specified handler. The logger acts as a bridge from
|
||||
// the older log API to newer structured logging handlers.
|
||||
func NewLogLogger(h Handler, level Level) *log.Logger {
|
||||
return slog.NewLogLogger(h, level)
|
||||
}
|
||||
|
||||
// Debug calls Logger.Debug on the default logger.
|
||||
func Debug(msg string, args ...any) {
|
||||
slog.Debug(msg, args...)
|
||||
}
|
||||
|
||||
// DebugContext calls Logger.DebugContext on the default logger.
|
||||
func DebugContext(ctx context.Context, msg string, args ...any) {
|
||||
slog.DebugContext(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Info calls Logger.Info on the default logger.
|
||||
func Info(msg string, args ...any) {
|
||||
slog.Info(msg, args...)
|
||||
}
|
||||
|
||||
// InfoContext calls Logger.InfoContext on the default logger.
|
||||
func InfoContext(ctx context.Context, msg string, args ...any) {
|
||||
slog.InfoContext(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Warn calls Logger.Warn on the default logger.
|
||||
func Warn(msg string, args ...any) {
|
||||
slog.Warn(msg, args...)
|
||||
}
|
||||
|
||||
// WarnContext calls Logger.WarnContext on the default logger.
|
||||
func WarnContext(ctx context.Context, msg string, args ...any) {
|
||||
slog.WarnContext(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Error calls Logger.Error on the default logger.
|
||||
func Error(msg string, args ...any) {
|
||||
slog.Error(msg, args...)
|
||||
}
|
||||
|
||||
// ErrorContext calls Logger.ErrorContext on the default logger.
|
||||
func ErrorContext(ctx context.Context, msg string, args ...any) {
|
||||
slog.ErrorContext(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Log calls Logger.Log on the default logger.
|
||||
func Log(ctx context.Context, level Level, msg string, args ...any) {
|
||||
slog.Log(ctx, level, msg, args...)
|
||||
}
|
||||
|
||||
// LogAttrs calls Logger.LogAttrs on the default logger.
|
||||
func LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) {
|
||||
slog.LogAttrs(ctx, level, msg, attrs...)
|
||||
}
|
|
@ -0,0 +1,99 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// Default returns the default Logger.
|
||||
func Default() *Logger { return slog.Default() }
|
||||
|
||||
// SetDefault makes l the default Logger.
|
||||
// After this call, output from the log package's default Logger
|
||||
// (as with [log.Print], etc.) will be logged at LevelInfo using l's Handler.
|
||||
func SetDefault(l *Logger) {
|
||||
slog.SetDefault(l)
|
||||
}
|
||||
|
||||
// A Logger records structured information about each call to its
|
||||
// Log, Debug, Info, Warn, and Error methods.
|
||||
// For each call, it creates a Record and passes it to a Handler.
|
||||
//
|
||||
// To create a new Logger, call [New] or a Logger method
|
||||
// that begins "With".
|
||||
type Logger = slog.Logger
|
||||
|
||||
// New creates a new Logger with the given non-nil Handler.
|
||||
func New(h Handler) *Logger {
|
||||
return slog.New(h)
|
||||
}
|
||||
|
||||
// With calls Logger.With on the default logger.
|
||||
func With(args ...any) *Logger {
|
||||
return slog.With(args...)
|
||||
}
|
||||
|
||||
// NewLogLogger returns a new log.Logger such that each call to its Output method
|
||||
// dispatches a Record to the specified handler. The logger acts as a bridge from
|
||||
// the older log API to newer structured logging handlers.
|
||||
func NewLogLogger(h Handler, level Level) *log.Logger {
|
||||
return slog.NewLogLogger(h, level)
|
||||
}
|
||||
|
||||
// Debug calls Logger.Debug on the default logger.
|
||||
func Debug(msg string, args ...any) {
|
||||
slog.Debug(msg, args...)
|
||||
}
|
||||
|
||||
// DebugContext calls Logger.DebugContext on the default logger.
|
||||
func DebugContext(ctx context.Context, msg string, args ...any) {
|
||||
slog.DebugContext(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Info calls Logger.Info on the default logger.
|
||||
func Info(msg string, args ...any) {
|
||||
slog.Info(msg, args...)
|
||||
}
|
||||
|
||||
// InfoContext calls Logger.InfoContext on the default logger.
|
||||
func InfoContext(ctx context.Context, msg string, args ...any) {
|
||||
slog.InfoContext(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Warn calls Logger.Warn on the default logger.
|
||||
func Warn(msg string, args ...any) {
|
||||
slog.Warn(msg, args...)
|
||||
}
|
||||
|
||||
// WarnContext calls Logger.WarnContext on the default logger.
|
||||
func WarnContext(ctx context.Context, msg string, args ...any) {
|
||||
slog.WarnContext(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Error calls Logger.Error on the default logger.
|
||||
func Error(msg string, args ...any) {
|
||||
slog.Error(msg, args...)
|
||||
}
|
||||
|
||||
// ErrorContext calls Logger.ErrorContext on the default logger.
|
||||
func ErrorContext(ctx context.Context, msg string, args ...any) {
|
||||
slog.ErrorContext(ctx, msg, args...)
|
||||
}
|
||||
|
||||
// Log calls Logger.Log on the default logger.
|
||||
func Log(ctx context.Context, level Level, msg string, args ...any) {
|
||||
slog.Log(ctx, level, msg, args...)
|
||||
}
|
||||
|
||||
// LogAttrs calls Logger.LogAttrs on the default logger.
|
||||
func LogAttrs(ctx context.Context, level Level, msg string, attrs ...Attr) {
|
||||
slog.LogAttrs(ctx, level, msg, attrs...)
|
||||
}
|
|
@ -0,0 +1,31 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Record holds information about a log event.
|
||||
// Copies of a Record share state.
|
||||
// Do not modify a Record after handing out a copy to it.
|
||||
// Call [NewRecord] to create a new Record.
|
||||
// Use [Record.Clone] to create a copy with no shared state.
|
||||
type Record = slog.Record
|
||||
|
||||
// NewRecord creates a Record from the given arguments.
|
||||
// Use [Record.AddAttrs] to add attributes to the Record.
|
||||
//
|
||||
// NewRecord is intended for logging APIs that want to support a [Handler] as
|
||||
// a backend.
|
||||
func NewRecord(t time.Time, level Level, msg string, pc uintptr) Record {
|
||||
return slog.NewRecord(t, level, msg, pc)
|
||||
}
|
||||
|
||||
// Source describes the location of a line of source code.
|
||||
type Source = slog.Source
|
|
@ -0,0 +1,32 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// A Record holds information about a log event.
|
||||
// Copies of a Record share state.
|
||||
// Do not modify a Record after handing out a copy to it.
|
||||
// Call [NewRecord] to create a new Record.
|
||||
// Use [Record.Clone] to create a copy with no shared state.
|
||||
type Record = slog.Record
|
||||
|
||||
// NewRecord creates a Record from the given arguments.
|
||||
// Use [Record.AddAttrs] to add attributes to the Record.
|
||||
//
|
||||
// NewRecord is intended for logging APIs that want to support a [Handler] as
|
||||
// a backend.
|
||||
func NewRecord(t time.Time, level Level, msg string, pc uintptr) Record {
|
||||
return slog.NewRecord(t, level, msg, pc)
|
||||
}
|
||||
|
||||
// Source describes the location of a line of source code.
|
||||
type Source = slog.Source
|
|
@ -0,0 +1,23 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"io"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
// TextHandler is a Handler that writes Records to an io.Writer as a
|
||||
// sequence of key=value pairs separated by spaces and followed by a newline.
|
||||
type TextHandler = slog.TextHandler
|
||||
|
||||
// NewTextHandler creates a TextHandler that writes to w,
|
||||
// using the given options.
|
||||
// If opts is nil, the default options are used.
|
||||
func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler {
|
||||
return slog.NewTextHandler(w, opts)
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// TextHandler is a Handler that writes Records to an io.Writer as a
|
||||
// sequence of key=value pairs separated by spaces and followed by a newline.
|
||||
type TextHandler = slog.TextHandler
|
||||
|
||||
// NewTextHandler creates a TextHandler that writes to w,
|
||||
// using the given options.
|
||||
// If opts is nil, the default options are used.
|
||||
func NewTextHandler(w io.Writer, opts *HandlerOptions) *TextHandler {
|
||||
return slog.NewTextHandler(w, opts)
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"time"
|
||||
)
|
||||
|
||||
// A Value can represent any Go value, but unlike type any,
|
||||
// it can represent most small values without an allocation.
|
||||
// The zero Value corresponds to nil.
|
||||
type Value = slog.Value
|
||||
|
||||
// Kind is the kind of a Value.
|
||||
type Kind = slog.Kind
|
||||
|
||||
// The following list is sorted alphabetically, but it's also important that
|
||||
// KindAny is 0 so that a zero Value represents nil.
|
||||
const (
|
||||
KindAny = slog.KindAny
|
||||
KindBool = slog.KindBool
|
||||
KindDuration = slog.KindDuration
|
||||
KindFloat64 = slog.KindFloat64
|
||||
KindInt64 = slog.KindInt64
|
||||
KindString = slog.KindString
|
||||
KindTime = slog.KindTime
|
||||
KindUint64 = slog.KindUint64
|
||||
KindGroup = slog.KindGroup
|
||||
KindLogValuer = slog.KindLogValuer
|
||||
)
|
||||
|
||||
//////////////// Constructors
|
||||
|
||||
// StringValue returns a new Value for a string.
|
||||
func StringValue(value string) Value {
|
||||
return slog.StringValue(value)
|
||||
}
|
||||
|
||||
// IntValue returns a Value for an int.
|
||||
func IntValue(v int) Value {
|
||||
return slog.IntValue(v)
|
||||
}
|
||||
|
||||
// Int64Value returns a Value for an int64.
|
||||
func Int64Value(v int64) Value {
|
||||
return slog.Int64Value(v)
|
||||
}
|
||||
|
||||
// Uint64Value returns a Value for a uint64.
|
||||
func Uint64Value(v uint64) Value {
|
||||
return slog.Uint64Value(v)
|
||||
}
|
||||
|
||||
// Float64Value returns a Value for a floating-point number.
|
||||
func Float64Value(v float64) Value {
|
||||
return slog.Float64Value(v)
|
||||
}
|
||||
|
||||
// BoolValue returns a Value for a bool.
|
||||
func BoolValue(v bool) Value {
|
||||
return slog.BoolValue(v)
|
||||
}
|
||||
|
||||
// TimeValue returns a Value for a time.Time.
|
||||
// It discards the monotonic portion.
|
||||
func TimeValue(v time.Time) Value {
|
||||
return slog.TimeValue(v)
|
||||
}
|
||||
|
||||
// DurationValue returns a Value for a time.Duration.
|
||||
func DurationValue(v time.Duration) Value {
|
||||
return slog.DurationValue(v)
|
||||
}
|
||||
|
||||
// GroupValue returns a new Value for a list of Attrs.
|
||||
// The caller must not subsequently mutate the argument slice.
|
||||
func GroupValue(as ...Attr) Value {
|
||||
return slog.GroupValue(as...)
|
||||
}
|
||||
|
||||
// AnyValue returns a Value for the supplied value.
|
||||
//
|
||||
// If the supplied value is of type Value, it is returned
|
||||
// unmodified.
|
||||
//
|
||||
// Given a value of one of Go's predeclared string, bool, or
|
||||
// (non-complex) numeric types, AnyValue returns a Value of kind
|
||||
// String, Bool, Uint64, Int64, or Float64. The width of the
|
||||
// original numeric type is not preserved.
|
||||
//
|
||||
// Given a time.Time or time.Duration value, AnyValue returns a Value of kind
|
||||
// KindTime or KindDuration. The monotonic time is not preserved.
|
||||
//
|
||||
// For nil, or values of all other types, including named types whose
|
||||
// underlying type is numeric, AnyValue returns a value of kind KindAny.
|
||||
func AnyValue(v any) Value {
|
||||
return slog.AnyValue(v)
|
||||
}
|
||||
|
||||
// A LogValuer is any Go value that can convert itself into a Value for logging.
|
||||
//
|
||||
// This mechanism may be used to defer expensive operations until they are
|
||||
// needed, or to expand a single value into a sequence of components.
|
||||
type LogValuer = slog.LogValuer
|
|
@ -0,0 +1,110 @@
|
|||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
//go:build !go1.21
|
||||
|
||||
package slog
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"golang.org/x/exp/slog"
|
||||
)
|
||||
|
||||
// A Value can represent any Go value, but unlike type any,
|
||||
// it can represent most small values without an allocation.
|
||||
// The zero Value corresponds to nil.
|
||||
type Value = slog.Value
|
||||
|
||||
// Kind is the kind of a Value.
|
||||
type Kind = slog.Kind
|
||||
|
||||
// The following list is sorted alphabetically, but it's also important that
|
||||
// KindAny is 0 so that a zero Value represents nil.
|
||||
const (
|
||||
KindAny = slog.KindAny
|
||||
KindBool = slog.KindBool
|
||||
KindDuration = slog.KindDuration
|
||||
KindFloat64 = slog.KindFloat64
|
||||
KindInt64 = slog.KindInt64
|
||||
KindString = slog.KindString
|
||||
KindTime = slog.KindTime
|
||||
KindUint64 = slog.KindUint64
|
||||
KindGroup = slog.KindGroup
|
||||
KindLogValuer = slog.KindLogValuer
|
||||
)
|
||||
|
||||
//////////////// Constructors
|
||||
|
||||
// StringValue returns a new Value for a string.
|
||||
func StringValue(value string) Value {
|
||||
return slog.StringValue(value)
|
||||
}
|
||||
|
||||
// IntValue returns a Value for an int.
|
||||
func IntValue(v int) Value {
|
||||
return slog.IntValue(v)
|
||||
}
|
||||
|
||||
// Int64Value returns a Value for an int64.
|
||||
func Int64Value(v int64) Value {
|
||||
return slog.Int64Value(v)
|
||||
}
|
||||
|
||||
// Uint64Value returns a Value for a uint64.
|
||||
func Uint64Value(v uint64) Value {
|
||||
return slog.Uint64Value(v)
|
||||
}
|
||||
|
||||
// Float64Value returns a Value for a floating-point number.
|
||||
func Float64Value(v float64) Value {
|
||||
return slog.Float64Value(v)
|
||||
}
|
||||
|
||||
// BoolValue returns a Value for a bool.
|
||||
func BoolValue(v bool) Value {
|
||||
return slog.BoolValue(v)
|
||||
}
|
||||
|
||||
// TimeValue returns a Value for a time.Time.
|
||||
// It discards the monotonic portion.
|
||||
func TimeValue(v time.Time) Value {
|
||||
return slog.TimeValue(v)
|
||||
}
|
||||
|
||||
// DurationValue returns a Value for a time.Duration.
|
||||
func DurationValue(v time.Duration) Value {
|
||||
return slog.DurationValue(v)
|
||||
}
|
||||
|
||||
// GroupValue returns a new Value for a list of Attrs.
|
||||
// The caller must not subsequently mutate the argument slice.
|
||||
func GroupValue(as ...Attr) Value {
|
||||
return slog.GroupValue(as...)
|
||||
}
|
||||
|
||||
// AnyValue returns a Value for the supplied value.
|
||||
//
|
||||
// If the supplied value is of type Value, it is returned
|
||||
// unmodified.
|
||||
//
|
||||
// Given a value of one of Go's predeclared string, bool, or
|
||||
// (non-complex) numeric types, AnyValue returns a Value of kind
|
||||
// String, Bool, Uint64, Int64, or Float64. The width of the
|
||||
// original numeric type is not preserved.
|
||||
//
|
||||
// Given a time.Time or time.Duration value, AnyValue returns a Value of kind
|
||||
// KindTime or KindDuration. The monotonic time is not preserved.
|
||||
//
|
||||
// For nil, or values of all other types, including named types whose
|
||||
// underlying type is numeric, AnyValue returns a value of kind KindAny.
|
||||
func AnyValue(v any) Value {
|
||||
return slog.AnyValue(v)
|
||||
}
|
||||
|
||||
// A LogValuer is any Go value that can convert itself into a Value for logging.
|
||||
//
|
||||
// This mechanism may be used to defer expensive operations until they are
|
||||
// needed, or to expand a single value into a sequence of components.
|
||||
type LogValuer = slog.LogValuer
|
|
@ -0,0 +1,11 @@
|
|||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- errcheck
|
||||
- godot
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- staticcheck
|
||||
- typecheck
|
||||
- unused
|
|
@ -1,6 +1,6 @@
|
|||
The MIT License (MIT)
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2014 Steve Francia
|
||||
Copyright (c) 2023 Sourcegraph
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
@ -18,4 +18,4 @@ 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.
|
||||
SOFTWARE.
|
|
@ -0,0 +1,464 @@
|
|||
![conch](https://user-images.githubusercontent.com/12631702/210295964-785cc63d-d697-420c-99ff-f492eb81dec9.svg)
|
||||
|
||||
# `conc`: better structured concurrency for go
|
||||
|
||||
[![Go Reference](https://pkg.go.dev/badge/github.com/sourcegraph/conc.svg)](https://pkg.go.dev/github.com/sourcegraph/conc)
|
||||
[![Sourcegraph](https://img.shields.io/badge/view%20on-sourcegraph-A112FE?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAEZklEQVRoQ+2aXWgUZxSG3292sxtNN43BhBakFPyhxSujRSxiU1pr7SaGXqgUxOIEW0IFkeYighYUxAuLUlq0lrq2iCDpjWtmFVtoG6QVNOCFVShVLyxIk0DVjZLMxt3xTGTccd2ZOd/8JBHci0CY9zvnPPN+/7sCIXwKavOwAcy2QgngQiIztDSE0OwQlDPYR1ebiaH6J5kZChyfW12gRG4QVgGTBfMchMbFP9Sn5nlZL2D0JjLD6710lc+z0NfqSGTXQRQ4bX07Mq423yoBL3OSyHSvUxirMuaEvgbJWrdcvkHMoJwxYuq4INUhyuWvQa1jvdMGxAvCxJlyEC9XOBCWL04wwRzpbDoDQ7wfZJzIQLi5Eggk6DiRhZgWIAbE3NrM4A3LPT8Q7UgqAqLqTmLSHLGPkyzG/qXEczhd0q6RH+zaSBfaUoc4iQx19pIClIscrTkNZzG6gd7qMY6eC2Hqyo705ZfTf+eqJmhMzcSbYtQpOXc92ZsZjLVAL4YNUQbJ5Ttg4CQrQdGYj44Xr9m1XJCzmZusFDJOWNpHjmh5x624a2ZFtOKDVL+uNo2TuXE3bZQQZUf8gtgqP31uI94Z/rMqix+IGiRfWw3xN9dCgVx+L3WrHm4Dju6PXz/EkjuXJ6R+IGgyOE1TbZqTq9y1eo0EZo7oMo1ktPu3xjHvuiLT5AFNszUyDULtWpzE2/fEsey8O5TbWuGWwxrs5rS7nFNMWJrNh2No74s9Ec4vRNmRRzPXMP19fBMSVsGcOJ98G8N3Wl2gXcbTjbX7vUBxLaeASDQCm5Cu/0E2tvtb0Ea+BowtskFD0wvlc6Rf2M+Jx7dTu7ubFr2dnKDRaMQe2v/tcIrNB7FH0O50AcrBaApmRDVwFO31ql3pD8QW4dP0feNwl/Q+kFEtRyIGyaWXnpy1OO0qNJWHo1y6iCmAGkBb/Ru+HenDWIF2mo4r8G+tRRzoniSn2uqFLxANhe9LKHVyTbz6egk9+x5w5fK6ulSNNMhZ/Feno+GebLZV6isTTa6k5qNl5RnZ5u56Ib6SBvFzaWBBVFZzvnERWlt/Cg4l27XChLCqFyLekjhy6xJyoytgjPf7opIB8QPx7sYFiMXHPGt76m741MhCKMZfng0nBOIjmoJPsLqWHwgFpe6V6qtfcopxveR2Oy+J0ntIN/zCWkf8QNAJ7y6d8Bq4lxLc2/qJl5K7t432XwcqX5CrI34gzATWuYILQtdQPyePDK3iuOekCR3Efjhig1B1Uq5UoXEEoZX7d1q535J5S9VOeFyYyEBku5XTMXXKQTToX5Rg7OI44nbW5oKYeYK4EniMeF0YFNSmb+grhc84LyRCEP1/OurOcipCQbKxDeK2V5FcVyIDMQvsgz5gwFhcWWwKyRlvQ3gv29RwWoDYAbIofNyBxI9eDlQ+n3YgsgCWnr4MStGXQXmv9pF2La/k3OccV54JEBM4yp9EsXa/3LfO0dGPcYq0Y7DfZB8nJzZw2rppHgKgVHs8L5wvRwAAAABJRU5ErkJggg==)](https://sourcegraph.com/github.com/sourcegraph/conc)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/sourcegraph/conc)](https://goreportcard.com/report/github.com/sourcegraph/conc)
|
||||
[![codecov](https://codecov.io/gh/sourcegraph/conc/branch/main/graph/badge.svg?token=MQZTEA1QWT)](https://codecov.io/gh/sourcegraph/conc)
|
||||
[![Discord](https://img.shields.io/badge/discord-chat-%235765F2)](https://discord.gg/bvXQXmtRjN)
|
||||
|
||||
`conc` is your toolbelt for structured concurrency in go, making common tasks
|
||||
easier and safer.
|
||||
|
||||
```sh
|
||||
go get github.com/sourcegraph/conc
|
||||
```
|
||||
|
||||
# At a glance
|
||||
|
||||
- Use [`conc.WaitGroup`](https://pkg.go.dev/github.com/sourcegraph/conc#WaitGroup) if you just want a safer version of `sync.WaitGroup`
|
||||
- Use [`pool.Pool`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#Pool) if you want a concurrency-limited task runner
|
||||
- Use [`pool.ResultPool`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#ResultPool) if you want a concurrent task runner that collects task results
|
||||
- Use [`pool.(Result)?ErrorPool`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#ErrorPool) if your tasks are fallible
|
||||
- Use [`pool.(Result)?ContextPool`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#ContextPool) if your tasks should be canceled on failure
|
||||
- Use [`stream.Stream`](https://pkg.go.dev/github.com/sourcegraph/conc/stream#Stream) if you want to process an ordered stream of tasks in parallel with serial callbacks
|
||||
- Use [`iter.Map`](https://pkg.go.dev/github.com/sourcegraph/conc/iter#Map) if you want to concurrently map a slice
|
||||
- Use [`iter.ForEach`](https://pkg.go.dev/github.com/sourcegraph/conc/iter#ForEach) if you want to concurrently iterate over a slice
|
||||
- Use [`panics.Catcher`](https://pkg.go.dev/github.com/sourcegraph/conc/panics#Catcher) if you want to catch panics in your own goroutines
|
||||
|
||||
All pools are created with
|
||||
[`pool.New()`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#New)
|
||||
or
|
||||
[`pool.NewWithResults[T]()`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#NewWithResults),
|
||||
then configured with methods:
|
||||
|
||||
- [`p.WithMaxGoroutines()`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#Pool.MaxGoroutines) configures the maximum number of goroutines in the pool
|
||||
- [`p.WithErrors()`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#Pool.WithErrors) configures the pool to run tasks that return errors
|
||||
- [`p.WithContext(ctx)`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#Pool.WithContext) configures the pool to run tasks that should be canceled on first error
|
||||
- [`p.WithFirstError()`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#ErrorPool.WithFirstError) configures error pools to only keep the first returned error rather than an aggregated error
|
||||
- [`p.WithCollectErrored()`](https://pkg.go.dev/github.com/sourcegraph/conc/pool#ResultContextPool.WithCollectErrored) configures result pools to collect results even when the task errored
|
||||
|
||||
# Goals
|
||||
|
||||
The main goals of the package are:
|
||||
1) Make it harder to leak goroutines
|
||||
2) Handle panics gracefully
|
||||
3) Make concurrent code easier to read
|
||||
|
||||
## Goal #1: Make it harder to leak goroutines
|
||||
|
||||
A common pain point when working with goroutines is cleaning them up. It's
|
||||
really easy to fire off a `go` statement and fail to properly wait for it to
|
||||
complete.
|
||||
|
||||
`conc` takes the opinionated stance that all concurrency should be scoped.
|
||||
That is, goroutines should have an owner and that owner should always
|
||||
ensure that its owned goroutines exit properly.
|
||||
|
||||
In `conc`, the owner of a goroutine is always a `conc.WaitGroup`. Goroutines
|
||||
are spawned in a `WaitGroup` with `(*WaitGroup).Go()`, and
|
||||
`(*WaitGroup).Wait()` should always be called before the `WaitGroup` goes out
|
||||
of scope.
|
||||
|
||||
In some cases, you might want a spawned goroutine to outlast the scope of the
|
||||
caller. In that case, you could pass a `WaitGroup` into the spawning function.
|
||||
|
||||
```go
|
||||
func main() {
|
||||
var wg conc.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
startTheThing(&wg)
|
||||
}
|
||||
|
||||
func startTheThing(wg *conc.WaitGroup) {
|
||||
wg.Go(func() { ... })
|
||||
}
|
||||
```
|
||||
|
||||
For some more discussion on why scoped concurrency is nice, check out [this
|
||||
blog
|
||||
post](https://vorpus.org/blog/notes-on-structured-concurrency-or-go-statement-considered-harmful/).
|
||||
|
||||
## Goal #2: Handle panics gracefully
|
||||
|
||||
A frequent problem with goroutines in long-running applications is handling
|
||||
panics. A goroutine spawned without a panic handler will crash the whole process
|
||||
on panic. This is usually undesirable.
|
||||
|
||||
However, if you do add a panic handler to a goroutine, what do you do with the
|
||||
panic once you catch it? Some options:
|
||||
1) Ignore it
|
||||
2) Log it
|
||||
3) Turn it into an error and return that to the goroutine spawner
|
||||
4) Propagate the panic to the goroutine spawner
|
||||
|
||||
Ignoring panics is a bad idea since panics usually mean there is actually
|
||||
something wrong and someone should fix it.
|
||||
|
||||
Just logging panics isn't great either because then there is no indication to the spawner
|
||||
that something bad happened, and it might just continue on as normal even though your
|
||||
program is in a really bad state.
|
||||
|
||||
Both (3) and (4) are reasonable options, but both require the goroutine to have
|
||||
an owner that can actually receive the message that something went wrong. This
|
||||
is generally not true with a goroutine spawned with `go`, but in the `conc`
|
||||
package, all goroutines have an owner that must collect the spawned goroutine.
|
||||
In the conc package, any call to `Wait()` will panic if any of the spawned goroutines
|
||||
panicked. Additionally, it decorates the panic value with a stacktrace from the child
|
||||
goroutine so that you don't lose information about what caused the panic.
|
||||
|
||||
Doing this all correctly every time you spawn something with `go` is not
|
||||
trivial and it requires a lot of boilerplate that makes the important parts of
|
||||
the code more difficult to read, so `conc` does this for you.
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th><code>stdlib</code></th>
|
||||
<th><code>conc</code></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```go
|
||||
type caughtPanicError struct {
|
||||
val any
|
||||
stack []byte
|
||||
}
|
||||
|
||||
func (e *caughtPanicError) Error() string {
|
||||
return fmt.Sprintf(
|
||||
"panic: %q\n%s",
|
||||
e.val,
|
||||
string(e.stack)
|
||||
)
|
||||
}
|
||||
|
||||
func main() {
|
||||
done := make(chan error)
|
||||
go func() {
|
||||
defer func() {
|
||||
if v := recover(); v != nil {
|
||||
done <- &caughtPanicError{
|
||||
val: v,
|
||||
stack: debug.Stack()
|
||||
}
|
||||
} else {
|
||||
done <- nil
|
||||
}
|
||||
}()
|
||||
doSomethingThatMightPanic()
|
||||
}()
|
||||
err := <-done
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
```
|
||||
</td>
|
||||
<td>
|
||||
|
||||
```go
|
||||
func main() {
|
||||
var wg conc.WaitGroup
|
||||
wg.Go(doSomethingThatMightPanic)
|
||||
// panics with a nice stacktrace
|
||||
wg.Wait()
|
||||
}
|
||||
```
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## Goal #3: Make concurrent code easier to read
|
||||
|
||||
Doing concurrency correctly is difficult. Doing it in a way that doesn't
|
||||
obfuscate what the code is actually doing is more difficult. The `conc` package
|
||||
attempts to make common operations easier by abstracting as much boilerplate
|
||||
complexity as possible.
|
||||
|
||||
Want to run a set of concurrent tasks with a bounded set of goroutines? Use
|
||||
`pool.New()`. Want to process an ordered stream of results concurrently, but
|
||||
still maintain order? Try `stream.New()`. What about a concurrent map over
|
||||
a slice? Take a peek at `iter.Map()`.
|
||||
|
||||
Browse some examples below for some comparisons with doing these by hand.
|
||||
|
||||
# Examples
|
||||
|
||||
Each of these examples forgoes propagating panics for simplicity. To see
|
||||
what kind of complexity that would add, check out the "Goal #2" header above.
|
||||
|
||||
Spawn a set of goroutines and waiting for them to finish:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th><code>stdlib</code></th>
|
||||
<th><code>conc</code></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```go
|
||||
func main() {
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
// crashes on panic!
|
||||
doSomething()
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
```
|
||||
</td>
|
||||
<td>
|
||||
|
||||
```go
|
||||
func main() {
|
||||
var wg conc.WaitGroup
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Go(doSomething)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
```
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Process each element of a stream in a static pool of goroutines:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th><code>stdlib</code></th>
|
||||
<th><code>conc</code></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```go
|
||||
func process(stream chan int) {
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for elem := range stream {
|
||||
handle(elem)
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
||||
```
|
||||
</td>
|
||||
<td>
|
||||
|
||||
```go
|
||||
func process(stream chan int) {
|
||||
p := pool.New().WithMaxGoroutines(10)
|
||||
for elem := range stream {
|
||||
elem := elem
|
||||
p.Go(func() {
|
||||
handle(elem)
|
||||
})
|
||||
}
|
||||
p.Wait()
|
||||
}
|
||||
```
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Process each element of a slice in a static pool of goroutines:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th><code>stdlib</code></th>
|
||||
<th><code>conc</code></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```go
|
||||
func process(values []int) {
|
||||
feeder := make(chan int, 8)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
for elem := range feeder {
|
||||
handle(elem)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
for _, value := range values {
|
||||
feeder <- value
|
||||
}
|
||||
close(feeder)
|
||||
wg.Wait()
|
||||
}
|
||||
```
|
||||
</td>
|
||||
<td>
|
||||
|
||||
```go
|
||||
func process(values []int) {
|
||||
iter.ForEach(values, handle)
|
||||
}
|
||||
```
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Concurrently map a slice:
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th><code>stdlib</code></th>
|
||||
<th><code>conc</code></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```go
|
||||
func concMap(
|
||||
input []int,
|
||||
f func(int) int,
|
||||
) []int {
|
||||
res := make([]int, len(input))
|
||||
var idx atomic.Int64
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 10; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
|
||||
for {
|
||||
i := int(idx.Add(1) - 1)
|
||||
if i >= len(input) {
|
||||
return
|
||||
}
|
||||
|
||||
res[i] = f(input[i])
|
||||
}
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
return res
|
||||
}
|
||||
```
|
||||
</td>
|
||||
<td>
|
||||
|
||||
```go
|
||||
func concMap(
|
||||
input []int,
|
||||
f func(*int) int,
|
||||
) []int {
|
||||
return iter.Map(input, f)
|
||||
}
|
||||
```
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
Process an ordered stream concurrently:
|
||||
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<th><code>stdlib</code></th>
|
||||
<th><code>conc</code></th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
```go
|
||||
func mapStream(
|
||||
in chan int,
|
||||
out chan int,
|
||||
f func(int) int,
|
||||
) {
|
||||
tasks := make(chan func())
|
||||
taskResults := make(chan chan int)
|
||||
|
||||
// Worker goroutines
|
||||
var workerWg sync.WaitGroup
|
||||
for i := 0; i < 10; i++ {
|
||||
workerWg.Add(1)
|
||||
go func() {
|
||||
defer workerWg.Done()
|
||||
for task := range tasks {
|
||||
task()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// Ordered reader goroutines
|
||||
var readerWg sync.WaitGroup
|
||||
readerWg.Add(1)
|
||||
go func() {
|
||||
defer readerWg.Done()
|
||||
for result := range taskResults {
|
||||
item := <-result
|
||||
out <- item
|
||||
}
|
||||
}()
|
||||
|
||||
// Feed the workers with tasks
|
||||
for elem := range in {
|
||||
resultCh := make(chan int, 1)
|
||||
taskResults <- resultCh
|
||||
tasks <- func() {
|
||||
resultCh <- f(elem)
|
||||
}
|
||||
}
|
||||
|
||||
// We've exhausted input.
|
||||
// Wait for everything to finish
|
||||
close(tasks)
|
||||
workerWg.Wait()
|
||||
close(taskResults)
|
||||
readerWg.Wait()
|
||||
}
|
||||
```
|
||||
</td>
|
||||
<td>
|
||||
|
||||
```go
|
||||
func mapStream(
|
||||
in chan int,
|
||||
out chan int,
|
||||
f func(int) int,
|
||||
) {
|
||||
s := stream.New().WithMaxGoroutines(10)
|
||||
for elem := range in {
|
||||
elem := elem
|
||||
s.Go(func() stream.Callback {
|
||||
res := f(elem)
|
||||
return func() { out <- res }
|
||||
})
|
||||
}
|
||||
s.Wait()
|
||||
}
|
||||
```
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
# Status
|
||||
|
||||
This package is currently pre-1.0. There are likely to be minor breaking
|
||||
changes before a 1.0 release as we stabilize the APIs and tweak defaults.
|
||||
Please open an issue if you have questions, concerns, or requests that you'd
|
||||
like addressed before the 1.0 release. Currently, a 1.0 is targeted for
|
||||
March 2023.
|
10
vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go119.go
generated
vendored
Normal file
10
vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go119.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
//go:build !go1.20
|
||||
// +build !go1.20
|
||||
|
||||
package multierror
|
||||
|
||||
import "go.uber.org/multierr"
|
||||
|
||||
var (
|
||||
Join = multierr.Combine
|
||||
)
|
10
vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go120.go
generated
vendored
Normal file
10
vendor/github.com/sourcegraph/conc/internal/multierror/multierror_go120.go
generated
vendored
Normal file
|
@ -0,0 +1,10 @@
|
|||
//go:build go1.20
|
||||
// +build go1.20
|
||||
|
||||
package multierror
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
Join = errors.Join
|
||||
)
|
|
@ -0,0 +1,85 @@
|
|||
package iter
|
||||
|
||||
import (
|
||||
"runtime"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/sourcegraph/conc"
|
||||
)
|
||||
|
||||
// defaultMaxGoroutines returns the default maximum number of
|
||||
// goroutines to use within this package.
|
||||
func defaultMaxGoroutines() int { return runtime.GOMAXPROCS(0) }
|
||||
|
||||
// Iterator can be used to configure the behaviour of ForEach
|
||||
// and ForEachIdx. The zero value is safe to use with reasonable
|
||||
// defaults.
|
||||
//
|
||||
// Iterator is also safe for reuse and concurrent use.
|
||||
type Iterator[T any] struct {
|
||||
// MaxGoroutines controls the maximum number of goroutines
|
||||
// to use on this Iterator's methods.
|
||||
//
|
||||
// If unset, MaxGoroutines defaults to runtime.GOMAXPROCS(0).
|
||||
MaxGoroutines int
|
||||
}
|
||||
|
||||
// ForEach executes f in parallel over each element in input.
|
||||
//
|
||||
// It is safe to mutate the input parameter, which makes it
|
||||
// possible to map in place.
|
||||
//
|
||||
// ForEach always uses at most runtime.GOMAXPROCS goroutines.
|
||||
// It takes roughly 2µs to start up the goroutines and adds
|
||||
// an overhead of roughly 50ns per element of input. For
|
||||
// a configurable goroutine limit, use a custom Iterator.
|
||||
func ForEach[T any](input []T, f func(*T)) { Iterator[T]{}.ForEach(input, f) }
|
||||
|
||||
// ForEach executes f in parallel over each element in input,
|
||||
// using up to the Iterator's configured maximum number of
|
||||
// goroutines.
|
||||
//
|
||||
// It is safe to mutate the input parameter, which makes it
|
||||
// possible to map in place.
|
||||
//
|
||||
// It takes roughly 2µs to start up the goroutines and adds
|
||||
// an overhead of roughly 50ns per element of input.
|
||||
func (iter Iterator[T]) ForEach(input []T, f func(*T)) {
|
||||
iter.ForEachIdx(input, func(_ int, t *T) {
|
||||
f(t)
|
||||
})
|
||||
}
|
||||
|
||||
// ForEachIdx is the same as ForEach except it also provides the
|
||||
// index of the element to the callback.
|
||||
func ForEachIdx[T any](input []T, f func(int, *T)) { Iterator[T]{}.ForEachIdx(input, f) }
|
||||
|
||||
// ForEachIdx is the same as ForEach except it also provides the
|
||||
// index of the element to the callback.
|
||||
func (iter Iterator[T]) ForEachIdx(input []T, f func(int, *T)) {
|
||||
if iter.MaxGoroutines == 0 {
|
||||
// iter is a value receiver and is hence safe to mutate
|
||||
iter.MaxGoroutines = defaultMaxGoroutines()
|
||||
}
|
||||
|
||||
numInput := len(input)
|
||||
if iter.MaxGoroutines > numInput {
|
||||
// No more concurrent tasks than the number of input items.
|
||||
iter.MaxGoroutines = numInput
|
||||
}
|
||||
|
||||
var idx atomic.Int64
|
||||
// Create the task outside the loop to avoid extra closure allocations.
|
||||
task := func() {
|
||||
i := int(idx.Add(1) - 1)
|
||||
for ; i < numInput; i = int(idx.Add(1) - 1) {
|
||||
f(i, &input[i])
|
||||
}
|
||||
}
|
||||
|
||||
var wg conc.WaitGroup
|
||||
for i := 0; i < iter.MaxGoroutines; i++ {
|
||||
wg.Go(task)
|
||||
}
|
||||
wg.Wait()
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package iter
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/sourcegraph/conc/internal/multierror"
|
||||
)
|
||||
|
||||
// Mapper is an Iterator with a result type R. It can be used to configure
|
||||
// the behaviour of Map and MapErr. The zero value is safe to use with
|
||||
// reasonable defaults.
|
||||
//
|
||||
// Mapper is also safe for reuse and concurrent use.
|
||||
type Mapper[T, R any] Iterator[T]
|
||||
|
||||
// Map applies f to each element of input, returning the mapped result.
|
||||
//
|
||||
// Map always uses at most runtime.GOMAXPROCS goroutines. For a configurable
|
||||
// goroutine limit, use a custom Mapper.
|
||||
func Map[T, R any](input []T, f func(*T) R) []R {
|
||||
return Mapper[T, R]{}.Map(input, f)
|
||||
}
|
||||
|
||||
// Map applies f to each element of input, returning the mapped result.
|
||||
//
|
||||
// Map uses up to the configured Mapper's maximum number of goroutines.
|
||||
func (m Mapper[T, R]) Map(input []T, f func(*T) R) []R {
|
||||
res := make([]R, len(input))
|
||||
Iterator[T](m).ForEachIdx(input, func(i int, t *T) {
|
||||
res[i] = f(t)
|
||||
})
|
||||
return res
|
||||
}
|
||||
|
||||
// MapErr applies f to each element of the input, returning the mapped result
|
||||
// and a combined error of all returned errors.
|
||||
//
|
||||
// Map always uses at most runtime.GOMAXPROCS goroutines. For a configurable
|
||||
// goroutine limit, use a custom Mapper.
|
||||
func MapErr[T, R any](input []T, f func(*T) (R, error)) ([]R, error) {
|
||||
return Mapper[T, R]{}.MapErr(input, f)
|
||||
}
|
||||
|
||||
// MapErr applies f to each element of the input, returning the mapped result
|
||||
// and a combined error of all returned errors.
|
||||
//
|
||||
// Map uses up to the configured Mapper's maximum number of goroutines.
|
||||
func (m Mapper[T, R]) MapErr(input []T, f func(*T) (R, error)) ([]R, error) {
|
||||
var (
|
||||
res = make([]R, len(input))
|
||||
errMux sync.Mutex
|
||||
errs error
|
||||
)
|
||||
Iterator[T](m).ForEachIdx(input, func(i int, t *T) {
|
||||
var err error
|
||||
res[i], err = f(t)
|
||||
if err != nil {
|
||||
errMux.Lock()
|
||||
// TODO: use stdlib errors once multierrors land in go 1.20
|
||||
errs = multierror.Join(errs, err)
|
||||
errMux.Unlock()
|
||||
}
|
||||
})
|
||||
return res, errs
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
package panics
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Catcher is used to catch panics. You can execute a function with Try,
|
||||
// which will catch any spawned panic. Try can be called any number of times,
|
||||
// from any number of goroutines. Once all calls to Try have completed, you can
|
||||
// get the value of the first panic (if any) with Recovered(), or you can just
|
||||
// propagate the panic (re-panic) with Repanic().
|
||||
type Catcher struct {
|
||||
recovered atomic.Pointer[Recovered]
|
||||
}
|
||||
|
||||
// Try executes f, catching any panic it might spawn. It is safe
|
||||
// to call from multiple goroutines simultaneously.
|
||||
func (p *Catcher) Try(f func()) {
|
||||
defer p.tryRecover()
|
||||
f()
|
||||
}
|
||||
|
||||
func (p *Catcher) tryRecover() {
|
||||
if val := recover(); val != nil {
|
||||
rp := NewRecovered(1, val)
|
||||
p.recovered.CompareAndSwap(nil, &rp)
|
||||
}
|
||||
}
|
||||
|
||||
// Repanic panics if any calls to Try caught a panic. It will panic with the
|
||||
// value of the first panic caught, wrapped in a panics.Recovered with caller
|
||||
// information.
|
||||
func (p *Catcher) Repanic() {
|
||||
if val := p.Recovered(); val != nil {
|
||||
panic(val)
|
||||
}
|
||||
}
|
||||
|
||||
// Recovered returns the value of the first panic caught by Try, or nil if
|
||||
// no calls to Try panicked.
|
||||
func (p *Catcher) Recovered() *Recovered {
|
||||
return p.recovered.Load()
|
||||
}
|
||||
|
||||
// NewRecovered creates a panics.Recovered from a panic value and a collected
|
||||
// stacktrace. The skip parameter allows the caller to skip stack frames when
|
||||
// collecting the stacktrace. Calling with a skip of 0 means include the call to
|
||||
// NewRecovered in the stacktrace.
|
||||
func NewRecovered(skip int, value any) Recovered {
|
||||
// 64 frames should be plenty
|
||||
var callers [64]uintptr
|
||||
n := runtime.Callers(skip+1, callers[:])
|
||||
return Recovered{
|
||||
Value: value,
|
||||
Callers: callers[:n],
|
||||
Stack: debug.Stack(),
|
||||
}
|
||||
}
|
||||
|
||||
// Recovered is a panic that was caught with recover().
|
||||
type Recovered struct {
|
||||
// The original value of the panic.
|
||||
Value any
|
||||
// The caller list as returned by runtime.Callers when the panic was
|
||||
// recovered. Can be used to produce a more detailed stack information with
|
||||
// runtime.CallersFrames.
|
||||
Callers []uintptr
|
||||
// The formatted stacktrace from the goroutine where the panic was recovered.
|
||||
// Easier to use than Callers.
|
||||
Stack []byte
|
||||
}
|
||||
|
||||
// String renders a human-readable formatting of the panic.
|
||||
func (p *Recovered) String() string {
|
||||
return fmt.Sprintf("panic: %v\nstacktrace:\n%s\n", p.Value, p.Stack)
|
||||
}
|
||||
|
||||
// AsError casts the panic into an error implementation. The implementation
|
||||
// is unwrappable with the cause of the panic, if the panic was provided one.
|
||||
func (p *Recovered) AsError() error {
|
||||
if p == nil {
|
||||
return nil
|
||||
}
|
||||
return &ErrRecovered{*p}
|
||||
}
|
||||
|
||||
// ErrRecovered wraps a panics.Recovered in an error implementation.
|
||||
type ErrRecovered struct{ Recovered }
|
||||
|
||||
var _ error = (*ErrRecovered)(nil)
|
||||
|
||||
func (p *ErrRecovered) Error() string { return p.String() }
|
||||
|
||||
func (p *ErrRecovered) Unwrap() error {
|
||||
if err, ok := p.Value.(error); ok {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package panics
|
||||
|
||||
// Try executes f, catching and returning any panic it might spawn.
|
||||
//
|
||||
// The recovered panic can be propagated with panic(), or handled as a normal error with
|
||||
// (*panics.Recovered).AsError().
|
||||
func Try(f func()) *Recovered {
|
||||
var c Catcher
|
||||
c.Try(f)
|
||||
return c.Recovered()
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package conc
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/sourcegraph/conc/panics"
|
||||
)
|
||||
|
||||
// NewWaitGroup creates a new WaitGroup.
|
||||
func NewWaitGroup() *WaitGroup {
|
||||
return &WaitGroup{}
|
||||
}
|
||||
|
||||
// WaitGroup is the primary building block for scoped concurrency.
|
||||
// Goroutines can be spawned in the WaitGroup with the Go method,
|
||||
// and calling Wait() will ensure that each of those goroutines exits
|
||||
// before continuing. Any panics in a child goroutine will be caught
|
||||
// and propagated to the caller of Wait().
|
||||
//
|
||||
// The zero value of WaitGroup is usable, just like sync.WaitGroup.
|
||||
// Also like sync.WaitGroup, it must not be copied after first use.
|
||||
type WaitGroup struct {
|
||||
wg sync.WaitGroup
|
||||
pc panics.Catcher
|
||||
}
|
||||
|
||||
// Go spawns a new goroutine in the WaitGroup.
|
||||
func (h *WaitGroup) Go(f func()) {
|
||||
h.wg.Add(1)
|
||||
go func() {
|
||||
defer h.wg.Done()
|
||||
h.pc.Try(f)
|
||||
}()
|
||||
}
|
||||
|
||||
// Wait will block until all goroutines spawned with Go exit and will
|
||||
// propagate any panics spawned in a child goroutine.
|
||||
func (h *WaitGroup) Wait() {
|
||||
h.wg.Wait()
|
||||
|
||||
// Propagate a panic if we caught one from a child goroutine.
|
||||
h.pc.Repanic()
|
||||
}
|
||||
|
||||
// WaitAndRecover will block until all goroutines spawned with Go exit and
|
||||
// will return a *panics.Recovered if one of the child goroutines panics.
|
||||
func (h *WaitGroup) WaitAndRecover() *panics.Recovered {
|
||||
h.wg.Wait()
|
||||
|
||||
// Return a recovered panic if we caught one from a child goroutine.
|
||||
return h.pc.Recovered()
|
||||
}
|
|
@ -11,8 +11,8 @@
|
|||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
//go:build aix || darwin || openbsd || freebsd || netbsd || dragonfly
|
||||
// +build aix darwin openbsd freebsd netbsd dragonfly
|
||||
//go:build aix || darwin || openbsd || freebsd || netbsd || dragonfly || zos
|
||||
// +build aix darwin openbsd freebsd netbsd dragonfly zos
|
||||
|
||||
package afero
|
||||
|
||||
|
|
|
@ -10,8 +10,8 @@
|
|||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
//go:build !darwin && !openbsd && !freebsd && !dragonfly && !netbsd && !aix
|
||||
// +build !darwin,!openbsd,!freebsd,!dragonfly,!netbsd,!aix
|
||||
//go:build !darwin && !openbsd && !freebsd && !dragonfly && !netbsd && !aix && !zos
|
||||
// +build !darwin,!openbsd,!freebsd,!dragonfly,!netbsd,!aix,!zos
|
||||
|
||||
package afero
|
||||
|
||||
|
|
|
@ -16,9 +16,12 @@ package afero
|
|||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
@ -88,6 +91,24 @@ func (m *MemMapFs) findParent(f *mem.FileData) *mem.FileData {
|
|||
return pfile
|
||||
}
|
||||
|
||||
func (m *MemMapFs) findDescendants(name string) []*mem.FileData {
|
||||
fData := m.getData()
|
||||
descendants := make([]*mem.FileData, 0, len(fData))
|
||||
for p, dFile := range fData {
|
||||
if strings.HasPrefix(p, name+FilePathSeparator) {
|
||||
descendants = append(descendants, dFile)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Slice(descendants, func(i, j int) bool {
|
||||
cur := len(strings.Split(descendants[i].Name(), FilePathSeparator))
|
||||
next := len(strings.Split(descendants[j].Name(), FilePathSeparator))
|
||||
return cur < next
|
||||
})
|
||||
|
||||
return descendants
|
||||
}
|
||||
|
||||
func (m *MemMapFs) registerWithParent(f *mem.FileData, perm os.FileMode) {
|
||||
if f == nil {
|
||||
return
|
||||
|
@ -309,29 +330,51 @@ func (m *MemMapFs) Rename(oldname, newname string) error {
|
|||
if _, ok := m.getData()[oldname]; ok {
|
||||
m.mu.RUnlock()
|
||||
m.mu.Lock()
|
||||
m.unRegisterWithParent(oldname)
|
||||
err := m.unRegisterWithParent(oldname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fileData := m.getData()[oldname]
|
||||
delete(m.getData(), oldname)
|
||||
mem.ChangeFileName(fileData, newname)
|
||||
m.getData()[newname] = fileData
|
||||
|
||||
err = m.renameDescendants(oldname, newname)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
delete(m.getData(), oldname)
|
||||
|
||||
m.registerWithParent(fileData, 0)
|
||||
m.mu.Unlock()
|
||||
m.mu.RLock()
|
||||
} else {
|
||||
return &os.PathError{Op: "rename", Path: oldname, Err: ErrFileNotFound}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
for p, fileData := range m.getData() {
|
||||
if strings.HasPrefix(p, oldname+FilePathSeparator) {
|
||||
m.mu.RUnlock()
|
||||
m.mu.Lock()
|
||||
delete(m.getData(), p)
|
||||
p := strings.Replace(p, oldname, newname, 1)
|
||||
m.getData()[p] = fileData
|
||||
m.mu.Unlock()
|
||||
m.mu.RLock()
|
||||
func (m *MemMapFs) renameDescendants(oldname, newname string) error {
|
||||
descendants := m.findDescendants(oldname)
|
||||
removes := make([]string, 0, len(descendants))
|
||||
for _, desc := range descendants {
|
||||
descNewName := strings.Replace(desc.Name(), oldname, newname, 1)
|
||||
err := m.unRegisterWithParent(desc.Name())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
removes = append(removes, desc.Name())
|
||||
mem.ChangeFileName(desc, descNewName)
|
||||
m.getData()[descNewName] = desc
|
||||
|
||||
m.registerWithParent(desc, 0)
|
||||
}
|
||||
for _, r := range removes {
|
||||
delete(m.getData(), r)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
# cast
|
||||
|
||||
[![Build Status](https://github.com/spf13/cast/actions/workflows/ci.yml/badge.svg)](https://github.com/spf13/cast/actions/workflows/ci.yml)
|
||||
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/cast/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/cast/actions/workflows/ci.yaml)
|
||||
[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/cast)](https://pkg.go.dev/mod/github.com/spf13/cast)
|
||||
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cast)](https://goreportcard.com/report/github.com/spf13/cast)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/cast?style=flat-square)](https://goreportcard.com/report/github.com/spf13/cast)
|
||||
|
||||
Easy and safe casting from one type to another in Go
|
||||
|
||||
|
|
|
@ -1,24 +0,0 @@
|
|||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.bench
|
||||
go.sum
|
|
@ -1,148 +0,0 @@
|
|||
jWalterWeatherman
|
||||
=================
|
||||
|
||||
Seamless printing to the terminal (stdout) and logging to a io.Writer
|
||||
(file) that’s as easy to use as fmt.Println.
|
||||
|
||||
![and_that__s_why_you_always_leave_a_note_by_jonnyetc-d57q7um](https://cloud.githubusercontent.com/assets/173412/11002937/ccd01654-847d-11e5-828e-12ebaf582eaf.jpg)
|
||||
Graphic by [JonnyEtc](http://jonnyetc.deviantart.com/art/And-That-s-Why-You-Always-Leave-a-Note-315311422)
|
||||
|
||||
JWW is primarily a wrapper around the excellent standard log library. It
|
||||
provides a few advantages over using the standard log library alone.
|
||||
|
||||
1. Ready to go out of the box.
|
||||
2. One library for both printing to the terminal and logging (to files).
|
||||
3. Really easy to log to either a temp file or a file you specify.
|
||||
|
||||
|
||||
I really wanted a very straightforward library that could seamlessly do
|
||||
the following things.
|
||||
|
||||
1. Replace all the println, printf, etc statements thoughout my code with
|
||||
something more useful
|
||||
2. Allow the user to easily control what levels are printed to stdout
|
||||
3. Allow the user to easily control what levels are logged
|
||||
4. Provide an easy mechanism (like fmt.Println) to print info to the user
|
||||
which can be easily logged as well
|
||||
5. Due to 2 & 3 provide easy verbose mode for output and logs
|
||||
6. Not have any unnecessary initialization cruft. Just use it.
|
||||
|
||||
# Usage
|
||||
|
||||
## Step 1. Use it
|
||||
Put calls throughout your source based on type of feedback.
|
||||
No initialization or setup needs to happen. Just start calling things.
|
||||
|
||||
Available Loggers are:
|
||||
|
||||
* TRACE
|
||||
* DEBUG
|
||||
* INFO
|
||||
* WARN
|
||||
* ERROR
|
||||
* CRITICAL
|
||||
* FATAL
|
||||
|
||||
These each are loggers based on the log standard library and follow the
|
||||
standard usage. Eg.
|
||||
|
||||
```go
|
||||
import (
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
...
|
||||
|
||||
if err != nil {
|
||||
|
||||
// This is a pretty serious error and the user should know about
|
||||
// it. It will be printed to the terminal as well as logged under the
|
||||
// default thresholds.
|
||||
|
||||
jww.ERROR.Println(err)
|
||||
}
|
||||
|
||||
if err2 != nil {
|
||||
// This error isn’t going to materially change the behavior of the
|
||||
// application, but it’s something that may not be what the user
|
||||
// expects. Under the default thresholds, Warn will be logged, but
|
||||
// not printed to the terminal.
|
||||
|
||||
jww.WARN.Println(err2)
|
||||
}
|
||||
|
||||
// Information that’s relevant to what’s happening, but not very
|
||||
// important for the user. Under the default thresholds this will be
|
||||
// discarded.
|
||||
|
||||
jww.INFO.Printf("information %q", response)
|
||||
|
||||
```
|
||||
|
||||
NOTE: You can also use the library in a non-global setting by creating an instance of a Notebook:
|
||||
|
||||
```go
|
||||
notepad = jww.NewNotepad(jww.LevelInfo, jww.LevelTrace, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
|
||||
notepad.WARN.Println("Some warning"")
|
||||
```
|
||||
|
||||
_Why 7 levels?_
|
||||
|
||||
Maybe you think that 7 levels are too much for any application... and you
|
||||
are probably correct. Just because there are seven levels doesn’t mean
|
||||
that you should be using all 7 levels. Pick the right set for your needs.
|
||||
Remember they only have to mean something to your project.
|
||||
|
||||
## Step 2. Optionally configure JWW
|
||||
|
||||
Under the default thresholds :
|
||||
|
||||
* Debug, Trace & Info goto /dev/null
|
||||
* Warn and above is logged (when a log file/io.Writer is provided)
|
||||
* Error and above is printed to the terminal (stdout)
|
||||
|
||||
### Changing the thresholds
|
||||
|
||||
The threshold can be changed at any time, but will only affect calls that
|
||||
execute after the change was made.
|
||||
|
||||
This is very useful if your application has a verbose mode. Of course you
|
||||
can decide what verbose means to you or even have multiple levels of
|
||||
verbosity.
|
||||
|
||||
|
||||
```go
|
||||
import (
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
)
|
||||
|
||||
if Verbose {
|
||||
jww.SetLogThreshold(jww.LevelTrace)
|
||||
jww.SetStdoutThreshold(jww.LevelInfo)
|
||||
}
|
||||
```
|
||||
|
||||
Note that JWW's own internal output uses log levels as well, so set the log
|
||||
level before making any other calls if you want to see what it's up to.
|
||||
|
||||
|
||||
### Setting a log file
|
||||
|
||||
JWW can log to any `io.Writer`:
|
||||
|
||||
|
||||
```go
|
||||
|
||||
jww.SetLogOutput(customWriter)
|
||||
|
||||
```
|
||||
|
||||
|
||||
# More information
|
||||
|
||||
This is an early release. I’ve been using it for a while and this is the
|
||||
third interface I’ve tried. I like this one pretty well, but no guarantees
|
||||
that it won’t change a bit.
|
||||
|
||||
I wrote this for use in [hugo](https://gohugo.io). If you are looking
|
||||
for a static website engine that’s super fast please checkout Hugo.
|
|
@ -1,111 +0,0 @@
|
|||
// Copyright © 2016 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package jwalterweatherman
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
)
|
||||
|
||||
var (
|
||||
TRACE *log.Logger
|
||||
DEBUG *log.Logger
|
||||
INFO *log.Logger
|
||||
WARN *log.Logger
|
||||
ERROR *log.Logger
|
||||
CRITICAL *log.Logger
|
||||
FATAL *log.Logger
|
||||
|
||||
LOG *log.Logger
|
||||
FEEDBACK *Feedback
|
||||
|
||||
defaultNotepad *Notepad
|
||||
)
|
||||
|
||||
func reloadDefaultNotepad() {
|
||||
TRACE = defaultNotepad.TRACE
|
||||
DEBUG = defaultNotepad.DEBUG
|
||||
INFO = defaultNotepad.INFO
|
||||
WARN = defaultNotepad.WARN
|
||||
ERROR = defaultNotepad.ERROR
|
||||
CRITICAL = defaultNotepad.CRITICAL
|
||||
FATAL = defaultNotepad.FATAL
|
||||
|
||||
LOG = defaultNotepad.LOG
|
||||
FEEDBACK = defaultNotepad.FEEDBACK
|
||||
}
|
||||
|
||||
func init() {
|
||||
defaultNotepad = NewNotepad(LevelError, LevelWarn, os.Stdout, ioutil.Discard, "", log.Ldate|log.Ltime)
|
||||
reloadDefaultNotepad()
|
||||
}
|
||||
|
||||
// SetLogThreshold set the log threshold for the default notepad. Trace by default.
|
||||
func SetLogThreshold(threshold Threshold) {
|
||||
defaultNotepad.SetLogThreshold(threshold)
|
||||
reloadDefaultNotepad()
|
||||
}
|
||||
|
||||
// SetLogOutput set the log output for the default notepad. Discarded by default.
|
||||
func SetLogOutput(handle io.Writer) {
|
||||
defaultNotepad.SetLogOutput(handle)
|
||||
reloadDefaultNotepad()
|
||||
}
|
||||
|
||||
// SetStdoutThreshold set the standard output threshold for the default notepad.
|
||||
// Info by default.
|
||||
func SetStdoutThreshold(threshold Threshold) {
|
||||
defaultNotepad.SetStdoutThreshold(threshold)
|
||||
reloadDefaultNotepad()
|
||||
}
|
||||
|
||||
// SetStdoutOutput set the stdout output for the default notepad. Default is stdout.
|
||||
func SetStdoutOutput(handle io.Writer) {
|
||||
defaultNotepad.outHandle = handle
|
||||
defaultNotepad.init()
|
||||
reloadDefaultNotepad()
|
||||
}
|
||||
|
||||
// SetPrefix set the prefix for the default logger. Empty by default.
|
||||
func SetPrefix(prefix string) {
|
||||
defaultNotepad.SetPrefix(prefix)
|
||||
reloadDefaultNotepad()
|
||||
}
|
||||
|
||||
// SetFlags set the flags for the default logger. "log.Ldate | log.Ltime" by default.
|
||||
func SetFlags(flags int) {
|
||||
defaultNotepad.SetFlags(flags)
|
||||
reloadDefaultNotepad()
|
||||
}
|
||||
|
||||
// SetLogListeners configures the default logger with one or more log listeners.
|
||||
func SetLogListeners(l ...LogListener) {
|
||||
defaultNotepad.logListeners = l
|
||||
defaultNotepad.init()
|
||||
reloadDefaultNotepad()
|
||||
}
|
||||
|
||||
// Level returns the current global log threshold.
|
||||
func LogThreshold() Threshold {
|
||||
return defaultNotepad.logThreshold
|
||||
}
|
||||
|
||||
// Level returns the current global output threshold.
|
||||
func StdoutThreshold() Threshold {
|
||||
return defaultNotepad.stdoutThreshold
|
||||
}
|
||||
|
||||
// GetStdoutThreshold returns the defined Treshold for the log logger.
|
||||
func GetLogThreshold() Threshold {
|
||||
return defaultNotepad.GetLogThreshold()
|
||||
}
|
||||
|
||||
// GetStdoutThreshold returns the Treshold for the stdout logger.
|
||||
func GetStdoutThreshold() Threshold {
|
||||
return defaultNotepad.GetStdoutThreshold()
|
||||
}
|
|
@ -1,46 +0,0 @@
|
|||
// Copyright © 2016 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package jwalterweatherman
|
||||
|
||||
import (
|
||||
"io"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
// Counter is an io.Writer that increments a counter on Write.
|
||||
type Counter struct {
|
||||
count uint64
|
||||
}
|
||||
|
||||
func (c *Counter) incr() {
|
||||
atomic.AddUint64(&c.count, 1)
|
||||
}
|
||||
|
||||
// Reset resets the counter.
|
||||
func (c *Counter) Reset() {
|
||||
atomic.StoreUint64(&c.count, 0)
|
||||
}
|
||||
|
||||
// Count returns the current count.
|
||||
func (c *Counter) Count() uint64 {
|
||||
return atomic.LoadUint64(&c.count)
|
||||
}
|
||||
|
||||
func (c *Counter) Write(p []byte) (n int, err error) {
|
||||
c.incr()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
// LogCounter creates a LogListener that counts log statements >= the given threshold.
|
||||
func LogCounter(counter *Counter, t1 Threshold) LogListener {
|
||||
return func(t2 Threshold) io.Writer {
|
||||
if t2 < t1 {
|
||||
// Not interested in this threshold.
|
||||
return nil
|
||||
}
|
||||
return counter
|
||||
}
|
||||
}
|
|
@ -1,225 +0,0 @@
|
|||
// Copyright © 2016 Steve Francia <spf@spf13.com>.
|
||||
//
|
||||
// Use of this source code is governed by an MIT-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package jwalterweatherman
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
)
|
||||
|
||||
type Threshold int
|
||||
|
||||
func (t Threshold) String() string {
|
||||
return prefixes[t]
|
||||
}
|
||||
|
||||
const (
|
||||
LevelTrace Threshold = iota
|
||||
LevelDebug
|
||||
LevelInfo
|
||||
LevelWarn
|
||||
LevelError
|
||||
LevelCritical
|
||||
LevelFatal
|
||||
)
|
||||
|
||||
var prefixes map[Threshold]string = map[Threshold]string{
|
||||
LevelTrace: "TRACE",
|
||||
LevelDebug: "DEBUG",
|
||||
LevelInfo: "INFO",
|
||||
LevelWarn: "WARN",
|
||||
LevelError: "ERROR",
|
||||
LevelCritical: "CRITICAL",
|
||||
LevelFatal: "FATAL",
|
||||
}
|
||||
|
||||
// Notepad is where you leave a note!
|
||||
type Notepad struct {
|
||||
TRACE *log.Logger
|
||||
DEBUG *log.Logger
|
||||
INFO *log.Logger
|
||||
WARN *log.Logger
|
||||
ERROR *log.Logger
|
||||
CRITICAL *log.Logger
|
||||
FATAL *log.Logger
|
||||
|
||||
LOG *log.Logger
|
||||
FEEDBACK *Feedback
|
||||
|
||||
loggers [7]**log.Logger
|
||||
logHandle io.Writer
|
||||
outHandle io.Writer
|
||||
logThreshold Threshold
|
||||
stdoutThreshold Threshold
|
||||
prefix string
|
||||
flags int
|
||||
|
||||
logListeners []LogListener
|
||||
}
|
||||
|
||||
// A LogListener can ble supplied to a Notepad to listen on log writes for a given
|
||||
// threshold. This can be used to capture log events in unit tests and similar.
|
||||
// Note that this function will be invoked once for each log threshold. If
|
||||
// the given threshold is not of interest to you, return nil.
|
||||
// Note that these listeners will receive log events for a given threshold, even
|
||||
// if the current configuration says not to log it. That way you can count ERRORs even
|
||||
// if you don't print them to the console.
|
||||
type LogListener func(t Threshold) io.Writer
|
||||
|
||||
// NewNotepad creates a new Notepad.
|
||||
func NewNotepad(
|
||||
outThreshold Threshold,
|
||||
logThreshold Threshold,
|
||||
outHandle, logHandle io.Writer,
|
||||
prefix string, flags int,
|
||||
logListeners ...LogListener,
|
||||
) *Notepad {
|
||||
|
||||
n := &Notepad{logListeners: logListeners}
|
||||
|
||||
n.loggers = [7]**log.Logger{&n.TRACE, &n.DEBUG, &n.INFO, &n.WARN, &n.ERROR, &n.CRITICAL, &n.FATAL}
|
||||
n.outHandle = outHandle
|
||||
n.logHandle = logHandle
|
||||
n.stdoutThreshold = outThreshold
|
||||
n.logThreshold = logThreshold
|
||||
|
||||
if len(prefix) != 0 {
|
||||
n.prefix = "[" + prefix + "] "
|
||||
} else {
|
||||
n.prefix = ""
|
||||
}
|
||||
|
||||
n.flags = flags
|
||||
|
||||
n.LOG = log.New(n.logHandle,
|
||||
"LOG: ",
|
||||
n.flags)
|
||||
n.FEEDBACK = &Feedback{out: log.New(outHandle, "", 0), log: n.LOG}
|
||||
|
||||
n.init()
|
||||
return n
|
||||
}
|
||||
|
||||
// init creates the loggers for each level depending on the notepad thresholds.
|
||||
func (n *Notepad) init() {
|
||||
logAndOut := io.MultiWriter(n.outHandle, n.logHandle)
|
||||
|
||||
for t, logger := range n.loggers {
|
||||
threshold := Threshold(t)
|
||||
prefix := n.prefix + threshold.String() + " "
|
||||
|
||||
switch {
|
||||
case threshold >= n.logThreshold && threshold >= n.stdoutThreshold:
|
||||
*logger = log.New(n.createLogWriters(threshold, logAndOut), prefix, n.flags)
|
||||
|
||||
case threshold >= n.logThreshold:
|
||||
*logger = log.New(n.createLogWriters(threshold, n.logHandle), prefix, n.flags)
|
||||
|
||||
case threshold >= n.stdoutThreshold:
|
||||
*logger = log.New(n.createLogWriters(threshold, n.outHandle), prefix, n.flags)
|
||||
|
||||
default:
|
||||
*logger = log.New(n.createLogWriters(threshold, ioutil.Discard), prefix, n.flags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (n *Notepad) createLogWriters(t Threshold, handle io.Writer) io.Writer {
|
||||
if len(n.logListeners) == 0 {
|
||||
return handle
|
||||
}
|
||||
writers := []io.Writer{handle}
|
||||
for _, l := range n.logListeners {
|
||||
w := l(t)
|
||||
if w != nil {
|
||||
writers = append(writers, w)
|
||||
}
|
||||
}
|
||||
|
||||
if len(writers) == 1 {
|
||||
return handle
|
||||
}
|
||||
|
||||
return io.MultiWriter(writers...)
|
||||
}
|
||||
|
||||
// SetLogThreshold changes the threshold above which messages are written to the
|
||||
// log file.
|
||||
func (n *Notepad) SetLogThreshold(threshold Threshold) {
|
||||
n.logThreshold = threshold
|
||||
n.init()
|
||||
}
|
||||
|
||||
// SetLogOutput changes the file where log messages are written.
|
||||
func (n *Notepad) SetLogOutput(handle io.Writer) {
|
||||
n.logHandle = handle
|
||||
n.init()
|
||||
}
|
||||
|
||||
// GetStdoutThreshold returns the defined Treshold for the log logger.
|
||||
func (n *Notepad) GetLogThreshold() Threshold {
|
||||
return n.logThreshold
|
||||
}
|
||||
|
||||
// SetStdoutThreshold changes the threshold above which messages are written to the
|
||||
// standard output.
|
||||
func (n *Notepad) SetStdoutThreshold(threshold Threshold) {
|
||||
n.stdoutThreshold = threshold
|
||||
n.init()
|
||||
}
|
||||
|
||||
// GetStdoutThreshold returns the Treshold for the stdout logger.
|
||||
func (n *Notepad) GetStdoutThreshold() Threshold {
|
||||
return n.stdoutThreshold
|
||||
}
|
||||
|
||||
// SetPrefix changes the prefix used by the notepad. Prefixes are displayed between
|
||||
// brackets at the beginning of the line. An empty prefix won't be displayed at all.
|
||||
func (n *Notepad) SetPrefix(prefix string) {
|
||||
if len(prefix) != 0 {
|
||||
n.prefix = "[" + prefix + "] "
|
||||
} else {
|
||||
n.prefix = ""
|
||||
}
|
||||
n.init()
|
||||
}
|
||||
|
||||
// SetFlags choose which flags the logger will display (after prefix and message
|
||||
// level). See the package log for more informations on this.
|
||||
func (n *Notepad) SetFlags(flags int) {
|
||||
n.flags = flags
|
||||
n.init()
|
||||
}
|
||||
|
||||
// Feedback writes plainly to the outHandle while
|
||||
// logging with the standard extra information (date, file, etc).
|
||||
type Feedback struct {
|
||||
out *log.Logger
|
||||
log *log.Logger
|
||||
}
|
||||
|
||||
func (fb *Feedback) Println(v ...interface{}) {
|
||||
fb.output(fmt.Sprintln(v...))
|
||||
}
|
||||
|
||||
func (fb *Feedback) Printf(format string, v ...interface{}) {
|
||||
fb.output(fmt.Sprintf(format, v...))
|
||||
}
|
||||
|
||||
func (fb *Feedback) Print(v ...interface{}) {
|
||||
fb.output(fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (fb *Feedback) output(s string) {
|
||||
if fb.out != nil {
|
||||
fb.out.Output(2, s)
|
||||
}
|
||||
if fb.log != nil {
|
||||
fb.log.Output(2, s)
|
||||
}
|
||||
}
|
|
@ -13,3 +13,6 @@ indent_style = tab
|
|||
|
||||
[{Makefile,*.mk}]
|
||||
indent_style = tab
|
||||
|
||||
[*.nix]
|
||||
indent_size = 2
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
/.devenv/
|
||||
/.direnv/
|
||||
/.idea/
|
||||
/.pre-commit-config.yaml
|
||||
/bin/
|
||||
/build/
|
||||
/var/
|
||||
|
|
|
@ -7,6 +7,16 @@ linters-settings:
|
|||
- standard
|
||||
- default
|
||||
- prefix(github.com/spf13/viper)
|
||||
gocritic:
|
||||
# Enable multiple checks by tags. See "Tags" section in https://github.com/go-critic/go-critic#usage.
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- experimental
|
||||
- opinionated
|
||||
- style
|
||||
disabled-checks:
|
||||
- importShadow
|
||||
- unnamedResult
|
||||
golint:
|
||||
min-confidence: 0
|
||||
goimports:
|
||||
|
@ -22,6 +32,8 @@ linters:
|
|||
- exhaustive
|
||||
- exportloopref
|
||||
- gci
|
||||
- gocritic
|
||||
- godot
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goimports
|
||||
|
@ -62,9 +74,7 @@ linters:
|
|||
# - gochecknoinits
|
||||
# - gocognit
|
||||
# - goconst
|
||||
# - gocritic
|
||||
# - gocyclo
|
||||
# - godot
|
||||
# - gosec
|
||||
# - gosimple
|
||||
# - ifshort
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
# TODO: FIXME
|
||||
/.github/
|
|
@ -0,0 +1,6 @@
|
|||
ignore-from-file: [.gitignore, .yamlignore]
|
||||
|
||||
extends: default
|
||||
|
||||
rules:
|
||||
line-length: disable
|
|
@ -16,7 +16,7 @@ endif
|
|||
|
||||
# Dependency versions
|
||||
GOTESTSUM_VERSION = 1.9.0
|
||||
GOLANGCI_VERSION = 1.52.2
|
||||
GOLANGCI_VERSION = 1.53.3
|
||||
|
||||
# Add the ability to override some variables
|
||||
# Use with care
|
||||
|
@ -29,11 +29,6 @@ clear: ## Clear the working area and the project
|
|||
.PHONY: check
|
||||
check: test lint ## Run tests and linters
|
||||
|
||||
bin/gotestsum: bin/gotestsum-${GOTESTSUM_VERSION}
|
||||
@ln -sf gotestsum-${GOTESTSUM_VERSION} bin/gotestsum
|
||||
bin/gotestsum-${GOTESTSUM_VERSION}:
|
||||
@mkdir -p bin
|
||||
curl -L https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_${OS}_amd64.tar.gz | tar -zOxf - gotestsum > ./bin/gotestsum-${GOTESTSUM_VERSION} && chmod +x ./bin/gotestsum-${GOTESTSUM_VERSION}
|
||||
|
||||
TEST_PKGS ?= ./...
|
||||
.PHONY: test
|
||||
|
@ -44,20 +39,36 @@ test: bin/gotestsum ## Run tests
|
|||
@mkdir -p ${BUILD_DIR}
|
||||
bin/gotestsum --no-summary=skipped --junitfile ${BUILD_DIR}/coverage.xml --format ${TEST_FORMAT} -- -race -coverprofile=${BUILD_DIR}/coverage.txt -covermode=atomic $(filter-out -v,${GOARGS}) $(if ${TEST_PKGS},${TEST_PKGS},./...)
|
||||
|
||||
bin/golangci-lint: bin/golangci-lint-${GOLANGCI_VERSION}
|
||||
@ln -sf golangci-lint-${GOLANGCI_VERSION} bin/golangci-lint
|
||||
bin/golangci-lint-${GOLANGCI_VERSION}:
|
||||
@mkdir -p bin
|
||||
curl -sfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- -b ./bin/ v${GOLANGCI_VERSION}
|
||||
@mv bin/golangci-lint "$@"
|
||||
|
||||
.PHONY: lint
|
||||
lint: bin/golangci-lint ## Run linter
|
||||
bin/golangci-lint run
|
||||
lint: lint-go lint-yaml
|
||||
lint: ## Run linters
|
||||
|
||||
.PHONY: fix
|
||||
fix: bin/golangci-lint ## Fix lint violations
|
||||
bin/golangci-lint run --fix
|
||||
.PHONY: lint-go
|
||||
lint-go:
|
||||
golangci-lint run $(if ${CI},--out-format github-actions,)
|
||||
|
||||
.PHONY: lint-yaml
|
||||
lint-yaml:
|
||||
yamllint $(if ${CI},-f github,) --no-warnings .
|
||||
|
||||
.PHONY: fmt
|
||||
fmt: ## Format code
|
||||
golangci-lint run --fix
|
||||
|
||||
deps: bin/golangci-lint bin/gotestsum yamllint
|
||||
deps: ## Install dependencies
|
||||
|
||||
bin/gotestsum:
|
||||
@mkdir -p bin
|
||||
curl -L https://github.com/gotestyourself/gotestsum/releases/download/v${GOTESTSUM_VERSION}/gotestsum_${GOTESTSUM_VERSION}_${OS}_amd64.tar.gz | tar -zOxf - gotestsum > ./bin/gotestsum && chmod +x ./bin/gotestsum
|
||||
|
||||
bin/golangci-lint:
|
||||
@mkdir -p bin
|
||||
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | bash -s -- v${GOLANGCI_VERSION}
|
||||
|
||||
.PHONY: yamllint
|
||||
yamllint:
|
||||
pip3 install --user yamllint
|
||||
|
||||
# Add custom targets here
|
||||
-include custom.mk
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
[![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/spf13/viper/ci.yaml?branch=master&style=flat-square)](https://github.com/spf13/viper/actions?query=workflow%3ACI)
|
||||
[![Join the chat at https://gitter.im/spf13/viper](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/spf13/viper?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
|
||||
[![Go Report Card](https://goreportcard.com/badge/github.com/spf13/viper?style=flat-square)](https://goreportcard.com/report/github.com/spf13/viper)
|
||||
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.16-61CFDD.svg?style=flat-square)
|
||||
![Go Version](https://img.shields.io/badge/go%20version-%3E=1.19-61CFDD.svg?style=flat-square)
|
||||
[![PkgGoDev](https://pkg.go.dev/badge/mod/github.com/spf13/viper)](https://pkg.go.dev/mod/github.com/spf13/viper)
|
||||
|
||||
**Go configuration with fangs!**
|
||||
|
@ -30,6 +30,7 @@ Many Go projects are built using Viper including:
|
|||
* [Meshery](https://github.com/meshery/meshery)
|
||||
* [Bearer](https://github.com/bearer/bearer)
|
||||
* [Coder](https://github.com/coder/coder)
|
||||
* [Vitess](https://vitess.io/)
|
||||
|
||||
|
||||
## Install
|
||||
|
@ -140,7 +141,7 @@ if err := viper.ReadInConfig(); err != nil {
|
|||
// Config file found and successfully parsed
|
||||
```
|
||||
|
||||
*NOTE [since 1.6]:* You can also have a file without an extension and specify the format programmaticaly. For those configuration files that lie in the home of the user without any extension like `.bashrc`
|
||||
*NOTE [since 1.6]:* You can also have a file without an extension and specify the format programmatically. For those configuration files that lie in the home of the user without any extension like `.bashrc`
|
||||
|
||||
### Writing Config Files
|
||||
|
||||
|
@ -221,6 +222,7 @@ These could be from a command line flag, or from your own application logic.
|
|||
```go
|
||||
viper.Set("Verbose", true)
|
||||
viper.Set("LogFile", LogFile)
|
||||
viper.Set("host.port", 5899) // set subset
|
||||
```
|
||||
|
||||
### Registering and Using Aliases
|
||||
|
@ -416,6 +418,8 @@ in a Key/Value store such as etcd or Consul. These values take precedence over
|
|||
default values, but are overridden by configuration values retrieved from disk,
|
||||
flags, or environment variables.
|
||||
|
||||
Viper supports multiple hosts. To use, pass a list of endpoints separated by `;`. For example `http://127.0.0.1:4001;http://127.0.0.1:4002`.
|
||||
|
||||
Viper uses [crypt](https://github.com/bketelsen/crypt) to retrieve
|
||||
configuration from the K/V store, which means that you can store your
|
||||
configuration values encrypted and have them automatically decrypted if you have
|
||||
|
@ -487,6 +491,15 @@ err := viper.ReadRemoteConfig()
|
|||
|
||||
Of course, you're allowed to use `SecureRemoteProvider` also
|
||||
|
||||
|
||||
#### NATS
|
||||
|
||||
```go
|
||||
viper.AddRemoteProvider("nats", "nats://127.0.0.1:4222", "myapp.config")
|
||||
viper.SetConfigType("json")
|
||||
err := viper.ReadRemoteConfig()
|
||||
```
|
||||
|
||||
### Remote Key/Value Store Example - Encrypted
|
||||
|
||||
```go
|
||||
|
@ -534,24 +547,27 @@ go func(){
|
|||
In Viper, there are a few ways to get a value depending on the value’s type.
|
||||
The following functions and methods exist:
|
||||
|
||||
* `Get(key string) : interface{}`
|
||||
* `Get(key string) : any`
|
||||
* `GetBool(key string) : bool`
|
||||
* `GetFloat64(key string) : float64`
|
||||
* `GetInt(key string) : int`
|
||||
* `GetIntSlice(key string) : []int`
|
||||
* `GetString(key string) : string`
|
||||
* `GetStringMap(key string) : map[string]interface{}`
|
||||
* `GetStringMap(key string) : map[string]any`
|
||||
* `GetStringMapString(key string) : map[string]string`
|
||||
* `GetStringSlice(key string) : []string`
|
||||
* `GetTime(key string) : time.Time`
|
||||
* `GetDuration(key string) : time.Duration`
|
||||
* `IsSet(key string) : bool`
|
||||
* `AllSettings() : map[string]interface{}`
|
||||
* `AllSettings() : map[string]any`
|
||||
|
||||
One important thing to recognize is that each Get function will return a zero
|
||||
value if it’s not found. To check if a given key exists, the `IsSet()` method
|
||||
has been provided.
|
||||
|
||||
The zero value will also be returned if the value is set, but fails to parse
|
||||
as the requested type.
|
||||
|
||||
Example:
|
||||
```go
|
||||
viper.GetString("logfile") // case-insensitive Setting & Getting
|
||||
|
@ -709,8 +725,8 @@ etc.
|
|||
|
||||
There are two methods to do this:
|
||||
|
||||
* `Unmarshal(rawVal interface{}) : error`
|
||||
* `UnmarshalKey(key string, rawVal interface{}) : error`
|
||||
* `Unmarshal(rawVal any) : error`
|
||||
* `UnmarshalKey(key string, rawVal any) : error`
|
||||
|
||||
Example:
|
||||
|
||||
|
@ -735,9 +751,9 @@ you have to change the delimiter:
|
|||
```go
|
||||
v := viper.NewWithOptions(viper.KeyDelimiter("::"))
|
||||
|
||||
v.SetDefault("chart::values", map[string]interface{}{
|
||||
"ingress": map[string]interface{}{
|
||||
"annotations": map[string]interface{}{
|
||||
v.SetDefault("chart::values", map[string]any{
|
||||
"ingress": map[string]any{
|
||||
"annotations": map[string]any{
|
||||
"traefik.frontend.rule.type": "PathPrefix",
|
||||
"traefik.ingress.kubernetes.io/ssl-redirect": "true",
|
||||
},
|
||||
|
@ -746,7 +762,7 @@ v.SetDefault("chart::values", map[string]interface{}{
|
|||
|
||||
type config struct {
|
||||
Chart struct{
|
||||
Values map[string]interface{}
|
||||
Values map[string]any
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -882,3 +898,31 @@ No, you will need to synchronize access to the viper yourself (for example by us
|
|||
## Troubleshooting
|
||||
|
||||
See [TROUBLESHOOTING.md](TROUBLESHOOTING.md).
|
||||
|
||||
## Development
|
||||
|
||||
**For an optimal developer experience, it is recommended to install [Nix](https://nixos.org/download.html) and [direnv](https://direnv.net/docs/installation.html).**
|
||||
|
||||
_Alternatively, install [Go](https://go.dev/dl/) on your computer then run `make deps` to install the rest of the dependencies._
|
||||
|
||||
Run the test suite:
|
||||
|
||||
```shell
|
||||
make test
|
||||
```
|
||||
|
||||
Run linters:
|
||||
|
||||
```shell
|
||||
make lint # pass -j option to run them in parallel
|
||||
```
|
||||
|
||||
Some linter violations can automatically be fixed:
|
||||
|
||||
```shell
|
||||
make fmt
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
The project is licensed under the [MIT License](LICENSE).
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
//go:build viper_logger
|
||||
// +build viper_logger
|
||||
|
||||
package viper
|
||||
|
||||
// WithLogger sets a custom logger.
|
||||
func WithLogger(l Logger) Option {
|
||||
return optionFunc(func(v *Viper) {
|
||||
v.logger = l
|
||||
})
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
//go:build !go1.16 || !finder
|
||||
// +build !go1.16 !finder
|
||||
//go:build !finder
|
||||
|
||||
package viper
|
||||
|
||||
|
@ -44,7 +43,7 @@ func (v *Viper) searchInPath(in string) (filename string) {
|
|||
return ""
|
||||
}
|
||||
|
||||
// Check if file Exists
|
||||
// exists checks if file exists.
|
||||
func exists(fs afero.Fs, path string) (bool, error) {
|
||||
stat, err := fs.Stat(path)
|
||||
if err == nil {
|
|
@ -0,0 +1,38 @@
|
|||
//go:build finder
|
||||
|
||||
package viper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sagikazarmark/locafero"
|
||||
)
|
||||
|
||||
// Search all configPaths for any config file.
|
||||
// Returns the first path that exists (and is a config file).
|
||||
func (v *Viper) findConfigFile() (string, error) {
|
||||
var names []string
|
||||
|
||||
if v.configType != "" {
|
||||
names = locafero.NameWithOptionalExtensions(v.configName, SupportedExts...)
|
||||
} else {
|
||||
names = locafero.NameWithExtensions(v.configName, SupportedExts...)
|
||||
}
|
||||
|
||||
finder := locafero.Finder{
|
||||
Paths: v.configPaths,
|
||||
Names: names,
|
||||
Type: locafero.FileTypeFile,
|
||||
}
|
||||
|
||||
results, err := finder.Find(v.fs)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if len(results) == 0 {
|
||||
return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
|
||||
}
|
||||
|
||||
return results[0], nil
|
||||
}
|
|
@ -30,8 +30,8 @@ func (p pflagValueSet) VisitAll(fn func(flag FlagValue)) {
|
|||
})
|
||||
}
|
||||
|
||||
// pflagValue is a wrapper aroung *pflag.flag
|
||||
// that implements FlagValue
|
||||
// pflagValue is a wrapper around *pflag.flag
|
||||
// that implements FlagValue.
|
||||
type pflagValue struct {
|
||||
flag *pflag.Flag
|
||||
}
|
||||
|
|
|
@ -0,0 +1,255 @@
|
|||
{
|
||||
"nodes": {
|
||||
"devenv": {
|
||||
"inputs": {
|
||||
"flake-compat": "flake-compat",
|
||||
"nix": "nix",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"pre-commit-hooks": "pre-commit-hooks"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1687972261,
|
||||
"narHash": "sha256-+mxvZfwMVoaZYETmuQWqTi/7T9UKoAE+WpdSQkOVJ2g=",
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"rev": "e85df562088573305e55906eaa964341f8cb0d9f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "devenv",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-compat": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1673956053,
|
||||
"narHash": "sha256-4gtG9iQuiKITOjNQQeQIpoIB6b16fm+504Ch3sNKLd8=",
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"rev": "35bb57c0c8d8b62bbfd284272c928ceb64ddbde9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "edolstra",
|
||||
"repo": "flake-compat",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-parts": {
|
||||
"inputs": {
|
||||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1687762428,
|
||||
"narHash": "sha256-DIf7mi45PKo+s8dOYF+UlXHzE0Wl/+k3tXUyAoAnoGE=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "37dd7bb15791c86d55c5121740a1887ab55ee836",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"flake-utils": {
|
||||
"locked": {
|
||||
"lastModified": 1667395993,
|
||||
"narHash": "sha256-nuEHfE/LcWyuSWnS8t12N1wc105Qtau+/OdUAjtQ0rA=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "5aed5285a952e0b949eb3ba02c12fa4fcfef535f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"gitignore": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"pre-commit-hooks",
|
||||
"nixpkgs"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1660459072,
|
||||
"narHash": "sha256-8DFJjXG8zqoONA1vXtgeKXy68KdJL5UaXR8NtVMUbx8=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"rev": "a20de23b925fd8264fd7fad6454652e142fd7f73",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "hercules-ci",
|
||||
"repo": "gitignore.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"lowdown-src": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1633514407,
|
||||
"narHash": "sha256-Dw32tiMjdK9t3ETl5fzGrutQTzh2rufgZV4A/BbxuD4=",
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"rev": "d2c2b44ff6c27b936ec27358a2653caaef8f73b8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "kristapsdz",
|
||||
"repo": "lowdown",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nix": {
|
||||
"inputs": {
|
||||
"lowdown-src": "lowdown-src",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-regression": "nixpkgs-regression"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1676545802,
|
||||
"narHash": "sha256-EK4rZ+Hd5hsvXnzSzk2ikhStJnD63odF7SzsQ8CuSPU=",
|
||||
"owner": "domenkozar",
|
||||
"repo": "nix",
|
||||
"rev": "7c91803598ffbcfe4a55c44ac6d49b2cf07a527f",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "domenkozar",
|
||||
"ref": "relaxed-flakes",
|
||||
"repo": "nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1678875422,
|
||||
"narHash": "sha256-T3o6NcQPwXjxJMn2shz86Chch4ljXgZn746c2caGxd8=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "126f49a01de5b7e35a43fd43f891ecf6d3a51459",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"dir": "lib",
|
||||
"lastModified": 1685564631,
|
||||
"narHash": "sha256-8ywr3AkblY4++3lIVxmrWZFzac7+f32ZEhH/A8pNscI=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4f53efe34b3a8877ac923b9350c874e3dcd5dc0a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"dir": "lib",
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-regression": {
|
||||
"locked": {
|
||||
"lastModified": 1643052045,
|
||||
"narHash": "sha256-uGJ0VXIhWKGXxkeNnq4TvV3CIOkUJ3PAoLZ3HMzNVMw=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "215d4d0fd80ca5163643b03a33fde804a29cc1e2",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs-stable": {
|
||||
"locked": {
|
||||
"lastModified": 1678872516,
|
||||
"narHash": "sha256-/E1YwtMtFAu2KUQKV/1+KFuReYPANM2Rzehk84VxVoc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "9b8e5abb18324c7fe9f07cb100c3cd4a29cda8b8",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-22.11",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1687886075,
|
||||
"narHash": "sha256-PeayJDDDy+uw1Ats4moZnRdL1OFuZm1Tj+KiHlD67+o=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a565059a348422af5af9026b5174dc5c0dcefdae",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"pre-commit-hooks": {
|
||||
"inputs": {
|
||||
"flake-compat": [
|
||||
"devenv",
|
||||
"flake-compat"
|
||||
],
|
||||
"flake-utils": "flake-utils",
|
||||
"gitignore": "gitignore",
|
||||
"nixpkgs": [
|
||||
"devenv",
|
||||
"nixpkgs"
|
||||
],
|
||||
"nixpkgs-stable": "nixpkgs-stable"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1686050334,
|
||||
"narHash": "sha256-R0mczWjDzBpIvM3XXhO908X5e2CQqjyh/gFbwZk/7/Q=",
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"rev": "6881eb2ae5d8a3516e34714e7a90d9d95914c4dc",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "cachix",
|
||||
"repo": "pre-commit-hooks.nix",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"devenv": "devenv",
|
||||
"flake-parts": "flake-parts",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
description = "Viper";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
flake-parts.url = "github:hercules-ci/flake-parts";
|
||||
devenv.url = "github:cachix/devenv";
|
||||
};
|
||||
|
||||
outputs = inputs@{ flake-parts, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
imports = [
|
||||
inputs.devenv.flakeModule
|
||||
];
|
||||
|
||||
systems = [ "x86_64-linux" "x86_64-darwin" "aarch64-darwin" ];
|
||||
|
||||
perSystem = { config, self', inputs', pkgs, system, ... }: rec {
|
||||
devenv.shells = {
|
||||
default = {
|
||||
languages = {
|
||||
go.enable = true;
|
||||
};
|
||||
|
||||
pre-commit.hooks = {
|
||||
nixpkgs-fmt.enable = true;
|
||||
yamllint.enable = true;
|
||||
};
|
||||
|
||||
packages = with pkgs; [
|
||||
gnumake
|
||||
|
||||
golangci-lint
|
||||
yamllint
|
||||
];
|
||||
|
||||
scripts = {
|
||||
versions.exec = ''
|
||||
go version
|
||||
golangci-lint version
|
||||
'';
|
||||
};
|
||||
|
||||
enterShell = ''
|
||||
versions
|
||||
'';
|
||||
|
||||
# https://github.com/cachix/devenv/issues/528#issuecomment-1556108767
|
||||
containers = pkgs.lib.mkForce { };
|
||||
};
|
||||
|
||||
ci = devenv.shells.default;
|
||||
};
|
||||
};
|
||||
};
|
||||
}
|
|
@ -1,65 +0,0 @@
|
|||
//go:build go1.16 && finder
|
||||
// +build go1.16,finder
|
||||
|
||||
package viper
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"io/fs"
|
||||
"path"
|
||||
)
|
||||
|
||||
type finder struct {
|
||||
paths []string
|
||||
fileNames []string
|
||||
extensions []string
|
||||
|
||||
withoutExtension bool
|
||||
}
|
||||
|
||||
func (f finder) Find(fsys fs.FS) (string, error) {
|
||||
for _, searchPath := range f.paths {
|
||||
for _, fileName := range f.fileNames {
|
||||
for _, extension := range f.extensions {
|
||||
filePath := path.Join(searchPath, fileName+"."+extension)
|
||||
|
||||
ok, err := fileExists(fsys, filePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if ok {
|
||||
return filePath, nil
|
||||
}
|
||||
}
|
||||
|
||||
if f.withoutExtension {
|
||||
filePath := path.Join(searchPath, fileName)
|
||||
|
||||
ok, err := fileExists(fsys, filePath)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if ok {
|
||||
return filePath, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func fileExists(fsys fs.FS, filePath string) (bool, error) {
|
||||
fileInfo, err := fs.Stat(fsys, filePath)
|
||||
if err == nil {
|
||||
return !fileInfo.IsDir(), nil
|
||||
}
|
||||
|
||||
if errors.Is(err, fs.ErrNotExist) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
return false, err
|
||||
}
|
|
@ -5,9 +5,9 @@ import (
|
|||
)
|
||||
|
||||
// Decoder decodes the contents of b into v.
|
||||
// It's primarily used for decoding contents of a file into a map[string]interface{}.
|
||||
// It's primarily used for decoding contents of a file into a map[string]any.
|
||||
type Decoder interface {
|
||||
Decode(b []byte, v map[string]interface{}) error
|
||||
Decode(b []byte, v map[string]any) error
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -48,7 +48,7 @@ func (e *DecoderRegistry) RegisterDecoder(format string, enc Decoder) error {
|
|||
}
|
||||
|
||||
// Decode calls the underlying Decoder based on the format.
|
||||
func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]interface{}) error {
|
||||
func (e *DecoderRegistry) Decode(format string, b []byte, v map[string]any) error {
|
||||
e.mu.RLock()
|
||||
decoder, ok := e.decoders[format]
|
||||
e.mu.RUnlock()
|
||||
|
|
|
@ -15,8 +15,8 @@ const keyDelimiter = "_"
|
|||
// (commonly called as dotenv format).
|
||||
type Codec struct{}
|
||||
|
||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
||||
flattened := map[string]interface{}{}
|
||||
func (Codec) Encode(v map[string]any) ([]byte, error) {
|
||||
flattened := map[string]any{}
|
||||
|
||||
flattened = flattenAndMergeMap(flattened, v, "", keyDelimiter)
|
||||
|
||||
|
@ -40,7 +40,7 @@ func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
|||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
||||
func (Codec) Decode(b []byte, v map[string]any) error {
|
||||
var buf bytes.Buffer
|
||||
|
||||
_, err := buf.Write(b)
|
||||
|
|
|
@ -7,27 +7,27 @@ import (
|
|||
)
|
||||
|
||||
// flattenAndMergeMap recursively flattens the given map into a new map
|
||||
// Code is based on the function with the same name in tha main package.
|
||||
// TODO: move it to a common place
|
||||
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
|
||||
// Code is based on the function with the same name in the main package.
|
||||
// TODO: move it to a common place.
|
||||
func flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter string) map[string]any {
|
||||
if shadow != nil && prefix != "" && shadow[prefix] != nil {
|
||||
// prefix is shadowed => nothing more to flatten
|
||||
return shadow
|
||||
}
|
||||
if shadow == nil {
|
||||
shadow = make(map[string]interface{})
|
||||
shadow = make(map[string]any)
|
||||
}
|
||||
|
||||
var m2 map[string]interface{}
|
||||
var m2 map[string]any
|
||||
if prefix != "" {
|
||||
prefix += delimiter
|
||||
}
|
||||
for k, val := range m {
|
||||
fullKey := prefix + k
|
||||
switch val.(type) {
|
||||
case map[string]interface{}:
|
||||
m2 = val.(map[string]interface{})
|
||||
case map[interface{}]interface{}:
|
||||
switch val := val.(type) {
|
||||
case map[string]any:
|
||||
m2 = val
|
||||
case map[any]any:
|
||||
m2 = cast.ToStringMap(val)
|
||||
default:
|
||||
// immediate value
|
||||
|
|
|
@ -5,9 +5,9 @@ import (
|
|||
)
|
||||
|
||||
// Encoder encodes the contents of v into a byte representation.
|
||||
// It's primarily used for encoding a map[string]interface{} into a file format.
|
||||
// It's primarily used for encoding a map[string]any into a file format.
|
||||
type Encoder interface {
|
||||
Encode(v map[string]interface{}) ([]byte, error)
|
||||
Encode(v map[string]any) ([]byte, error)
|
||||
}
|
||||
|
||||
const (
|
||||
|
@ -47,7 +47,7 @@ func (e *EncoderRegistry) RegisterEncoder(format string, enc Encoder) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func (e *EncoderRegistry) Encode(format string, v map[string]interface{}) ([]byte, error) {
|
||||
func (e *EncoderRegistry) Encode(format string, v map[string]any) ([]byte, error) {
|
||||
e.mu.RLock()
|
||||
encoder, ok := e.encoders[format]
|
||||
e.mu.RUnlock()
|
||||
|
|
|
@ -12,7 +12,7 @@ import (
|
|||
// TODO: add printer config to the codec?
|
||||
type Codec struct{}
|
||||
|
||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
||||
func (Codec) Encode(v map[string]any) ([]byte, error) {
|
||||
b, err := json.Marshal(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -35,6 +35,6 @@ func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
|||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
||||
func (Codec) Decode(b []byte, v map[string]any) error {
|
||||
return hcl.Unmarshal(b, &v)
|
||||
}
|
||||
|
|
|
@ -19,11 +19,11 @@ type Codec struct {
|
|||
LoadOptions LoadOptions
|
||||
}
|
||||
|
||||
func (c Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
||||
func (c Codec) Encode(v map[string]any) ([]byte, error) {
|
||||
cfg := ini.Empty()
|
||||
ini.PrettyFormat = false
|
||||
|
||||
flattened := map[string]interface{}{}
|
||||
flattened := map[string]any{}
|
||||
|
||||
flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
|
||||
|
||||
|
@ -62,7 +62,7 @@ func (c Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
|||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c Codec) Decode(b []byte, v map[string]interface{}) error {
|
||||
func (c Codec) Decode(b []byte, v map[string]any) error {
|
||||
cfg := ini.Empty(c.LoadOptions)
|
||||
|
||||
err := cfg.Append(b)
|
||||
|
|
|
@ -15,22 +15,22 @@ import (
|
|||
// In case intermediate keys do not exist, or map to a non-map value,
|
||||
// a new map is created and inserted, and the search continues from there:
|
||||
// the initial map "m" may be modified!
|
||||
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
|
||||
func deepSearch(m map[string]any, path []string) map[string]any {
|
||||
for _, k := range path {
|
||||
m2, ok := m[k]
|
||||
if !ok {
|
||||
// intermediate key does not exist
|
||||
// => create it and continue from there
|
||||
m3 := make(map[string]interface{})
|
||||
m3 := make(map[string]any)
|
||||
m[k] = m3
|
||||
m = m3
|
||||
continue
|
||||
}
|
||||
m3, ok := m2.(map[string]interface{})
|
||||
m3, ok := m2.(map[string]any)
|
||||
if !ok {
|
||||
// intermediate key is a value
|
||||
// => replace with a new map
|
||||
m3 = make(map[string]interface{})
|
||||
m3 = make(map[string]any)
|
||||
m[k] = m3
|
||||
}
|
||||
// continue search from here
|
||||
|
@ -40,27 +40,27 @@ func deepSearch(m map[string]interface{}, path []string) map[string]interface{}
|
|||
}
|
||||
|
||||
// flattenAndMergeMap recursively flattens the given map into a new map
|
||||
// Code is based on the function with the same name in tha main package.
|
||||
// TODO: move it to a common place
|
||||
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
|
||||
// Code is based on the function with the same name in the main package.
|
||||
// TODO: move it to a common place.
|
||||
func flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter string) map[string]any {
|
||||
if shadow != nil && prefix != "" && shadow[prefix] != nil {
|
||||
// prefix is shadowed => nothing more to flatten
|
||||
return shadow
|
||||
}
|
||||
if shadow == nil {
|
||||
shadow = make(map[string]interface{})
|
||||
shadow = make(map[string]any)
|
||||
}
|
||||
|
||||
var m2 map[string]interface{}
|
||||
var m2 map[string]any
|
||||
if prefix != "" {
|
||||
prefix += delimiter
|
||||
}
|
||||
for k, val := range m {
|
||||
fullKey := prefix + k
|
||||
switch val.(type) {
|
||||
case map[string]interface{}:
|
||||
m2 = val.(map[string]interface{})
|
||||
case map[interface{}]interface{}:
|
||||
switch val := val.(type) {
|
||||
case map[string]any:
|
||||
m2 = val
|
||||
case map[any]any:
|
||||
m2 = cast.ToStringMap(val)
|
||||
default:
|
||||
// immediate value
|
||||
|
|
|
@ -20,12 +20,12 @@ type Codec struct {
|
|||
Properties *properties.Properties
|
||||
}
|
||||
|
||||
func (c *Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
||||
func (c *Codec) Encode(v map[string]any) ([]byte, error) {
|
||||
if c.Properties == nil {
|
||||
c.Properties = properties.NewProperties()
|
||||
}
|
||||
|
||||
flattened := map[string]interface{}{}
|
||||
flattened := map[string]any{}
|
||||
|
||||
flattened = flattenAndMergeMap(flattened, v, "", c.keyDelimiter())
|
||||
|
||||
|
@ -54,7 +54,7 @@ func (c *Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
|||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
func (c *Codec) Decode(b []byte, v map[string]interface{}) error {
|
||||
func (c *Codec) Decode(b []byte, v map[string]any) error {
|
||||
var err error
|
||||
c.Properties, err = properties.Load(b, properties.UTF8)
|
||||
if err != nil {
|
||||
|
|
|
@ -15,22 +15,22 @@ import (
|
|||
// In case intermediate keys do not exist, or map to a non-map value,
|
||||
// a new map is created and inserted, and the search continues from there:
|
||||
// the initial map "m" may be modified!
|
||||
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
|
||||
func deepSearch(m map[string]any, path []string) map[string]any {
|
||||
for _, k := range path {
|
||||
m2, ok := m[k]
|
||||
if !ok {
|
||||
// intermediate key does not exist
|
||||
// => create it and continue from there
|
||||
m3 := make(map[string]interface{})
|
||||
m3 := make(map[string]any)
|
||||
m[k] = m3
|
||||
m = m3
|
||||
continue
|
||||
}
|
||||
m3, ok := m2.(map[string]interface{})
|
||||
m3, ok := m2.(map[string]any)
|
||||
if !ok {
|
||||
// intermediate key is a value
|
||||
// => replace with a new map
|
||||
m3 = make(map[string]interface{})
|
||||
m3 = make(map[string]any)
|
||||
m[k] = m3
|
||||
}
|
||||
// continue search from here
|
||||
|
@ -40,27 +40,27 @@ func deepSearch(m map[string]interface{}, path []string) map[string]interface{}
|
|||
}
|
||||
|
||||
// flattenAndMergeMap recursively flattens the given map into a new map
|
||||
// Code is based on the function with the same name in tha main package.
|
||||
// TODO: move it to a common place
|
||||
func flattenAndMergeMap(shadow map[string]interface{}, m map[string]interface{}, prefix string, delimiter string) map[string]interface{} {
|
||||
// Code is based on the function with the same name in the main package.
|
||||
// TODO: move it to a common place.
|
||||
func flattenAndMergeMap(shadow, m map[string]any, prefix, delimiter string) map[string]any {
|
||||
if shadow != nil && prefix != "" && shadow[prefix] != nil {
|
||||
// prefix is shadowed => nothing more to flatten
|
||||
return shadow
|
||||
}
|
||||
if shadow == nil {
|
||||
shadow = make(map[string]interface{})
|
||||
shadow = make(map[string]any)
|
||||
}
|
||||
|
||||
var m2 map[string]interface{}
|
||||
var m2 map[string]any
|
||||
if prefix != "" {
|
||||
prefix += delimiter
|
||||
}
|
||||
for k, val := range m {
|
||||
fullKey := prefix + k
|
||||
switch val.(type) {
|
||||
case map[string]interface{}:
|
||||
m2 = val.(map[string]interface{})
|
||||
case map[interface{}]interface{}:
|
||||
switch val := val.(type) {
|
||||
case map[string]any:
|
||||
m2 = val
|
||||
case map[any]any:
|
||||
m2 = cast.ToStringMap(val)
|
||||
default:
|
||||
// immediate value
|
||||
|
|
|
@ -7,11 +7,11 @@ import (
|
|||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for JSON encoding.
|
||||
type Codec struct{}
|
||||
|
||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
||||
func (Codec) Encode(v map[string]any) ([]byte, error) {
|
||||
// TODO: expose prefix and indent in the Codec as setting?
|
||||
return json.MarshalIndent(v, "", " ")
|
||||
}
|
||||
|
||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
||||
func (Codec) Decode(b []byte, v map[string]any) error {
|
||||
return json.Unmarshal(b, &v)
|
||||
}
|
||||
|
|
|
@ -7,10 +7,10 @@ import (
|
|||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for TOML encoding.
|
||||
type Codec struct{}
|
||||
|
||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
||||
func (Codec) Encode(v map[string]any) ([]byte, error) {
|
||||
return toml.Marshal(v)
|
||||
}
|
||||
|
||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
||||
func (Codec) Decode(b []byte, v map[string]any) error {
|
||||
return toml.Unmarshal(b, &v)
|
||||
}
|
||||
|
|
|
@ -5,10 +5,10 @@ import "gopkg.in/yaml.v3"
|
|||
// Codec implements the encoding.Encoder and encoding.Decoder interfaces for YAML encoding.
|
||||
type Codec struct{}
|
||||
|
||||
func (Codec) Encode(v map[string]interface{}) ([]byte, error) {
|
||||
func (Codec) Encode(v map[string]any) ([]byte, error) {
|
||||
return yaml.Marshal(v)
|
||||
}
|
||||
|
||||
func (Codec) Decode(b []byte, v map[string]interface{}) error {
|
||||
func (Codec) Decode(b []byte, v map[string]any) error {
|
||||
return yaml.Unmarshal(b, &v)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
//go:build viper_bind_struct
|
||||
|
||||
package features
|
||||
|
||||
const BindStruct = true
|
5
vendor/github.com/spf13/viper/internal/features/bind_struct_default.go
generated
vendored
Normal file
5
vendor/github.com/spf13/viper/internal/features/bind_struct_default.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
//go:build !viper_bind_struct
|
||||
|
||||
package features
|
||||
|
||||
const BindStruct = false
|
|
@ -1,77 +1,68 @@
|
|||
package viper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"context"
|
||||
|
||||
jww "github.com/spf13/jwalterweatherman"
|
||||
slog "github.com/sagikazarmark/slog-shim"
|
||||
)
|
||||
|
||||
// Logger is a unified interface for various logging use cases and practices, including:
|
||||
// - leveled logging
|
||||
// - structured logging
|
||||
//
|
||||
// Deprecated: use `log/slog` instead.
|
||||
type Logger interface {
|
||||
// Trace logs a Trace event.
|
||||
//
|
||||
// Even more fine-grained information than Debug events.
|
||||
// Loggers not supporting this level should fall back to Debug.
|
||||
Trace(msg string, keyvals ...interface{})
|
||||
Trace(msg string, keyvals ...any)
|
||||
|
||||
// Debug logs a Debug event.
|
||||
//
|
||||
// A verbose series of information events.
|
||||
// They are useful when debugging the system.
|
||||
Debug(msg string, keyvals ...interface{})
|
||||
Debug(msg string, keyvals ...any)
|
||||
|
||||
// Info logs an Info event.
|
||||
//
|
||||
// General information about what's happening inside the system.
|
||||
Info(msg string, keyvals ...interface{})
|
||||
Info(msg string, keyvals ...any)
|
||||
|
||||
// Warn logs a Warn(ing) event.
|
||||
//
|
||||
// Non-critical events that should be looked at.
|
||||
Warn(msg string, keyvals ...interface{})
|
||||
Warn(msg string, keyvals ...any)
|
||||
|
||||
// Error logs an Error event.
|
||||
//
|
||||
// Critical events that require immediate attention.
|
||||
// Loggers commonly provide Fatal and Panic levels above Error level,
|
||||
// but exiting and panicing is out of scope for a logging library.
|
||||
Error(msg string, keyvals ...interface{})
|
||||
// but exiting and panicking is out of scope for a logging library.
|
||||
Error(msg string, keyvals ...any)
|
||||
}
|
||||
|
||||
type jwwLogger struct{}
|
||||
|
||||
func (jwwLogger) Trace(msg string, keyvals ...interface{}) {
|
||||
jww.TRACE.Printf(jwwLogMessage(msg, keyvals...))
|
||||
// WithLogger sets a custom logger.
|
||||
func WithLogger(l *slog.Logger) Option {
|
||||
return optionFunc(func(v *Viper) {
|
||||
v.logger = l
|
||||
})
|
||||
}
|
||||
|
||||
func (jwwLogger) Debug(msg string, keyvals ...interface{}) {
|
||||
jww.DEBUG.Printf(jwwLogMessage(msg, keyvals...))
|
||||
type discardHandler struct{}
|
||||
|
||||
func (n *discardHandler) Enabled(_ context.Context, _ slog.Level) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (jwwLogger) Info(msg string, keyvals ...interface{}) {
|
||||
jww.INFO.Printf(jwwLogMessage(msg, keyvals...))
|
||||
func (n *discardHandler) Handle(_ context.Context, _ slog.Record) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (jwwLogger) Warn(msg string, keyvals ...interface{}) {
|
||||
jww.WARN.Printf(jwwLogMessage(msg, keyvals...))
|
||||
func (n *discardHandler) WithAttrs(_ []slog.Attr) slog.Handler {
|
||||
return n
|
||||
}
|
||||
|
||||
func (jwwLogger) Error(msg string, keyvals ...interface{}) {
|
||||
jww.ERROR.Printf(jwwLogMessage(msg, keyvals...))
|
||||
}
|
||||
|
||||
func jwwLogMessage(msg string, keyvals ...interface{}) string {
|
||||
out := msg
|
||||
|
||||
if len(keyvals) > 0 && len(keyvals)%2 == 1 {
|
||||
keyvals = append(keyvals, nil)
|
||||
}
|
||||
|
||||
for i := 0; i <= len(keyvals)-2; i += 2 {
|
||||
out = fmt.Sprintf("%s %v=%v", out, keyvals[i], keyvals[i+1])
|
||||
}
|
||||
|
||||
return out
|
||||
func (n *discardHandler) WithGroup(_ string) slog.Handler {
|
||||
return n
|
||||
}
|
||||
|
|
|
@ -18,6 +18,7 @@ import (
|
|||
"strings"
|
||||
"unicode"
|
||||
|
||||
slog "github.com/sagikazarmark/slog-shim"
|
||||
"github.com/spf13/cast"
|
||||
)
|
||||
|
||||
|
@ -38,11 +39,11 @@ func (pe ConfigParseError) Unwrap() error {
|
|||
|
||||
// toCaseInsensitiveValue checks if the value is a map;
|
||||
// if so, create a copy and lower-case the keys recursively.
|
||||
func toCaseInsensitiveValue(value interface{}) interface{} {
|
||||
func toCaseInsensitiveValue(value any) any {
|
||||
switch v := value.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
case map[any]any:
|
||||
value = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
value = copyAndInsensitiviseMap(v)
|
||||
}
|
||||
|
||||
|
@ -51,15 +52,15 @@ func toCaseInsensitiveValue(value interface{}) interface{} {
|
|||
|
||||
// copyAndInsensitiviseMap behaves like insensitiviseMap, but creates a copy of
|
||||
// any map it makes case insensitive.
|
||||
func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
|
||||
nm := make(map[string]interface{})
|
||||
func copyAndInsensitiviseMap(m map[string]any) map[string]any {
|
||||
nm := make(map[string]any)
|
||||
|
||||
for key, val := range m {
|
||||
lkey := strings.ToLower(key)
|
||||
switch v := val.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
case map[any]any:
|
||||
nm[lkey] = copyAndInsensitiviseMap(cast.ToStringMap(v))
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
nm[lkey] = copyAndInsensitiviseMap(v)
|
||||
default:
|
||||
nm[lkey] = v
|
||||
|
@ -69,23 +70,23 @@ func copyAndInsensitiviseMap(m map[string]interface{}) map[string]interface{} {
|
|||
return nm
|
||||
}
|
||||
|
||||
func insensitiviseVal(val interface{}) interface{} {
|
||||
switch val.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
func insensitiviseVal(val any) any {
|
||||
switch v := val.(type) {
|
||||
case map[any]any:
|
||||
// nested map: cast and recursively insensitivise
|
||||
val = cast.ToStringMap(val)
|
||||
insensitiviseMap(val.(map[string]interface{}))
|
||||
case map[string]interface{}:
|
||||
insensitiviseMap(val.(map[string]any))
|
||||
case map[string]any:
|
||||
// nested map: recursively insensitivise
|
||||
insensitiviseMap(val.(map[string]interface{}))
|
||||
case []interface{}:
|
||||
insensitiviseMap(v)
|
||||
case []any:
|
||||
// nested array: recursively insensitivise
|
||||
insensitiveArray(val.([]interface{}))
|
||||
insensitiveArray(v)
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func insensitiviseMap(m map[string]interface{}) {
|
||||
func insensitiviseMap(m map[string]any) {
|
||||
for key, val := range m {
|
||||
val = insensitiviseVal(val)
|
||||
lower := strings.ToLower(key)
|
||||
|
@ -98,13 +99,13 @@ func insensitiviseMap(m map[string]interface{}) {
|
|||
}
|
||||
}
|
||||
|
||||
func insensitiveArray(a []interface{}) {
|
||||
func insensitiveArray(a []any) {
|
||||
for i, val := range a {
|
||||
a[i] = insensitiviseVal(val)
|
||||
}
|
||||
}
|
||||
|
||||
func absPathify(logger Logger, inPath string) string {
|
||||
func absPathify(logger *slog.Logger, inPath string) string {
|
||||
logger.Info("trying to resolve absolute path", "path", inPath)
|
||||
|
||||
if inPath == "$HOME" || strings.HasPrefix(inPath, "$HOME"+string(os.PathSeparator)) {
|
||||
|
@ -155,7 +156,7 @@ func safeMul(a, b uint) uint {
|
|||
return c
|
||||
}
|
||||
|
||||
// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes
|
||||
// parseSizeInBytes converts strings like 1GB or 12 mb into an unsigned integer number of bytes.
|
||||
func parseSizeInBytes(sizeStr string) uint {
|
||||
sizeStr = strings.TrimSpace(sizeStr)
|
||||
lastChar := len(sizeStr) - 1
|
||||
|
@ -197,22 +198,22 @@ func parseSizeInBytes(sizeStr string) uint {
|
|||
// In case intermediate keys do not exist, or map to a non-map value,
|
||||
// a new map is created and inserted, and the search continues from there:
|
||||
// the initial map "m" may be modified!
|
||||
func deepSearch(m map[string]interface{}, path []string) map[string]interface{} {
|
||||
func deepSearch(m map[string]any, path []string) map[string]any {
|
||||
for _, k := range path {
|
||||
m2, ok := m[k]
|
||||
if !ok {
|
||||
// intermediate key does not exist
|
||||
// => create it and continue from there
|
||||
m3 := make(map[string]interface{})
|
||||
m3 := make(map[string]any)
|
||||
m[k] = m3
|
||||
m = m3
|
||||
continue
|
||||
}
|
||||
m3, ok := m2.(map[string]interface{})
|
||||
m3, ok := m2.(map[string]any)
|
||||
if !ok {
|
||||
// intermediate key is a value
|
||||
// => replace with a new map
|
||||
m3 = make(map[string]interface{})
|
||||
m3 = make(map[string]any)
|
||||
m[k] = m3
|
||||
}
|
||||
// continue search from here
|
||||
|
|
|
@ -35,6 +35,7 @@ import (
|
|||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
slog "github.com/sagikazarmark/slog-shim"
|
||||
"github.com/spf13/afero"
|
||||
"github.com/spf13/cast"
|
||||
"github.com/spf13/pflag"
|
||||
|
@ -47,6 +48,7 @@ import (
|
|||
"github.com/spf13/viper/internal/encoding/json"
|
||||
"github.com/spf13/viper/internal/encoding/toml"
|
||||
"github.com/spf13/viper/internal/encoding/yaml"
|
||||
"github.com/spf13/viper/internal/features"
|
||||
)
|
||||
|
||||
// ConfigMarshalError happens when failing to marshal the configuration.
|
||||
|
@ -76,7 +78,7 @@ type remoteConfigFactory interface {
|
|||
WatchChannel(rp RemoteProvider) (<-chan *RemoteResponse, chan bool)
|
||||
}
|
||||
|
||||
// RemoteConfig is optional, see the remote package
|
||||
// RemoteConfig is optional, see the remote package.
|
||||
var RemoteConfig remoteConfigFactory
|
||||
|
||||
// UnsupportedConfigError denotes encountering an unsupported
|
||||
|
@ -101,7 +103,7 @@ func (str UnsupportedRemoteProviderError) Error() string {
|
|||
// pull the configuration from the remote provider.
|
||||
type RemoteConfigError string
|
||||
|
||||
// Error returns the formatted remote provider error
|
||||
// Error returns the formatted remote provider error.
|
||||
func (rce RemoteConfigError) Error() string {
|
||||
return fmt.Sprintf("Remote Configurations Error: %s", string(rce))
|
||||
}
|
||||
|
@ -125,7 +127,7 @@ func (faee ConfigFileAlreadyExistsError) Error() string {
|
|||
}
|
||||
|
||||
// A DecoderConfigOption can be passed to viper.Unmarshal to configure
|
||||
// mapstructure.DecoderConfig options
|
||||
// mapstructure.DecoderConfig options.
|
||||
type DecoderConfigOption func(*mapstructure.DecoderConfig)
|
||||
|
||||
// DecodeHook returns a DecoderConfigOption which overrides the default
|
||||
|
@ -206,10 +208,10 @@ type Viper struct {
|
|||
allowEmptyEnv bool
|
||||
|
||||
parents []string
|
||||
config map[string]interface{}
|
||||
override map[string]interface{}
|
||||
defaults map[string]interface{}
|
||||
kvstore map[string]interface{}
|
||||
config map[string]any
|
||||
override map[string]any
|
||||
defaults map[string]any
|
||||
kvstore map[string]any
|
||||
pflags map[string]FlagValue
|
||||
env map[string][]string
|
||||
aliases map[string]string
|
||||
|
@ -217,7 +219,7 @@ type Viper struct {
|
|||
|
||||
onConfigChange func(fsnotify.Event)
|
||||
|
||||
logger Logger
|
||||
logger *slog.Logger
|
||||
|
||||
// TODO: should probably be protected with a mutex
|
||||
encoderRegistry *encoding.EncoderRegistry
|
||||
|
@ -231,16 +233,16 @@ func New() *Viper {
|
|||
v.configName = "config"
|
||||
v.configPermissions = os.FileMode(0o644)
|
||||
v.fs = afero.NewOsFs()
|
||||
v.config = make(map[string]interface{})
|
||||
v.config = make(map[string]any)
|
||||
v.parents = []string{}
|
||||
v.override = make(map[string]interface{})
|
||||
v.defaults = make(map[string]interface{})
|
||||
v.kvstore = make(map[string]interface{})
|
||||
v.override = make(map[string]any)
|
||||
v.defaults = make(map[string]any)
|
||||
v.kvstore = make(map[string]any)
|
||||
v.pflags = make(map[string]FlagValue)
|
||||
v.env = make(map[string][]string)
|
||||
v.aliases = make(map[string]string)
|
||||
v.typeByDefValue = false
|
||||
v.logger = jwwLogger{}
|
||||
v.logger = slog.New(&discardHandler{})
|
||||
|
||||
v.resetEncoding()
|
||||
|
||||
|
@ -301,10 +303,10 @@ func NewWithOptions(opts ...Option) *Viper {
|
|||
func Reset() {
|
||||
v = New()
|
||||
SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
|
||||
SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
|
||||
SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"}
|
||||
}
|
||||
|
||||
// TODO: make this lazy initialization instead
|
||||
// TODO: make this lazy initialization instead.
|
||||
func (v *Viper) resetEncoding() {
|
||||
encoderRegistry := encoding.NewEncoderRegistry()
|
||||
decoderRegistry := encoding.NewDecoderRegistry()
|
||||
|
@ -420,7 +422,7 @@ type RemoteProvider interface {
|
|||
var SupportedExts = []string{"json", "toml", "yaml", "yml", "properties", "props", "prop", "hcl", "tfvars", "dotenv", "env", "ini"}
|
||||
|
||||
// SupportedRemoteProviders are universally supported remote providers.
|
||||
var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore"}
|
||||
var SupportedRemoteProviders = []string{"etcd", "etcd3", "consul", "firestore", "nats"}
|
||||
|
||||
// OnConfigChange sets the event handler that is called when a config file changes.
|
||||
func OnConfigChange(run func(in fsnotify.Event)) { v.OnConfigChange(run) }
|
||||
|
@ -438,7 +440,7 @@ func (v *Viper) WatchConfig() {
|
|||
initWG := sync.WaitGroup{}
|
||||
initWG.Add(1)
|
||||
go func() {
|
||||
watcher, err := newWatcher()
|
||||
watcher, err := fsnotify.NewWatcher()
|
||||
if err != nil {
|
||||
v.logger.Error(fmt.Sprintf("failed to create watcher: %s", err))
|
||||
os.Exit(1)
|
||||
|
@ -523,6 +525,12 @@ func (v *Viper) SetEnvPrefix(in string) {
|
|||
}
|
||||
}
|
||||
|
||||
func GetEnvPrefix() string { return v.GetEnvPrefix() }
|
||||
|
||||
func (v *Viper) GetEnvPrefix() string {
|
||||
return v.envPrefix
|
||||
}
|
||||
|
||||
func (v *Viper) mergeWithEnvPrefix(in string) string {
|
||||
if v.envPrefix != "" {
|
||||
return strings.ToUpper(v.envPrefix + "_" + in)
|
||||
|
@ -578,12 +586,12 @@ func (v *Viper) AddConfigPath(in string) {
|
|||
|
||||
// AddRemoteProvider adds a remote configuration source.
|
||||
// Remote Providers are searched in the order they are added.
|
||||
// provider is a string value: "etcd", "etcd3", "consul" or "firestore" are currently supported.
|
||||
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
|
||||
// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported.
|
||||
// endpoint is the url. etcd requires http://ip:port, consul requires ip:port, nats requires nats://ip:port
|
||||
// path is the path in the k/v store to retrieve configuration
|
||||
// To retrieve a config file called myapp.json from /configs/myapp.json
|
||||
// you should set path to /configs and set config name (SetConfigName()) to
|
||||
// "myapp"
|
||||
// "myapp".
|
||||
func AddRemoteProvider(provider, endpoint, path string) error {
|
||||
return v.AddRemoteProvider(provider, endpoint, path)
|
||||
}
|
||||
|
@ -609,14 +617,14 @@ func (v *Viper) AddRemoteProvider(provider, endpoint, path string) error {
|
|||
|
||||
// AddSecureRemoteProvider adds a remote configuration source.
|
||||
// Secure Remote Providers are searched in the order they are added.
|
||||
// provider is a string value: "etcd", "etcd3", "consul" or "firestore" are currently supported.
|
||||
// provider is a string value: "etcd", "etcd3", "consul", "firestore" or "nats" are currently supported.
|
||||
// endpoint is the url. etcd requires http://ip:port consul requires ip:port
|
||||
// secretkeyring is the filepath to your openpgp secret keyring. e.g. /etc/secrets/myring.gpg
|
||||
// path is the path in the k/v store to retrieve configuration
|
||||
// To retrieve a config file called myapp.json from /configs/myapp.json
|
||||
// you should set path to /configs and set config name (SetConfigName()) to
|
||||
// "myapp"
|
||||
// Secure Remote Providers are implemented with github.com/bketelsen/crypt
|
||||
// "myapp".
|
||||
// Secure Remote Providers are implemented with github.com/bketelsen/crypt.
|
||||
func AddSecureRemoteProvider(provider, endpoint, path, secretkeyring string) error {
|
||||
return v.AddSecureRemoteProvider(provider, endpoint, path, secretkeyring)
|
||||
}
|
||||
|
@ -653,7 +661,7 @@ func (v *Viper) providerPathExists(p *defaultRemoteProvider) bool {
|
|||
// searchMap recursively searches for a value for path in source map.
|
||||
// Returns nil if not found.
|
||||
// Note: This assumes that the path entries and map keys are lower cased.
|
||||
func (v *Viper) searchMap(source map[string]interface{}, path []string) interface{} {
|
||||
func (v *Viper) searchMap(source map[string]any, path []string) any {
|
||||
if len(path) == 0 {
|
||||
return source
|
||||
}
|
||||
|
@ -666,13 +674,13 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac
|
|||
}
|
||||
|
||||
// Nested case
|
||||
switch next.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
switch next := next.(type) {
|
||||
case map[any]any:
|
||||
return v.searchMap(cast.ToStringMap(next), path[1:])
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
// Type assertion is safe here since it is only reached
|
||||
// if the type of `next` is the same as the type being asserted
|
||||
return v.searchMap(next.(map[string]interface{}), path[1:])
|
||||
return v.searchMap(next, path[1:])
|
||||
default:
|
||||
// got a value but nested key expected, return "nil" for not found
|
||||
return nil
|
||||
|
@ -692,7 +700,7 @@ func (v *Viper) searchMap(source map[string]interface{}, path []string) interfac
|
|||
// in their keys).
|
||||
//
|
||||
// Note: This assumes that the path entries and map keys are lower cased.
|
||||
func (v *Viper) searchIndexableWithPathPrefixes(source interface{}, path []string) interface{} {
|
||||
func (v *Viper) searchIndexableWithPathPrefixes(source any, path []string) any {
|
||||
if len(path) == 0 {
|
||||
return source
|
||||
}
|
||||
|
@ -701,11 +709,11 @@ func (v *Viper) searchIndexableWithPathPrefixes(source interface{}, path []strin
|
|||
for i := len(path); i > 0; i-- {
|
||||
prefixKey := strings.ToLower(strings.Join(path[0:i], v.keyDelim))
|
||||
|
||||
var val interface{}
|
||||
var val any
|
||||
switch sourceIndexable := source.(type) {
|
||||
case []interface{}:
|
||||
case []any:
|
||||
val = v.searchSliceWithPathPrefixes(sourceIndexable, prefixKey, i, path)
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
val = v.searchMapWithPathPrefixes(sourceIndexable, prefixKey, i, path)
|
||||
}
|
||||
if val != nil {
|
||||
|
@ -722,11 +730,11 @@ func (v *Viper) searchIndexableWithPathPrefixes(source interface{}, path []strin
|
|||
// This function is part of the searchIndexableWithPathPrefixes recurring search and
|
||||
// should not be called directly from functions other than searchIndexableWithPathPrefixes.
|
||||
func (v *Viper) searchSliceWithPathPrefixes(
|
||||
sourceSlice []interface{},
|
||||
sourceSlice []any,
|
||||
prefixKey string,
|
||||
pathIndex int,
|
||||
path []string,
|
||||
) interface{} {
|
||||
) any {
|
||||
// if the prefixKey is not a number or it is out of bounds of the slice
|
||||
index, err := strconv.Atoi(prefixKey)
|
||||
if err != nil || len(sourceSlice) <= index {
|
||||
|
@ -741,9 +749,9 @@ func (v *Viper) searchSliceWithPathPrefixes(
|
|||
}
|
||||
|
||||
switch n := next.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
case map[any]any:
|
||||
return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:])
|
||||
case map[string]interface{}, []interface{}:
|
||||
case map[string]any, []any:
|
||||
return v.searchIndexableWithPathPrefixes(n, path[pathIndex:])
|
||||
default:
|
||||
// got a value but nested key expected, do nothing and look for next prefix
|
||||
|
@ -758,11 +766,11 @@ func (v *Viper) searchSliceWithPathPrefixes(
|
|||
// This function is part of the searchIndexableWithPathPrefixes recurring search and
|
||||
// should not be called directly from functions other than searchIndexableWithPathPrefixes.
|
||||
func (v *Viper) searchMapWithPathPrefixes(
|
||||
sourceMap map[string]interface{},
|
||||
sourceMap map[string]any,
|
||||
prefixKey string,
|
||||
pathIndex int,
|
||||
path []string,
|
||||
) interface{} {
|
||||
) any {
|
||||
next, ok := sourceMap[prefixKey]
|
||||
if !ok {
|
||||
return nil
|
||||
|
@ -775,9 +783,9 @@ func (v *Viper) searchMapWithPathPrefixes(
|
|||
|
||||
// Nested case
|
||||
switch n := next.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
case map[any]any:
|
||||
return v.searchIndexableWithPathPrefixes(cast.ToStringMap(n), path[pathIndex:])
|
||||
case map[string]interface{}, []interface{}:
|
||||
case map[string]any, []any:
|
||||
return v.searchIndexableWithPathPrefixes(n, path[pathIndex:])
|
||||
default:
|
||||
// got a value but nested key expected, do nothing and look for next prefix
|
||||
|
@ -792,8 +800,8 @@ func (v *Viper) searchMapWithPathPrefixes(
|
|||
// e.g., if "foo.bar" has a value in the given map, it “shadows”
|
||||
//
|
||||
// "foo.bar.baz" in a lower-priority map
|
||||
func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{}) string {
|
||||
var parentVal interface{}
|
||||
func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]any) string {
|
||||
var parentVal any
|
||||
for i := 1; i < len(path); i++ {
|
||||
parentVal = v.searchMap(m, path[0:i])
|
||||
if parentVal == nil {
|
||||
|
@ -801,9 +809,9 @@ func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{})
|
|||
return ""
|
||||
}
|
||||
switch parentVal.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
case map[any]any:
|
||||
continue
|
||||
case map[string]interface{}:
|
||||
case map[string]any:
|
||||
continue
|
||||
default:
|
||||
// parentVal is a regular value which shadows "path"
|
||||
|
@ -818,12 +826,14 @@ func (v *Viper) isPathShadowedInDeepMap(path []string, m map[string]interface{})
|
|||
// e.g., if "foo.bar" has a value in the given map, it “shadows”
|
||||
//
|
||||
// "foo.bar.baz" in a lower-priority map
|
||||
func (v *Viper) isPathShadowedInFlatMap(path []string, mi interface{}) string {
|
||||
func (v *Viper) isPathShadowedInFlatMap(path []string, mi any) string {
|
||||
// unify input map
|
||||
var m map[string]interface{}
|
||||
switch mi.(type) {
|
||||
case map[string]string, map[string]FlagValue:
|
||||
m = cast.ToStringMap(mi)
|
||||
switch miv := mi.(type) {
|
||||
case map[string]string:
|
||||
m = castMapStringToMapInterface(miv)
|
||||
case map[string]FlagValue:
|
||||
m = castMapFlagToMapInterface(miv)
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
|
@ -887,9 +897,9 @@ func GetViper() *Viper {
|
|||
// override, flag, env, config file, key/value store, default
|
||||
//
|
||||
// Get returns an interface. For a specific value use one of the Get____ methods.
|
||||
func Get(key string) interface{} { return v.Get(key) }
|
||||
func Get(key string) any { return v.Get(key) }
|
||||
|
||||
func (v *Viper) Get(key string) interface{} {
|
||||
func (v *Viper) Get(key string) any {
|
||||
lcaseKey := strings.ToLower(key)
|
||||
val := v.find(lcaseKey, true)
|
||||
if val == nil {
|
||||
|
@ -950,7 +960,8 @@ func (v *Viper) Sub(key string) *Viper {
|
|||
}
|
||||
|
||||
if reflect.TypeOf(data).Kind() == reflect.Map {
|
||||
subv.parents = append(v.parents, strings.ToLower(key))
|
||||
subv.parents = append([]string(nil), v.parents...)
|
||||
subv.parents = append(subv.parents, strings.ToLower(key))
|
||||
subv.automaticEnvApplied = v.automaticEnvApplied
|
||||
subv.envPrefix = v.envPrefix
|
||||
subv.envKeyReplacer = v.envKeyReplacer
|
||||
|
@ -1059,9 +1070,9 @@ func (v *Viper) GetStringSlice(key string) []string {
|
|||
}
|
||||
|
||||
// GetStringMap returns the value associated with the key as a map of interfaces.
|
||||
func GetStringMap(key string) map[string]interface{} { return v.GetStringMap(key) }
|
||||
func GetStringMap(key string) map[string]any { return v.GetStringMap(key) }
|
||||
|
||||
func (v *Viper) GetStringMap(key string) map[string]interface{} {
|
||||
func (v *Viper) GetStringMap(key string) map[string]any {
|
||||
return cast.ToStringMap(v.Get(key))
|
||||
}
|
||||
|
||||
|
@ -1089,27 +1100,58 @@ func (v *Viper) GetSizeInBytes(key string) uint {
|
|||
}
|
||||
|
||||
// UnmarshalKey takes a single key and unmarshals it into a Struct.
|
||||
func UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
|
||||
func UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error {
|
||||
return v.UnmarshalKey(key, rawVal, opts...)
|
||||
}
|
||||
|
||||
func (v *Viper) UnmarshalKey(key string, rawVal interface{}, opts ...DecoderConfigOption) error {
|
||||
func (v *Viper) UnmarshalKey(key string, rawVal any, opts ...DecoderConfigOption) error {
|
||||
return decode(v.Get(key), defaultDecoderConfig(rawVal, opts...))
|
||||
}
|
||||
|
||||
// Unmarshal unmarshals the config into a Struct. Make sure that the tags
|
||||
// on the fields of the structure are properly set.
|
||||
func Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
|
||||
func Unmarshal(rawVal any, opts ...DecoderConfigOption) error {
|
||||
return v.Unmarshal(rawVal, opts...)
|
||||
}
|
||||
|
||||
func (v *Viper) Unmarshal(rawVal interface{}, opts ...DecoderConfigOption) error {
|
||||
return decode(v.AllSettings(), defaultDecoderConfig(rawVal, opts...))
|
||||
func (v *Viper) Unmarshal(rawVal any, opts ...DecoderConfigOption) error {
|
||||
keys := v.AllKeys()
|
||||
|
||||
if features.BindStruct {
|
||||
// TODO: make this optional?
|
||||
structKeys, err := v.decodeStructKeys(rawVal, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keys = append(keys, structKeys...)
|
||||
}
|
||||
|
||||
// TODO: struct keys should be enough?
|
||||
return decode(v.getSettings(keys), defaultDecoderConfig(rawVal, opts...))
|
||||
}
|
||||
|
||||
func (v *Viper) decodeStructKeys(input any, opts ...DecoderConfigOption) ([]string, error) {
|
||||
var structKeyMap map[string]any
|
||||
|
||||
err := decode(input, defaultDecoderConfig(&structKeyMap, opts...))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
flattenedStructKeyMap := v.flattenAndMergeMap(map[string]bool{}, structKeyMap, "")
|
||||
|
||||
r := make([]string, 0, len(flattenedStructKeyMap))
|
||||
for v := range flattenedStructKeyMap {
|
||||
r = append(r, v)
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// defaultDecoderConfig returns default mapstructure.DecoderConfig with support
|
||||
// of time.Duration values & string slices
|
||||
func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {
|
||||
// of time.Duration values & string slices.
|
||||
func defaultDecoderConfig(output any, opts ...DecoderConfigOption) *mapstructure.DecoderConfig {
|
||||
c := &mapstructure.DecoderConfig{
|
||||
Metadata: nil,
|
||||
Result: output,
|
||||
|
@ -1125,8 +1167,8 @@ func defaultDecoderConfig(output interface{}, opts ...DecoderConfigOption) *maps
|
|||
return c
|
||||
}
|
||||
|
||||
// A wrapper around mapstructure.Decode that mimics the WeakDecode functionality
|
||||
func decode(input interface{}, config *mapstructure.DecoderConfig) error {
|
||||
// decode is a wrapper around mapstructure.Decode that mimics the WeakDecode functionality.
|
||||
func decode(input any, config *mapstructure.DecoderConfig) error {
|
||||
decoder, err := mapstructure.NewDecoder(config)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -1136,15 +1178,28 @@ func decode(input interface{}, config *mapstructure.DecoderConfig) error {
|
|||
|
||||
// UnmarshalExact unmarshals the config into a Struct, erroring if a field is nonexistent
|
||||
// in the destination struct.
|
||||
func UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error {
|
||||
func UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error {
|
||||
return v.UnmarshalExact(rawVal, opts...)
|
||||
}
|
||||
|
||||
func (v *Viper) UnmarshalExact(rawVal interface{}, opts ...DecoderConfigOption) error {
|
||||
func (v *Viper) UnmarshalExact(rawVal any, opts ...DecoderConfigOption) error {
|
||||
config := defaultDecoderConfig(rawVal, opts...)
|
||||
config.ErrorUnused = true
|
||||
|
||||
return decode(v.AllSettings(), config)
|
||||
keys := v.AllKeys()
|
||||
|
||||
if features.BindStruct {
|
||||
// TODO: make this optional?
|
||||
structKeys, err := v.decodeStructKeys(rawVal, opts...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
keys = append(keys, structKeys...)
|
||||
}
|
||||
|
||||
// TODO: struct keys should be enough?
|
||||
return decode(v.getSettings(keys), config)
|
||||
}
|
||||
|
||||
// BindPFlags binds a full flag set to the configuration, using each flag's long
|
||||
|
@ -1237,9 +1292,9 @@ func (v *Viper) MustBindEnv(input ...string) {
|
|||
// corresponds to a flag, the flag's default value is returned.
|
||||
//
|
||||
// Note: this assumes a lower-cased key given.
|
||||
func (v *Viper) find(lcaseKey string, flagDefault bool) interface{} {
|
||||
func (v *Viper) find(lcaseKey string, flagDefault bool) any {
|
||||
var (
|
||||
val interface{}
|
||||
val any
|
||||
exists bool
|
||||
path = strings.Split(lcaseKey, v.keyDelim)
|
||||
nested = len(path) > 1
|
||||
|
@ -1398,46 +1453,46 @@ func readAsCSV(val string) ([]string, error) {
|
|||
}
|
||||
|
||||
// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/master/string_to_string.go#L79
|
||||
// alterations are: errors are swallowed, map[string]interface{} is returned in order to enable cast.ToStringMap
|
||||
func stringToStringConv(val string) interface{} {
|
||||
// alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap.
|
||||
func stringToStringConv(val string) any {
|
||||
val = strings.Trim(val, "[]")
|
||||
// An empty string would cause an empty map
|
||||
if len(val) == 0 {
|
||||
return map[string]interface{}{}
|
||||
if val == "" {
|
||||
return map[string]any{}
|
||||
}
|
||||
r := csv.NewReader(strings.NewReader(val))
|
||||
ss, err := r.Read()
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]interface{}, len(ss))
|
||||
out := make(map[string]any, len(ss))
|
||||
for _, pair := range ss {
|
||||
kv := strings.SplitN(pair, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
k, vv, found := strings.Cut(pair, "=")
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
out[kv[0]] = kv[1]
|
||||
out[k] = vv
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// mostly copied from pflag's implementation of this operation here https://github.com/spf13/pflag/blob/d5e0c0615acee7028e1e2740a11102313be88de1/string_to_int.go#L68
|
||||
// alterations are: errors are swallowed, map[string]interface{} is returned in order to enable cast.ToStringMap
|
||||
func stringToIntConv(val string) interface{} {
|
||||
// alterations are: errors are swallowed, map[string]any is returned in order to enable cast.ToStringMap.
|
||||
func stringToIntConv(val string) any {
|
||||
val = strings.Trim(val, "[]")
|
||||
// An empty string would cause an empty map
|
||||
if len(val) == 0 {
|
||||
return map[string]interface{}{}
|
||||
if val == "" {
|
||||
return map[string]any{}
|
||||
}
|
||||
ss := strings.Split(val, ",")
|
||||
out := make(map[string]interface{}, len(ss))
|
||||
out := make(map[string]any, len(ss))
|
||||
for _, pair := range ss {
|
||||
kv := strings.SplitN(pair, "=", 2)
|
||||
if len(kv) != 2 {
|
||||
k, vv, found := strings.Cut(pair, "=")
|
||||
if !found {
|
||||
return nil
|
||||
}
|
||||
var err error
|
||||
out[kv[0]], err = strconv.Atoi(kv[1])
|
||||
out[k], err = strconv.Atoi(vv)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
@ -1474,13 +1529,13 @@ func (v *Viper) SetEnvKeyReplacer(r *strings.Replacer) {
|
|||
|
||||
// RegisterAlias creates an alias that provides another accessor for the same key.
|
||||
// This enables one to change a name without breaking the application.
|
||||
func RegisterAlias(alias string, key string) { v.RegisterAlias(alias, key) }
|
||||
func RegisterAlias(alias, key string) { v.RegisterAlias(alias, key) }
|
||||
|
||||
func (v *Viper) RegisterAlias(alias string, key string) {
|
||||
func (v *Viper) RegisterAlias(alias, key string) {
|
||||
v.registerAlias(alias, strings.ToLower(key))
|
||||
}
|
||||
|
||||
func (v *Viper) registerAlias(alias string, key string) {
|
||||
func (v *Viper) registerAlias(alias, key string) {
|
||||
alias = strings.ToLower(alias)
|
||||
if alias != key && alias != v.realKey(key) {
|
||||
_, exists := v.aliases[alias]
|
||||
|
@ -1538,9 +1593,9 @@ func (v *Viper) InConfig(key string) bool {
|
|||
// SetDefault sets the default value for this key.
|
||||
// SetDefault is case-insensitive for a key.
|
||||
// Default only used when no value is provided by the user via flag, config or ENV.
|
||||
func SetDefault(key string, value interface{}) { v.SetDefault(key, value) }
|
||||
func SetDefault(key string, value any) { v.SetDefault(key, value) }
|
||||
|
||||
func (v *Viper) SetDefault(key string, value interface{}) {
|
||||
func (v *Viper) SetDefault(key string, value any) {
|
||||
// If alias passed in, then set the proper default
|
||||
key = v.realKey(strings.ToLower(key))
|
||||
value = toCaseInsensitiveValue(value)
|
||||
|
@ -1557,9 +1612,9 @@ func (v *Viper) SetDefault(key string, value interface{}) {
|
|||
// Set is case-insensitive for a key.
|
||||
// Will be used instead of values obtained via
|
||||
// flags, config file, ENV, default, or key/value store.
|
||||
func Set(key string, value interface{}) { v.Set(key, value) }
|
||||
func Set(key string, value any) { v.Set(key, value) }
|
||||
|
||||
func (v *Viper) Set(key string, value interface{}) {
|
||||
func (v *Viper) Set(key string, value any) {
|
||||
// If alias passed in, then set the proper override
|
||||
key = v.realKey(strings.ToLower(key))
|
||||
value = toCaseInsensitiveValue(value)
|
||||
|
@ -1593,7 +1648,7 @@ func (v *Viper) ReadInConfig() error {
|
|||
return err
|
||||
}
|
||||
|
||||
config := make(map[string]interface{})
|
||||
config := make(map[string]any)
|
||||
|
||||
err = v.unmarshalReader(bytes.NewReader(file), config)
|
||||
if err != nil {
|
||||
|
@ -1631,7 +1686,7 @@ func (v *Viper) MergeInConfig() error {
|
|||
func ReadConfig(in io.Reader) error { return v.ReadConfig(in) }
|
||||
|
||||
func (v *Viper) ReadConfig(in io.Reader) error {
|
||||
v.config = make(map[string]interface{})
|
||||
v.config = make(map[string]any)
|
||||
return v.unmarshalReader(in, v.config)
|
||||
}
|
||||
|
||||
|
@ -1639,7 +1694,7 @@ func (v *Viper) ReadConfig(in io.Reader) error {
|
|||
func MergeConfig(in io.Reader) error { return v.MergeConfig(in) }
|
||||
|
||||
func (v *Viper) MergeConfig(in io.Reader) error {
|
||||
cfg := make(map[string]interface{})
|
||||
cfg := make(map[string]any)
|
||||
if err := v.unmarshalReader(in, cfg); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -1648,11 +1703,11 @@ func (v *Viper) MergeConfig(in io.Reader) error {
|
|||
|
||||
// MergeConfigMap merges the configuration from the map given with an existing config.
|
||||
// Note that the map given may be modified.
|
||||
func MergeConfigMap(cfg map[string]interface{}) error { return v.MergeConfigMap(cfg) }
|
||||
func MergeConfigMap(cfg map[string]any) error { return v.MergeConfigMap(cfg) }
|
||||
|
||||
func (v *Viper) MergeConfigMap(cfg map[string]interface{}) error {
|
||||
func (v *Viper) MergeConfigMap(cfg map[string]any) error {
|
||||
if v.config == nil {
|
||||
v.config = make(map[string]interface{})
|
||||
v.config = make(map[string]any)
|
||||
}
|
||||
insensitiviseMap(cfg)
|
||||
mergeMaps(cfg, v.config, nil)
|
||||
|
@ -1717,7 +1772,7 @@ func (v *Viper) writeConfig(filename string, force bool) error {
|
|||
return UnsupportedConfigError(configType)
|
||||
}
|
||||
if v.config == nil {
|
||||
v.config = make(map[string]interface{})
|
||||
v.config = make(map[string]any)
|
||||
}
|
||||
flags := os.O_CREATE | os.O_TRUNC | os.O_WRONLY
|
||||
if !force {
|
||||
|
@ -1738,11 +1793,11 @@ func (v *Viper) writeConfig(filename string, force bool) error {
|
|||
|
||||
// Unmarshal a Reader into a map.
|
||||
// Should probably be an unexported function.
|
||||
func unmarshalReader(in io.Reader, c map[string]interface{}) error {
|
||||
func unmarshalReader(in io.Reader, c map[string]any) error {
|
||||
return v.unmarshalReader(in, c)
|
||||
}
|
||||
|
||||
func (v *Viper) unmarshalReader(in io.Reader, c map[string]interface{}) error {
|
||||
func (v *Viper) unmarshalReader(in io.Reader, c map[string]any) error {
|
||||
buf := new(bytes.Buffer)
|
||||
buf.ReadFrom(in)
|
||||
|
||||
|
@ -1776,7 +1831,7 @@ func (v *Viper) marshalWriter(f afero.File, configType string) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func keyExists(k string, m map[string]interface{}) string {
|
||||
func keyExists(k string, m map[string]any) string {
|
||||
lk := strings.ToLower(k)
|
||||
for mk := range m {
|
||||
lmk := strings.ToLower(mk)
|
||||
|
@ -1788,33 +1843,33 @@ func keyExists(k string, m map[string]interface{}) string {
|
|||
}
|
||||
|
||||
func castToMapStringInterface(
|
||||
src map[interface{}]interface{},
|
||||
) map[string]interface{} {
|
||||
tgt := map[string]interface{}{}
|
||||
src map[any]any,
|
||||
) map[string]any {
|
||||
tgt := map[string]any{}
|
||||
for k, v := range src {
|
||||
tgt[fmt.Sprintf("%v", k)] = v
|
||||
}
|
||||
return tgt
|
||||
}
|
||||
|
||||
func castMapStringSliceToMapInterface(src map[string][]string) map[string]interface{} {
|
||||
tgt := map[string]interface{}{}
|
||||
func castMapStringSliceToMapInterface(src map[string][]string) map[string]any {
|
||||
tgt := map[string]any{}
|
||||
for k, v := range src {
|
||||
tgt[k] = v
|
||||
}
|
||||
return tgt
|
||||
}
|
||||
|
||||
func castMapStringToMapInterface(src map[string]string) map[string]interface{} {
|
||||
tgt := map[string]interface{}{}
|
||||
func castMapStringToMapInterface(src map[string]string) map[string]any {
|
||||
tgt := map[string]any{}
|
||||
for k, v := range src {
|
||||
tgt[k] = v
|
||||
}
|
||||
return tgt
|
||||
}
|
||||
|
||||
func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{} {
|
||||
tgt := map[string]interface{}{}
|
||||
func castMapFlagToMapInterface(src map[string]FlagValue) map[string]any {
|
||||
tgt := map[string]any{}
|
||||
for k, v := range src {
|
||||
tgt[k] = v
|
||||
}
|
||||
|
@ -1822,17 +1877,15 @@ func castMapFlagToMapInterface(src map[string]FlagValue) map[string]interface{}
|
|||
}
|
||||
|
||||
// mergeMaps merges two maps. The `itgt` parameter is for handling go-yaml's
|
||||
// insistence on parsing nested structures as `map[interface{}]interface{}`
|
||||
// insistence on parsing nested structures as `map[any]any`
|
||||
// instead of using a `string` as the key for nest structures beyond one level
|
||||
// deep. Both map types are supported as there is a go-yaml fork that uses
|
||||
// `map[string]interface{}` instead.
|
||||
func mergeMaps(
|
||||
src, tgt map[string]interface{}, itgt map[interface{}]interface{},
|
||||
) {
|
||||
// `map[string]any` instead.
|
||||
func mergeMaps(src, tgt map[string]any, itgt map[any]any) {
|
||||
for sk, sv := range src {
|
||||
tk := keyExists(sk, tgt)
|
||||
if tk == "" {
|
||||
v.logger.Trace("", "tk", "\"\"", fmt.Sprintf("tgt[%s]", sk), sv)
|
||||
v.logger.Debug("", "tk", "\"\"", fmt.Sprintf("tgt[%s]", sk), sv)
|
||||
tgt[sk] = sv
|
||||
if itgt != nil {
|
||||
itgt[sk] = sv
|
||||
|
@ -1842,7 +1895,7 @@ func mergeMaps(
|
|||
|
||||
tv, ok := tgt[tk]
|
||||
if !ok {
|
||||
v.logger.Trace("", fmt.Sprintf("ok[%s]", tk), false, fmt.Sprintf("tgt[%s]", sk), sv)
|
||||
v.logger.Debug("", fmt.Sprintf("ok[%s]", tk), false, fmt.Sprintf("tgt[%s]", sk), sv)
|
||||
tgt[sk] = sv
|
||||
if itgt != nil {
|
||||
itgt[sk] = sv
|
||||
|
@ -1853,7 +1906,7 @@ func mergeMaps(
|
|||
svType := reflect.TypeOf(sv)
|
||||
tvType := reflect.TypeOf(tv)
|
||||
|
||||
v.logger.Trace(
|
||||
v.logger.Debug(
|
||||
"processing",
|
||||
"key", sk,
|
||||
"st", svType,
|
||||
|
@ -1863,12 +1916,12 @@ func mergeMaps(
|
|||
)
|
||||
|
||||
switch ttv := tv.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
v.logger.Trace("merging maps (must convert)")
|
||||
tsv, ok := sv.(map[interface{}]interface{})
|
||||
case map[any]any:
|
||||
v.logger.Debug("merging maps (must convert)")
|
||||
tsv, ok := sv.(map[any]any)
|
||||
if !ok {
|
||||
v.logger.Error(
|
||||
"Could not cast sv to map[interface{}]interface{}",
|
||||
"Could not cast sv to map[any]any",
|
||||
"key", sk,
|
||||
"st", svType,
|
||||
"tt", tvType,
|
||||
|
@ -1881,12 +1934,12 @@ func mergeMaps(
|
|||
ssv := castToMapStringInterface(tsv)
|
||||
stv := castToMapStringInterface(ttv)
|
||||
mergeMaps(ssv, stv, ttv)
|
||||
case map[string]interface{}:
|
||||
v.logger.Trace("merging maps")
|
||||
tsv, ok := sv.(map[string]interface{})
|
||||
case map[string]any:
|
||||
v.logger.Debug("merging maps")
|
||||
tsv, ok := sv.(map[string]any)
|
||||
if !ok {
|
||||
v.logger.Error(
|
||||
"Could not cast sv to map[string]interface{}",
|
||||
"Could not cast sv to map[string]any",
|
||||
"key", sk,
|
||||
"st", svType,
|
||||
"tt", tvType,
|
||||
|
@ -1897,7 +1950,7 @@ func mergeMaps(
|
|||
}
|
||||
mergeMaps(tsv, ttv, nil)
|
||||
default:
|
||||
v.logger.Trace("setting value")
|
||||
v.logger.Debug("setting value")
|
||||
tgt[tk] = sv
|
||||
if itgt != nil {
|
||||
itgt[tk] = sv
|
||||
|
@ -1948,7 +2001,7 @@ func (v *Viper) getKeyValueConfig() error {
|
|||
return RemoteConfigError("No Files Found")
|
||||
}
|
||||
|
||||
func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) {
|
||||
func (v *Viper) getRemoteConfig(provider RemoteProvider) (map[string]any, error) {
|
||||
reader, err := RemoteConfig.Get(provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -1997,7 +2050,7 @@ func (v *Viper) watchKeyValueConfig() error {
|
|||
return RemoteConfigError("No Files Found")
|
||||
}
|
||||
|
||||
func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface{}, error) {
|
||||
func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]any, error) {
|
||||
reader, err := RemoteConfig.Watch(provider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -2007,7 +2060,7 @@ func (v *Viper) watchRemoteConfig(provider RemoteProvider) (map[string]interface
|
|||
}
|
||||
|
||||
// AllKeys returns all keys holding a value, regardless of where they are set.
|
||||
// Nested keys are returned with a v.keyDelim separator
|
||||
// Nested keys are returned with a v.keyDelim separator.
|
||||
func AllKeys() []string { return v.AllKeys() }
|
||||
|
||||
func (v *Viper) AllKeys() []string {
|
||||
|
@ -2036,7 +2089,7 @@ func (v *Viper) AllKeys() []string {
|
|||
// it is skipped.
|
||||
//
|
||||
// The resulting set of paths is merged to the given shadow set at the same time.
|
||||
func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interface{}, prefix string) map[string]bool {
|
||||
func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]any, prefix string) map[string]bool {
|
||||
if shadow != nil && prefix != "" && shadow[prefix] {
|
||||
// prefix is shadowed => nothing more to flatten
|
||||
return shadow
|
||||
|
@ -2045,16 +2098,16 @@ func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interfac
|
|||
shadow = make(map[string]bool)
|
||||
}
|
||||
|
||||
var m2 map[string]interface{}
|
||||
var m2 map[string]any
|
||||
if prefix != "" {
|
||||
prefix += v.keyDelim
|
||||
}
|
||||
for k, val := range m {
|
||||
fullKey := prefix + k
|
||||
switch val.(type) {
|
||||
case map[string]interface{}:
|
||||
m2 = val.(map[string]interface{})
|
||||
case map[interface{}]interface{}:
|
||||
switch val := val.(type) {
|
||||
case map[string]any:
|
||||
m2 = val
|
||||
case map[any]any:
|
||||
m2 = cast.ToStringMap(val)
|
||||
default:
|
||||
// immediate value
|
||||
|
@ -2069,7 +2122,7 @@ func (v *Viper) flattenAndMergeMap(shadow map[string]bool, m map[string]interfac
|
|||
|
||||
// mergeFlatMap merges the given maps, excluding values of the second map
|
||||
// shadowed by values from the first map.
|
||||
func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]interface{}) map[string]bool {
|
||||
func (v *Viper) mergeFlatMap(shadow map[string]bool, m map[string]any) map[string]bool {
|
||||
// scan keys
|
||||
outer:
|
||||
for k := range m {
|
||||
|
@ -2089,13 +2142,17 @@ outer:
|
|||
return shadow
|
||||
}
|
||||
|
||||
// AllSettings merges all settings and returns them as a map[string]interface{}.
|
||||
func AllSettings() map[string]interface{} { return v.AllSettings() }
|
||||
// AllSettings merges all settings and returns them as a map[string]any.
|
||||
func AllSettings() map[string]any { return v.AllSettings() }
|
||||
|
||||
func (v *Viper) AllSettings() map[string]interface{} {
|
||||
m := map[string]interface{}{}
|
||||
func (v *Viper) AllSettings() map[string]any {
|
||||
return v.getSettings(v.AllKeys())
|
||||
}
|
||||
|
||||
func (v *Viper) getSettings(keys []string) map[string]any {
|
||||
m := map[string]any{}
|
||||
// start from the list of keys, and construct the map one value at a time
|
||||
for _, k := range v.AllKeys() {
|
||||
for _, k := range keys {
|
||||
value := v.Get(k)
|
||||
if value == nil {
|
||||
// should not happen, since AllKeys() returns only keys holding a value,
|
||||
|
|
|
@ -1,32 +0,0 @@
|
|||
//go:build go1.16 && finder
|
||||
// +build go1.16,finder
|
||||
|
||||
package viper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
// Search all configPaths for any config file.
|
||||
// Returns the first path that exists (and is a config file).
|
||||
func (v *Viper) findConfigFile() (string, error) {
|
||||
finder := finder{
|
||||
paths: v.configPaths,
|
||||
fileNames: []string{v.configName},
|
||||
extensions: SupportedExts,
|
||||
withoutExtension: v.configType != "",
|
||||
}
|
||||
|
||||
file, err := finder.Find(afero.NewIOFS(v.fs))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if file == "" {
|
||||
return "", ConfigFileNotFoundError{v.configName, fmt.Sprintf("%s", v.configPaths)}
|
||||
}
|
||||
|
||||
return file, nil
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
//go:build darwin || dragonfly || freebsd || openbsd || linux || netbsd || solaris || windows
|
||||
// +build darwin dragonfly freebsd openbsd linux netbsd solaris windows
|
||||
|
||||
package viper
|
||||
|
||||
import "github.com/fsnotify/fsnotify"
|
||||
|
||||
type watcher = fsnotify.Watcher
|
||||
|
||||
func newWatcher() (*watcher, error) {
|
||||
return fsnotify.NewWatcher()
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
//go:build appengine || (!darwin && !dragonfly && !freebsd && !openbsd && !linux && !netbsd && !solaris && !windows)
|
||||
// +build appengine !darwin,!dragonfly,!freebsd,!openbsd,!linux,!netbsd,!solaris,!windows
|
||||
|
||||
package viper
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
)
|
||||
|
||||
func newWatcher() (*watcher, error) {
|
||||
return &watcher{}, fmt.Errorf("fsnotify not supported on %s", runtime.GOOS)
|
||||
}
|
||||
|
||||
type watcher struct {
|
||||
Events chan fsnotify.Event
|
||||
Errors chan error
|
||||
}
|
||||
|
||||
func (*watcher) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*watcher) Add(name string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (*watcher) Remove(name string) error {
|
||||
return nil
|
||||
}
|
|
@ -1,5 +1,42 @@
|
|||
# Changelog
|
||||
|
||||
## [1.5.0] - 2023-08-15
|
||||
|
||||
### Fixed
|
||||
|
||||
- Use io.Reader instead of custom Reader
|
||||
|
||||
## [1.5.0] - 2023-08-15
|
||||
|
||||
### Added
|
||||
|
||||
- Support for reading UTF16 files
|
||||
|
||||
### Fixed
|
||||
|
||||
- Scanner error handling
|
||||
- Reader error handling
|
||||
|
||||
## [1.4.2] - 2023-01-11
|
||||
|
||||
### Fixed
|
||||
|
||||
- Env var initialization
|
||||
|
||||
### Changed
|
||||
|
||||
- More consitent line splitting
|
||||
|
||||
## [1.4.1] - 2022-08-23
|
||||
|
||||
### Fixed
|
||||
|
||||
- Missing file close
|
||||
|
||||
### Changed
|
||||
|
||||
- Updated dependencies
|
||||
|
||||
## [1.4.0] - 2022-06-02
|
||||
|
||||
### Added
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue