diff --git a/go.mod b/go.mod
index c0957cd89..1b4e3029c 100644
--- a/go.mod
+++ b/go.mod
@@ -65,7 +65,7 @@ require (
github.com/open-policy-agent/opa v1.15.1
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d
- github.com/opencloud-eu/reva/v2 v2.42.6
+ github.com/opencloud-eu/reva/v2 v2.42.7-0.20260408072824-411780d0b756
github.com/opensearch-project/opensearch-go/v4 v4.6.0
github.com/orcaman/concurrent-map v1.0.0
github.com/pkg/errors v0.9.1
@@ -75,7 +75,7 @@ require (
github.com/riandyrn/otelchi v0.12.2
github.com/rogpeppe/go-internal v1.14.1
github.com/rs/cors v1.11.1
- github.com/rs/zerolog v1.34.0
+ github.com/rs/zerolog v1.35.0
github.com/sirupsen/logrus v1.9.4
github.com/spf13/afero v1.15.0
github.com/spf13/cobra v1.10.2
@@ -95,14 +95,14 @@ require (
github.com/xhit/go-simple-mail/v2 v2.16.0
go-micro.dev/v4 v4.11.0
go.etcd.io/bbolt v1.4.3
- go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0
go.opentelemetry.io/contrib/zpages v0.67.0
- go.opentelemetry.io/otel v1.42.0
+ go.opentelemetry.io/otel v1.43.0
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0
- go.opentelemetry.io/otel/sdk v1.42.0
- go.opentelemetry.io/otel/trace v1.42.0
+ go.opentelemetry.io/otel/sdk v1.43.0
+ go.opentelemetry.io/otel/trace v1.43.0
golang.org/x/crypto v0.49.0
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac
golang.org/x/image v0.38.0
@@ -112,7 +112,7 @@ require (
golang.org/x/term v0.41.0
golang.org/x/text v0.35.0
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57
- google.golang.org/grpc v1.79.3
+ google.golang.org/grpc v1.80.0
google.golang.org/protobuf v1.36.11
gopkg.in/yaml.v2 v2.4.0
gopkg.in/yaml.v3 v3.0.1
@@ -174,7 +174,7 @@ require (
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v1.0.0-rc.2 // indirect
github.com/coreos/go-semver v0.3.1 // indirect
- github.com/coreos/go-systemd/v22 v22.6.0 // indirect
+ github.com/coreos/go-systemd/v22 v22.7.0 // indirect
github.com/cornelk/hashmap v1.0.8 // indirect
github.com/cpuguy83/dockercfg v0.3.2 // indirect
github.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect
@@ -206,10 +206,10 @@ require (
github.com/go-acme/lego/v4 v4.4.0 // indirect
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
- github.com/go-git/go-billy/v5 v5.6.2 // indirect
- github.com/go-git/go-git/v5 v5.16.5 // indirect
+ github.com/go-git/go-billy/v5 v5.8.0 // indirect
+ github.com/go-git/go-git/v5 v5.17.1 // indirect
github.com/go-ini/ini v1.67.0 // indirect
- github.com/go-jose/go-jose/v4 v4.1.3 // indirect
+ github.com/go-jose/go-jose/v4 v4.1.4 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-logr/logr v1.4.3 // indirect
@@ -336,9 +336,9 @@ require (
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
- github.com/samber/lo v1.52.0 // indirect
- github.com/samber/slog-common v0.20.0 // indirect
- github.com/samber/slog-zerolog/v2 v2.9.1 // indirect
+ github.com/samber/lo v1.53.0 // indirect
+ github.com/samber/slog-common v0.21.0 // indirect
+ github.com/samber/slog-zerolog/v2 v2.9.2 // indirect
github.com/segmentio/asm v1.2.1 // indirect
github.com/segmentio/kafka-go v0.4.50 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
@@ -382,7 +382,7 @@ require (
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 // indirect
- go.opentelemetry.io/otel/metric v1.42.0 // indirect
+ go.opentelemetry.io/otel/metric v1.43.0 // indirect
go.opentelemetry.io/proto/otlp v1.9.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
@@ -393,7 +393,7 @@ require (
golang.org/x/time v0.15.0 // indirect
golang.org/x/tools v0.42.0 // indirect
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 // indirect
- google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 // indirect
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect
gopkg.in/cenkalti/backoff.v1 v1.1.0 // indirect
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/warnings.v0 v0.1.2 // indirect
diff --git a/go.sum b/go.sum
index 54d0e2fca..2a5a205ee 100644
--- a/go.sum
+++ b/go.sum
@@ -245,9 +245,8 @@ github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4=
github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec=
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
-github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
-github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo=
-github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU=
+github.com/coreos/go-systemd/v22 v22.7.0 h1:LAEzFkke61DFROc7zNLX/WA2i5J8gYqe0rSj9KI28KA=
+github.com/coreos/go-systemd/v22 v22.7.0/go.mod h1:xNUYtjHu2EDXbsxz1i41wouACIwT7Ybq9o0BQhMwD0w=
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cornelk/hashmap v1.0.8 h1:nv0AWgw02n+iDcawr5It4CjQIAcdMMKRrs10HOJYlrc=
github.com/cornelk/hashmap v1.0.8/go.mod h1:RfZb7JO3RviW/rT6emczVuC/oxpdz4UsSB2LJSclR1k=
@@ -385,12 +384,12 @@ github.com/go-cmd/cmd v1.0.5/go.mod h1:y8q8qlK5wQibcw63djSl/ntiHUHXHGdCkPk0j4QeW
github.com/go-errors/errors v1.0.1/go.mod h1:f4zRHt4oKfwPJE5k8C9vpYG+aDHdBFUsgrm6/TyX73Q=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 h1:+zs/tPmkDkHx3U66DAb0lQFJrpS6731Oaa12ikc+DiI=
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376/go.mod h1:an3vInlBmSxCcxctByoQdvwPiA7DTK7jaaFDBTtu0ic=
-github.com/go-git/go-billy/v5 v5.6.2 h1:6Q86EsPXMa7c3YZ3aLAQsMA0VlWmy43r6FHqa/UNbRM=
-github.com/go-git/go-billy/v5 v5.6.2/go.mod h1:rcFC2rAsp/erv7CMz9GczHcuD0D32fWzH+MJAU+jaUU=
+github.com/go-git/go-billy/v5 v5.8.0 h1:I8hjc3LbBlXTtVuFNJuwYuMiHvQJDq1AT6u4DwDzZG0=
+github.com/go-git/go-billy/v5 v5.8.0/go.mod h1:RpvI/rw4Vr5QA+Z60c6d6LXH0rYJo0uD5SqfmrrheCY=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399 h1:eMje31YglSBqCdIqdhKBW8lokaMrL3uTkpGYlE2OOT4=
github.com/go-git/go-git-fixtures/v4 v4.3.2-0.20231010084843-55a94097c399/go.mod h1:1OCfN199q1Jm3HZlxleg+Dw/mwps2Wbk9frAWm+4FII=
-github.com/go-git/go-git/v5 v5.16.5 h1:mdkuqblwr57kVfXri5TTH+nMFLNUxIj9Z7F5ykFbw5s=
-github.com/go-git/go-git/v5 v5.16.5/go.mod h1:QOMLpNf1qxuSY4StA/ArOdfFR2TrKEjJiye2kel2m+M=
+github.com/go-git/go-git/v5 v5.17.1 h1:WnljyxIzSj9BRRUlnmAU35ohDsjRK0EKmL0evDqi5Jk=
+github.com/go-git/go-git/v5 v5.17.1/go.mod h1:pW/VmeqkanRFqR6AljLcs7EA7FbZaN5MQqO7oZADXpo=
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
@@ -399,8 +398,8 @@ github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3I
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
-github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=
-github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
+github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
+github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
@@ -484,7 +483,6 @@ github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
-github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw=
github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@@ -809,7 +807,6 @@ github.com/mattn/go-colorable v0.1.7/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.9/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
-github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
@@ -818,8 +815,6 @@ github.com/mattn/go-isatty v0.0.9/go.mod h1:YNRxwqDuOph6SZLI9vUUz6OYw3QyUt7WiY2y
github.com/mattn/go-isatty v0.0.10/go.mod h1:qgIWMr58cqv1PHHyhnkY9lrL7etaEgOFcMEpPG5Rm84=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
-github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
-github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
@@ -959,8 +954,8 @@ github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9 h1:dIft
github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9/go.mod h1:JWyDC6H+5oZRdUJUgKuaye+8Ph5hEs6HVzVoPKzWSGI=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d h1:JcqGDiyrcaQwVyV861TUyQgO7uEmsjkhfm7aQd84dOw=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
-github.com/opencloud-eu/reva/v2 v2.42.6 h1:GjGPa1lNrhpkBfj7No1vM9idcurG57Ax4qzf1dqnPTk=
-github.com/opencloud-eu/reva/v2 v2.42.6/go.mod h1:Ki3c/BKVg/5aHvAyOJIx+iZ50vvlhFvCf8wDZacF5qw=
+github.com/opencloud-eu/reva/v2 v2.42.7-0.20260408072824-411780d0b756 h1:Jbftg+S89x2PD7NVWhQB0+vlI/Qo0wv9rBwnM1N50yc=
+github.com/opencloud-eu/reva/v2 v2.42.7-0.20260408072824-411780d0b756/go.mod h1:hjMR/IerRm9xX4bthVRE9ZO/vhvrXVMbuvnnzwLjzK4=
github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4 h1:l2oB/RctH+t8r7QBj5p8thfEHCM/jF35aAY3WQ3hADI=
github.com/opencloud-eu/secure v0.0.0-20260312082735-b6f5cb2244e4/go.mod h1:BmF5hyM6tXczk3MpQkFf1hpKSRqCyhqcbiQtiAF7+40=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
@@ -1082,8 +1077,8 @@ github.com/rs/cors v1.11.1 h1:eU3gRzXLRK57F5rKMGMZURNdIG4EoAmX8k94r9wXWHA=
github.com/rs/cors v1.11.1/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU=
github.com/rs/xid v1.6.0 h1:fV591PaemRlL6JfRxGDEPl69wICngIQ3shQtzfy2gxU=
github.com/rs/xid v1.6.0/go.mod h1:7XoLgs4eV+QndskICGsho+ADou8ySMSjJKDIan90Nz0=
-github.com/rs/zerolog v1.34.0 h1:k43nTLIwcTVQAncfCw4KZ2VY6ukYoZaBPNOE8txlOeY=
-github.com/rs/zerolog v1.34.0/go.mod h1:bJsvje4Z08ROH4Nhs5iH600c3IkWhwp44iRc54W6wYQ=
+github.com/rs/zerolog v1.35.0 h1:VD0ykx7HMiMJytqINBsKcbLS+BJ4WYjz+05us+LRTdI=
+github.com/rs/zerolog v1.35.0/go.mod h1:EjML9kdfa/RMA7h/6z6pYmq1ykOuA8/mjWaEvGI+jcw=
github.com/russellhaering/goxmldsig v1.6.0 h1:8fdWXEPh2k/NZNQBPFNoVfS3JmzS4ZprY/sAOpKQLks=
github.com/russellhaering/goxmldsig v1.6.0/go.mod h1:TrnaquDcYxWXfJrOjeMBTX4mLBeYAqaHEyUeWPxZlBM=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -1095,12 +1090,12 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb
github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS7IBEjKVSrjg=
github.com/sagikazarmark/locafero v0.11.0 h1:1iurJgmM9G3PA/I+wWYIOw/5SyBtxapeHDcg+AAIFXc=
github.com/sagikazarmark/locafero v0.11.0/go.mod h1:nVIGvgyzw595SUSUE6tvCp3YYTeHs15MvlmU87WwIik=
-github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw=
-github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
-github.com/samber/slog-common v0.20.0 h1:WaLnm/aCvBJSk5nR5aXZTFBaV0B47A+AEaEOiZDeUnc=
-github.com/samber/slog-common v0.20.0/go.mod h1:+Ozat1jgnnE59UAlmNX1IF3IByHsODnnwf9jUcBZ+m8=
-github.com/samber/slog-zerolog/v2 v2.9.1 h1:RMOq8XqzfuGx1X0TEIlS9OXbbFmqLY2/wJppghz66YY=
-github.com/samber/slog-zerolog/v2 v2.9.1/go.mod h1:DQYYve14WgCRN/XnKeHl4266jXK0DgYkYXkfZ4Fp98k=
+github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=
+github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
+github.com/samber/slog-common v0.21.0 h1:Wo2hTly1Br5RjYqX/BTWJJeDnTE85oWk/7vqlpZuAUc=
+github.com/samber/slog-common v0.21.0/go.mod h1:d/6OaSlzdkl9PFpfRLgn8FwY1OW6EFmPtBpsHX4MrU0=
+github.com/samber/slog-zerolog/v2 v2.9.2 h1:DIFzfzDTxHeRyGlfg/D7b2by7VVzcsBTybRPrzjWF4c=
+github.com/samber/slog-zerolog/v2 v2.9.2/go.mod h1:2q6cYK2OcN6YfQE/WyCnUtigc+yYf3ozqGsGmRwZR6I=
github.com/scaleway/scaleway-sdk-go v1.0.0-beta.7.0.20210127161313-bd30bebeac4f/go.mod h1:CJJ5VAbozOl0yEw7nHB9+7BXTJbIn6h7W+f6Gau5IP8=
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
@@ -1303,14 +1298,14 @@ go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04=
-go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0 h1:0Qx7VGBacMm9ZENQ7TnNObTYI4ShC+lHI16seduaxZo=
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0/go.mod h1:Sje3i3MjSPKTSPvVWCaL8ugBzJwik3u4smCjUeuupqg=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
go.opentelemetry.io/contrib/zpages v0.67.0 h1:cIUwWSVDovuLEbDIKreptjdxMuIhGiqwq0uL8YNaq1c=
go.opentelemetry.io/contrib/zpages v0.67.0/go.mod h1:vK8fsYHgPYg4Z/XDbFSEvItSGZDbjWTvjBOu8+AiDhc=
-go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
-go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
+go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
+go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0 h1:THuZiwpQZuHPul65w4WcwEnkX2QIuMT+UFoOrygtoJw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.42.0/go.mod h1:J2pvYM5NGHofZ2/Ru6zw/TNWnEQp5crgyDeSrYpXkAw=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.42.0 h1:zWWrB1U6nqhS/k6zYB74CjRpuiitRtLLi68VcgmOEto=
@@ -1319,14 +1314,14 @@ go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0 h1:inYW9
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.41.0/go.mod h1:Izur+Wt8gClgMJqO/cZ8wdeeMryJ/xxiOVgFSSfpDTY=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0 h1:s/1iRkCKDfhlh1JF26knRneorus8aOwVIDhvYx9WoDw=
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.42.0/go.mod h1:UI3wi0FXg1Pofb8ZBiBLhtMzgoTm1TYkMvn71fAqDzs=
-go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
-go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
-go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
-go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
-go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
-go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
-go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
-go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
+go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
+go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
+go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
+go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
+go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
+go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
+go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
+go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A=
go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
@@ -1558,11 +1553,9 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220708085239-5a0f0661e09d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
@@ -1661,8 +1654,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
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=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
-gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
-gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
+gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
@@ -1720,8 +1713,8 @@ google.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH8
google.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57 h1:JLQynH/LBHfCTSbDWl+py8C+Rg/k1OVH3xfcaiANuF0=
google.golang.org/genproto/googleapis/api v0.0.0-20260209200024-4cfbd4190f57/go.mod h1:kSJwQxqmFXeo79zOmbrALdflXQeAYcUbgS7PbpMknCY=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171 h1:ggcbiqK8WWh6l1dnltU4BgWGIGo+EVYxCaAPih/zQXQ=
-google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA=
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.17.0/go.mod h1:6QZJwpn2B+Zp71q/5VxRsJ6NXXVCE5NRUHRo+f3cWCs=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.19.1/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
@@ -1737,8 +1730,8 @@ google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3Iji
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
-google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
-google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
+google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
+google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e h1:m7aQHHqd0q89mRwhwS9Bx2rjyl/hsFAeta+uGrHsQaU=
google.golang.org/grpc/examples v0.0.0-20211102180624-670c133e568e/go.mod h1:gID3PKrg7pWKntu9Ss6zTLJ0ttC0X9IHgREOCZwbCVU=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
diff --git a/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go b/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go
index 6266e16e5..55cb9eb26 100644
--- a/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go
+++ b/vendor/github.com/coreos/go-systemd/v22/journal/journal_unix.go
@@ -106,7 +106,7 @@ func fdIsJournalStream(fd int) (bool, error) {
var expectedStat syscall.Stat_t
_, err := fmt.Sscanf(journalStream, "%d:%d", &expectedStat.Dev, &expectedStat.Ino)
if err != nil {
- return false, fmt.Errorf("failed to parse JOURNAL_STREAM=%q: %v", journalStream, err)
+ return false, fmt.Errorf("failed to parse JOURNAL_STREAM=%q: %w", journalStream, err)
}
var stat syscall.Stat_t
diff --git a/vendor/github.com/go-git/go-billy/v5/fs.go b/vendor/github.com/go-git/go-billy/v5/fs.go
index d86f9d823..14a020a35 100644
--- a/vendor/github.com/go-git/go-billy/v5/fs.go
+++ b/vendor/github.com/go-git/go-billy/v5/fs.go
@@ -128,12 +128,18 @@ type Symlink interface {
Readlink(link string) (string, error)
}
-// Change abstract the FileInfo change related operations in a storage-agnostic
-// interface as an extension to the Basic interface
-type Change interface {
+// Chmod abstracts the logic around changing file modes.
+type Chmod interface {
// Chmod changes the mode of the named file to mode. If the file is a
// symbolic link, it changes the mode of the link's target.
Chmod(name string, mode os.FileMode) error
+}
+
+// Change abstract the FileInfo change related operations in a storage-agnostic
+// interface as an extension to the Basic interface
+type Change interface {
+ Chmod
+
// Lchown changes the numeric uid and gid of the named file. If the file is
// a symbolic link, it changes the uid and gid of the link itself.
Lchown(name string, uid, gid int) error
diff --git a/vendor/github.com/go-git/go-billy/v5/helper/chroot/chroot.go b/vendor/github.com/go-git/go-billy/v5/helper/chroot/chroot.go
index 8b44e784b..dbdf11186 100644
--- a/vendor/github.com/go-git/go-billy/v5/helper/chroot/chroot.go
+++ b/vendor/github.com/go-git/go-billy/v5/helper/chroot/chroot.go
@@ -1,6 +1,7 @@
package chroot
import (
+ "errors"
"os"
"path/filepath"
"strings"
@@ -200,6 +201,19 @@ func (fs *ChrootHelper) Readlink(link string) (string, error) {
return string(os.PathSeparator) + target, nil
}
+func (fs *ChrootHelper) Chmod(path string, mode os.FileMode) error {
+ fullpath, err := fs.underlyingPath(path)
+ if err != nil {
+ return err
+ }
+
+ c, ok := fs.underlying.(billy.Chmod)
+ if !ok {
+ return errors.New("underlying fs does not implement billy.Chmod")
+ }
+ return c.Chmod(fullpath, mode)
+}
+
func (fs *ChrootHelper) Chroot(path string) (billy.Filesystem, error) {
fullpath, err := fs.underlyingPath(path)
if err != nil {
diff --git a/vendor/github.com/go-git/go-billy/v5/helper/polyfill/polyfill.go b/vendor/github.com/go-git/go-billy/v5/helper/polyfill/polyfill.go
index 1efce0e7b..9fe131b3e 100644
--- a/vendor/github.com/go-git/go-billy/v5/helper/polyfill/polyfill.go
+++ b/vendor/github.com/go-git/go-billy/v5/helper/polyfill/polyfill.go
@@ -13,7 +13,7 @@ type Polyfill struct {
c capabilities
}
-type capabilities struct{ tempfile, dir, symlink, chroot bool }
+type capabilities struct{ tempfile, dir, symlink, chroot, chmod bool }
// New creates a new filesystem wrapping up 'fs' the intercepts all the calls
// made and errors if fs doesn't implement any of the billy interfaces.
@@ -28,6 +28,7 @@ func New(fs billy.Basic) billy.Filesystem {
_, h.c.dir = h.Basic.(billy.Dir)
_, h.c.symlink = h.Basic.(billy.Symlink)
_, h.c.chroot = h.Basic.(billy.Chroot)
+ _, h.c.chmod = h.Basic.(billy.Chmod)
return h
}
@@ -87,6 +88,14 @@ func (h *Polyfill) Chroot(path string) (billy.Filesystem, error) {
return h.Basic.(billy.Chroot).Chroot(path)
}
+func (h *Polyfill) Chmod(path string, mode os.FileMode) error {
+ if !h.c.chmod {
+ return billy.ErrNotSupported
+ }
+
+ return h.Basic.(billy.Chmod).Chmod(path, mode)
+}
+
func (h *Polyfill) Root() string {
if !h.c.chroot {
return string(filepath.Separator)
diff --git a/vendor/github.com/go-git/go-billy/v5/memfs/memory.go b/vendor/github.com/go-git/go-billy/v5/memfs/memory.go
index 6cbd7d08c..152cb9e4f 100644
--- a/vendor/github.com/go-git/go-billy/v5/memfs/memory.go
+++ b/vendor/github.com/go-git/go-billy/v5/memfs/memory.go
@@ -177,6 +177,10 @@ func (fs *Memory) Remove(filename string) error {
return fs.s.Remove(filename)
}
+func (fs *Memory) Chmod(path string, mode os.FileMode) error {
+ return fs.s.Chmod(path, mode)
+}
+
// Falls back to Go's filepath.Join, which works differently depending on the
// OS where the code is being executed.
func (fs *Memory) Join(elem ...string) string {
diff --git a/vendor/github.com/go-git/go-billy/v5/memfs/storage.go b/vendor/github.com/go-git/go-billy/v5/memfs/storage.go
index 16b48ce00..9960996b8 100644
--- a/vendor/github.com/go-git/go-billy/v5/memfs/storage.go
+++ b/vendor/github.com/go-git/go-billy/v5/memfs/storage.go
@@ -169,6 +169,18 @@ func (s *storage) Remove(path string) error {
return nil
}
+func (s *storage) Chmod(path string, mode os.FileMode) error {
+ path = clean(path)
+
+ f, has := s.Get(path)
+ if !has {
+ return os.ErrNotExist
+ }
+
+ f.mode = mode
+ return nil
+}
+
func clean(path string) string {
return filepath.Clean(filepath.FromSlash(path))
}
diff --git a/vendor/github.com/go-git/go-billy/v5/osfs/os_bound.go b/vendor/github.com/go-git/go-billy/v5/osfs/os_bound.go
index c0a610990..92ebc3dc7 100644
--- a/vendor/github.com/go-git/go-billy/v5/osfs/os_bound.go
+++ b/vendor/github.com/go-git/go-billy/v5/osfs/os_bound.go
@@ -126,6 +126,14 @@ func (fs *BoundOS) TempFile(dir, prefix string) (billy.File, error) {
if err != nil {
return nil, err
}
+
+ _, err = os.Stat(dir)
+ if err != nil && os.IsNotExist(err) {
+ err = os.MkdirAll(dir, defaultDirectoryMode)
+ if err != nil {
+ return nil, err
+ }
+ }
}
return tempFile(dir, prefix)
@@ -176,6 +184,14 @@ func (fs *BoundOS) Readlink(link string) (string, error) {
return os.Readlink(link)
}
+func (fs *BoundOS) Chmod(path string, mode os.FileMode) error {
+ abspath, err := fs.abs(path)
+ if err != nil {
+ return err
+ }
+ return os.Chmod(abspath, mode)
+}
+
// Chroot returns a new OS filesystem, with the base dir set to the
// result of joining the provided path with the underlying base dir.
func (fs *BoundOS) Chroot(path string) (billy.Filesystem, error) {
diff --git a/vendor/github.com/go-git/go-billy/v5/osfs/os_chroot.go b/vendor/github.com/go-git/go-billy/v5/osfs/os_chroot.go
index fd65e773c..413b3b898 100644
--- a/vendor/github.com/go-git/go-billy/v5/osfs/os_chroot.go
+++ b/vendor/github.com/go-git/go-billy/v5/osfs/os_chroot.go
@@ -74,6 +74,10 @@ func (fs *ChrootOS) Remove(filename string) error {
return os.Remove(filename)
}
+func (fs *ChrootOS) Chmod(path string, mode os.FileMode) error {
+ return os.Chmod(path, mode)
+}
+
func (fs *ChrootOS) TempFile(dir, prefix string) (billy.File, error) {
if err := fs.createDir(dir + string(os.PathSeparator)); err != nil {
return nil, err
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/decoder.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/decoder.go
index 867553c68..9e006a726 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/decoder.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/idxfile/decoder.go
@@ -91,8 +91,8 @@ func readVersion(idx *MemoryIndex, r io.Reader) error {
return err
}
- if v > VersionSupported {
- return ErrUnsupportedVersion
+ if v != VersionSupported {
+ return fmt.Errorf("%w: v%d", ErrUnsupportedVersion, v)
}
idx.Version = v
@@ -106,6 +106,10 @@ func readFanout(idx *MemoryIndex, r io.Reader) error {
return err
}
+ if k > 0 && n < idx.Fanout[k-1] {
+ return fmt.Errorf("%w: fanout table is not monotonically non-decreasing at entry %d", ErrMalformedIdxFile, k)
+ }
+
idx.Fanout[k] = n
idx.FanoutMapping[k] = noMapping
}
@@ -155,7 +159,7 @@ func readCRC32(idx *MemoryIndex, r io.Reader) error {
}
func readOffsets(idx *MemoryIndex, r io.Reader) error {
- var o64cnt int
+ var o64cnt int64
for k := 0; k < fanout; k++ {
if pos := idx.FanoutMapping[k]; pos != noMapping {
if _, err := io.ReadFull(r, idx.Offset32[pos]); err != nil {
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/decoder.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/decoder.go
index fc25d3702..a1bdf00a2 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/decoder.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/decoder.go
@@ -4,8 +4,8 @@ import (
"bufio"
"bytes"
"errors"
+ "fmt"
"io"
-
"strconv"
"time"
@@ -26,12 +26,14 @@ var (
ErrInvalidChecksum = errors.New("invalid checksum")
// ErrUnknownExtension is returned when an index extension is encountered that is considered mandatory
ErrUnknownExtension = errors.New("unknown extension")
+ // ErrMalformedIndexFile is returned when the index file contents are
+ // structurally invalid.
+ ErrMalformedIndexFile = errors.New("index decoder: malformed index file")
)
const (
entryHeaderLength = 62
entryExtended = 0x4000
- entryValid = 0x8000
nameMask = 0xfff
intentToAddMask = 1 << 13
skipWorkTreeMask = 1 << 14
@@ -140,33 +142,55 @@ func (d *Decoder) readEntry(idx *Index) (*Entry, error) {
e.SkipWorktree = extended&skipWorkTreeMask != 0
}
- if err := d.readEntryName(idx, e, flags); err != nil {
+ nameConsumed, err := d.readEntryName(idx, e, flags)
+ if err != nil {
return nil, err
}
- return e, d.padEntry(idx, e, read)
+ return e, d.padEntry(idx, e, read, nameConsumed)
}
-func (d *Decoder) readEntryName(idx *Index, e *Entry, flags uint16) error {
- var name string
- var err error
-
+// readEntryName reads the entry path and sets e.Name. It returns the
+// number of bytes consumed from the stream for the name portion.
+func (d *Decoder) readEntryName(idx *Index, e *Entry, flags uint16) (int, error) {
switch idx.Version {
case 2, 3:
- len := flags & nameMask
- name, err = d.doReadEntryName(len)
+ nameLen := flags & nameMask
+ name, consumed, err := d.doReadEntryName(nameLen)
+ if err != nil {
+ return 0, err
+ }
+ e.Name = name
+ return consumed, nil
case 4:
- name, err = d.doReadEntryNameV4()
+ name, err := d.doReadEntryNameV4()
+ if err != nil {
+ return 0, err
+ }
+ e.Name = name
+ return 0, nil // V4 has no padding; consumed count unused
default:
- return ErrUnsupportedVersion
+ return 0, ErrUnsupportedVersion
+ }
+}
+
+// doReadEntryName reads the entry path for V2/V3 indexes. It returns the
+// name, the number of bytes consumed from the stream, and any error.
+// When nameLen equals nameMask (0xFFF), the name was too long to fit in
+// the 12-bit field and the real length is found by scanning for the NUL
+// terminator — matching C Git's strlen(name) fallback in create_from_disk.
+func (d *Decoder) doReadEntryName(nameLen uint16) (string, int, error) {
+ if nameLen == nameMask {
+ name, err := binary.ReadUntil(d.r, '\x00')
+ if err != nil {
+ return "", 0, err
+ }
+ return string(name), len(name) + 1, nil // +1 for the consumed NUL delimiter
}
- if err != nil {
- return err
- }
-
- e.Name = name
- return nil
+ name := make([]byte, nameLen)
+ _, err := io.ReadFull(d.r, name)
+ return string(name), int(nameLen), err
}
func (d *Decoder) doReadEntryNameV4() (string, error) {
@@ -177,7 +201,14 @@ func (d *Decoder) doReadEntryNameV4() (string, error) {
var base string
if d.lastEntry != nil {
+ if l < 0 || int(l) > len(d.lastEntry.Name) {
+ return "", fmt.Errorf("%w: invalid V4 entry name strip length %d (previous name length: %d)",
+ ErrMalformedIndexFile, l, len(d.lastEntry.Name))
+ }
base = d.lastEntry.Name[:len(d.lastEntry.Name)-int(l)]
+ } else if l > 0 {
+ return "", fmt.Errorf("%w: non-zero strip length %d on first V4 entry",
+ ErrMalformedIndexFile, l)
}
name, err := binary.ReadUntil(d.r, '\x00')
@@ -188,24 +219,23 @@ func (d *Decoder) doReadEntryNameV4() (string, error) {
return base + string(name), nil
}
-func (d *Decoder) doReadEntryName(len uint16) (string, error) {
- name := make([]byte, len)
- _, err := io.ReadFull(d.r, name)
-
- return string(name), err
-}
-
-// Index entries are padded out to the next 8 byte alignment
-// for historical reasons related to how C Git read the files.
-func (d *Decoder) padEntry(idx *Index, e *Entry, read int) error {
+// padEntry discards NUL padding bytes that follow each V2/V3 entry on
+// disk. nameConsumed is the number of stream bytes consumed while reading
+// the entry name (which may exceed len(e.Name) when a NUL terminator was
+// consumed for long names where the 12-bit length field overflowed).
+func (d *Decoder) padEntry(idx *Index, e *Entry, read, nameConsumed int) error {
if idx.Version == 4 {
return nil
}
entrySize := read + len(e.Name)
padLen := 8 - entrySize%8
- _, err := io.CopyN(io.Discard, d.r, int64(padLen))
- return err
+ padLen -= nameConsumed - len(e.Name)
+ if padLen > 0 {
+ _, err := io.CopyN(io.Discard, d.r, int64(padLen))
+ return err
+ }
+ return nil
}
func (d *Decoder) readExtensions(idx *Index) error {
@@ -312,7 +342,7 @@ func (d *Decoder) readChecksum(expected []byte) error {
}
func validateHeader(r io.Reader) (version uint32, err error) {
- var s = make([]byte, 4)
+ s := make([]byte, 4)
if _, err := io.ReadFull(r, s); err != nil {
return 0, err
}
@@ -376,24 +406,26 @@ func (d *treeExtensionDecoder) readEntry() (*TreeEntry, error) {
return nil, err
}
- // An entry can be in an invalidated state and is represented by having a
- // negative number in the entry_count field.
- if i == -1 {
- return nil, nil
- }
-
e.Entries = i
trees, err := binary.ReadUntil(d.r, '\n')
if err != nil {
return nil, err
}
- i, err = strconv.Atoi(string(trees))
+ subtrees, err := strconv.Atoi(string(trees))
if err != nil {
return nil, err
}
- e.Trees = i
+ e.Trees = subtrees
+
+ // An entry can be in an invalidated state and is represented by having a
+ // negative number in the entry_count field. In this case, there is no
+ // object name and the next entry starts immediately after the newline.
+ if i < 0 {
+ return nil, nil
+ }
+
_, err = io.ReadFull(d.r, e.Hash[:])
if err != nil {
return nil, err
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/encoder.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/encoder.go
index c232e0323..161bd97ff 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/encoder.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/encoder.go
@@ -5,9 +5,7 @@ import (
"errors"
"fmt"
"io"
- "path"
"sort"
- "strings"
"time"
"github.com/go-git/go-git/v5/plumbing/hash"
@@ -160,26 +158,39 @@ func (e *Encoder) encodeEntryName(entry *Entry) error {
}
func (e *Encoder) encodeEntryNameV4(entry *Entry) error {
- name := entry.Name
- l := 0
+ // V4 prefix compression: find the longest common prefix between the
+ // previous entry's name and the current one. The strip length tells
+ // the decoder how many bytes to remove from the end of the previous
+ // name, and the suffix is the remainder of the current name.
+ prefix := 0
if e.lastEntry != nil {
- dir := path.Dir(e.lastEntry.Name) + "/"
- if strings.HasPrefix(entry.Name, dir) {
- l = len(e.lastEntry.Name) - len(dir)
- name = strings.TrimPrefix(entry.Name, dir)
- } else {
- l = len(e.lastEntry.Name)
- }
+ prefix = commonPrefixLen(e.lastEntry.Name, entry.Name)
+ }
+ stripLen := 0
+ if e.lastEntry != nil {
+ stripLen = len(e.lastEntry.Name) - prefix
}
e.lastEntry = entry
- err := binary.WriteVariableWidthInt(e.w, int64(l))
- if err != nil {
+ if err := binary.WriteVariableWidthInt(e.w, int64(stripLen)); err != nil {
return err
}
- return binary.Write(e.w, []byte(name+string('\x00')))
+ suffix := entry.Name[prefix:]
+ return binary.Write(e.w, append([]byte(suffix), '\x00'))
+}
+
+// commonPrefixLen returns the length of the longest common byte prefix
+// between a and b.
+func commonPrefixLen(a, b string) int {
+ n := min(len(b), len(a))
+ for i := range n {
+ if a[i] != b[i] {
+ return i
+ }
+ }
+ return n
}
func (e *Encoder) encodeRawExtension(signature string, data []byte) error {
diff --git a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/index.go b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/index.go
index f4c7647d3..30a7e1410 100644
--- a/vendor/github.com/go-git/go-git/v5/plumbing/format/index/index.go
+++ b/vendor/github.com/go-git/go-git/v5/plumbing/format/index/index.go
@@ -54,6 +54,8 @@ type Index struct {
ResolveUndo *ResolveUndo
// EndOfIndexEntry represents the 'End of Index Entry' extension
EndOfIndexEntry *EndOfIndexEntry
+ // ModTime is the modification time of the index file
+ ModTime time.Time
}
// Add creates a new Entry and returns it. The caller should first check that
diff --git a/vendor/github.com/go-git/go-git/v5/repository.go b/vendor/github.com/go-git/go-git/v5/repository.go
index 401590544..e0cefc491 100644
--- a/vendor/github.com/go-git/go-git/v5/repository.go
+++ b/vendor/github.com/go-git/go-git/v5/repository.go
@@ -208,6 +208,12 @@ func Open(s storage.Storer, worktree billy.Filesystem) (*Repository, error) {
return nil, ErrRepositoryNotExists
}
+ cfg, err := s.Config()
+ if err != nil {
+ return nil, err
+ }
+
+ err = verifyExtensions(s, cfg)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/go-git/go-git/v5/repository_extensions.go b/vendor/github.com/go-git/go-git/v5/repository_extensions.go
new file mode 100644
index 000000000..635d9aaa9
--- /dev/null
+++ b/vendor/github.com/go-git/go-git/v5/repository_extensions.go
@@ -0,0 +1,121 @@
+package git
+
+import (
+ "errors"
+ "fmt"
+ "strings"
+
+ "github.com/go-git/go-git/v5/config"
+ cfgformat "github.com/go-git/go-git/v5/plumbing/format/config"
+ "github.com/go-git/go-git/v5/storage"
+)
+
+var (
+ // ErrUnsupportedExtensionRepositoryFormatVersion represents when an
+ // extension being used is not compatible with the repository's
+ // core.repositoryFormatVersion.
+ ErrUnsupportedExtensionRepositoryFormatVersion = errors.New("core.repositoryformatversion does not support extension")
+
+ // ErrUnsupportedRepositoryFormatVersion represents when an repository
+ // is using a format version that is not supported.
+ ErrUnsupportedRepositoryFormatVersion = errors.New("core.repositoryformatversion not supported")
+
+ // ErrUnknownExtension represents when a repository has an extension
+ // which is unknown or unsupported by go-git.
+ ErrUnknownExtension = errors.New("unknown extension")
+
+ // builtinExtensions defines the Git extensions that are supported by
+ // the core go-git implementation.
+ //
+ // Some extensions are storage-specific, those are defined by the Storers
+ // themselves by implementing the ExtensionChecker interface.
+ builtinExtensions = map[string]struct{}{
+ // noop does not change git’s behavior at all.
+ // It is useful only for testing format-1 compatibility.
+ //
+ // This extension is respected regardless of the
+ // core.repositoryFormatVersion setting.
+ "noop": {},
+
+ // noop-v1 does not change git’s behavior at all.
+ // It is useful only for testing format-1 compatibility.
+ "noop-v1": {},
+ }
+
+ // Some Git extensions were supported upstream before the introduction
+ // of repositoryformatversion. These are the only extensions that can be
+ // enabled while core.repositoryformatversion is unset or set to 0.
+ extensionsValidForV0 = map[string]struct{}{
+ "noop": {},
+ "partialClone": {},
+ "preciousObjects": {},
+ "worktreeConfig": {},
+ }
+)
+
+type extension struct {
+ name string
+ value string
+}
+
+func extensions(cfg *config.Config) []extension {
+ if cfg == nil || cfg.Raw == nil {
+ return nil
+ }
+
+ if !cfg.Raw.HasSection("extensions") {
+ return nil
+ }
+
+ section := cfg.Raw.Section("extensions")
+ out := make([]extension, 0, len(section.Options))
+ for _, opt := range section.Options {
+ out = append(out, extension{name: strings.ToLower(opt.Key), value: strings.ToLower(opt.Value)})
+ }
+
+ return out
+}
+
+func verifyExtensions(st storage.Storer, cfg *config.Config) error {
+ needed := extensions(cfg)
+
+ switch cfg.Core.RepositoryFormatVersion {
+ case "", cfgformat.Version_0, cfgformat.Version_1:
+ default:
+ return fmt.Errorf("%w: %q",
+ ErrUnsupportedRepositoryFormatVersion,
+ cfg.Core.RepositoryFormatVersion)
+ }
+
+ if len(needed) > 0 {
+ if cfg.Core.RepositoryFormatVersion == cfgformat.Version_0 ||
+ cfg.Core.RepositoryFormatVersion == "" {
+ var unsupported []string
+ for _, ext := range needed {
+ if _, ok := extensionsValidForV0[ext.name]; !ok {
+ unsupported = append(unsupported, ext.name)
+ }
+ }
+ if len(unsupported) > 0 {
+ return fmt.Errorf("%w: %s",
+ ErrUnsupportedExtensionRepositoryFormatVersion,
+ strings.Join(unsupported, ", "))
+ }
+ }
+
+ var missing []string
+ for _, ext := range needed {
+ if _, ok := builtinExtensions[ext.name]; ok {
+ continue
+ }
+
+ missing = append(missing, ext.name)
+ }
+
+ if len(missing) > 0 {
+ return fmt.Errorf("%w: %s", ErrUnknownExtension, strings.Join(missing, ", "))
+ }
+ }
+
+ return nil
+}
diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/writers.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/writers.go
index 849b7a176..fcd02abdb 100644
--- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/writers.go
+++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/writers.go
@@ -3,6 +3,7 @@ package dotgit
import (
"fmt"
"io"
+ "os"
"sync/atomic"
"github.com/go-git/go-git/v5/plumbing"
@@ -137,14 +138,22 @@ func (w *PackWriter) save() error {
}
if err := w.encodeIdx(idx); err != nil {
+ _ = idx.Close()
return err
}
if err := idx.Close(); err != nil {
return err
}
+ fixPermissions(w.fs, fmt.Sprintf("%s.idx", base))
- return w.fs.Rename(w.fw.Name(), fmt.Sprintf("%s.pack", base))
+ packPath := fmt.Sprintf("%s.pack", base)
+ if err := w.fs.Rename(w.fw.Name(), packPath); err != nil {
+ return err
+ }
+ fixPermissions(w.fs, packPath)
+
+ return nil
}
func (w *PackWriter) encodeIdx(writer io.Writer) error {
@@ -281,5 +290,17 @@ func (w *ObjectWriter) save() error {
hex := w.Hash().String()
file := w.fs.Join(objectsPath, hex[0:2], hex[2:hash.HexSize])
- return w.fs.Rename(w.f.Name(), file)
+ // Loose objects are content addressable, if they already exist
+ // we can safely delete the temporary file and short-circuit the
+ // operation.
+ if _, err := w.fs.Stat(file); err == nil || os.IsExist(err) {
+ return w.fs.Remove(w.f.Name())
+ }
+
+ if err := w.fs.Rename(w.f.Name(), file); err != nil {
+ return err
+ }
+ fixPermissions(w.fs, file)
+
+ return nil
}
diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/writers_unix.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/writers_unix.go
new file mode 100644
index 000000000..134a2586f
--- /dev/null
+++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/writers_unix.go
@@ -0,0 +1,29 @@
+//go:build !windows
+
+package dotgit
+
+import (
+ "github.com/go-git/go-billy/v5"
+ "github.com/go-git/go-git/v5/utils/trace"
+)
+
+func fixPermissions(fs billy.Filesystem, path string) {
+ if chmodFS, ok := fs.(billy.Chmod); ok {
+ if err := chmodFS.Chmod(path, 0o444); err != nil {
+ trace.General.Printf("failed to chmod %s: %v", path, err)
+ }
+ }
+}
+
+func isReadOnly(fs billy.Filesystem, path string) (bool, error) {
+ fi, err := fs.Stat(path)
+ if err != nil {
+ return false, err
+ }
+
+ if fi.Mode().Perm() == 0o444 {
+ return true, nil
+ }
+
+ return false, nil
+}
diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/writers_windows.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/writers_windows.go
new file mode 100644
index 000000000..c22abcce9
--- /dev/null
+++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/dotgit/writers_windows.go
@@ -0,0 +1,58 @@
+//go:build windows
+
+package dotgit
+
+import (
+ "fmt"
+ "path/filepath"
+
+ "github.com/go-git/go-billy/v5"
+ "github.com/go-git/go-git/v5/utils/trace"
+ "golang.org/x/sys/windows"
+)
+
+func fixPermissions(fs billy.Filesystem, path string) {
+ fullpath := filepath.Join(fs.Root(), path)
+ p, err := windows.UTF16PtrFromString(fullpath)
+ if err != nil {
+ trace.General.Printf("failed to chmod %s: %v", fullpath, err)
+ return
+ }
+
+ attrs, err := windows.GetFileAttributes(p)
+ if err != nil {
+ trace.General.Printf("failed to chmod %s: %v", fullpath, err)
+ return
+ }
+
+ if attrs&windows.FILE_ATTRIBUTE_READONLY != 0 {
+ return
+ }
+
+ err = windows.SetFileAttributes(p,
+ attrs|windows.FILE_ATTRIBUTE_READONLY,
+ )
+
+ if err != nil {
+ trace.General.Printf("failed to chmod %s: %v", fullpath, err)
+ }
+}
+
+func isReadOnly(fs billy.Filesystem, path string) (bool, error) {
+ fullpath := filepath.Join(fs.Root(), path)
+ p, err := windows.UTF16PtrFromString(fullpath)
+ if err != nil {
+ return false, fmt.Errorf("%w: %q", err, fullpath)
+ }
+
+ attrs, err := windows.GetFileAttributes(p)
+ if err != nil {
+ return false, fmt.Errorf("%w: %q", err, fullpath)
+ }
+
+ if attrs&windows.FILE_ATTRIBUTE_READONLY != 0 {
+ return true, nil
+ }
+
+ return false, nil
+}
diff --git a/vendor/github.com/go-git/go-git/v5/storage/filesystem/index.go b/vendor/github.com/go-git/go-git/v5/storage/filesystem/index.go
index a86ef3e2e..b5b9f95f7 100644
--- a/vendor/github.com/go-git/go-git/v5/storage/filesystem/index.go
+++ b/vendor/github.com/go-git/go-git/v5/storage/filesystem/index.go
@@ -48,6 +48,11 @@ func (s *IndexStorage) Index() (i *index.Index, err error) {
defer ioutil.CheckClose(f, &err)
+ fi, statErr := s.dir.Fs().Stat(f.Name())
+ if statErr == nil {
+ idx.ModTime = fi.ModTime()
+ }
+
d := index.NewDecoder(f)
err = d.Decode(idx)
return idx, err
diff --git a/vendor/github.com/go-git/go-git/v5/storage/memory/storage.go b/vendor/github.com/go-git/go-git/v5/storage/memory/storage.go
index 79211c7c0..b5d0aa7d3 100644
--- a/vendor/github.com/go-git/go-git/v5/storage/memory/storage.go
+++ b/vendor/github.com/go-git/go-git/v5/storage/memory/storage.go
@@ -69,7 +69,11 @@ type IndexStorage struct {
index *index.Index
}
+// SetIndex stores the given index.
+// Note: this method sets idx.ModTime to simulate filesystem storage behavior.
func (c *IndexStorage) SetIndex(idx *index.Index) error {
+ // Set ModTime to enable racy git detection in the metadata optimization.
+ idx.ModTime = time.Now()
c.index = idx
return nil
}
diff --git a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/filesystem/node.go b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/filesystem/node.go
index 33800627d..83df4dd3a 100644
--- a/vendor/github.com/go-git/go-git/v5/utils/merkletrie/filesystem/node.go
+++ b/vendor/github.com/go-git/go-git/v5/utils/merkletrie/filesystem/node.go
@@ -4,9 +4,11 @@ import (
"io"
"os"
"path"
+ "time"
"github.com/go-git/go-git/v5/plumbing"
"github.com/go-git/go-git/v5/plumbing/filemode"
+ "github.com/go-git/go-git/v5/plumbing/format/index"
"github.com/go-git/go-git/v5/utils/merkletrie/noder"
"github.com/go-git/go-billy/v5"
@@ -16,6 +18,14 @@ var ignore = map[string]bool{
".git": true,
}
+// Options contains configuration for the filesystem node.
+type Options struct {
+ // Index is used to enable the metadata-first comparison optimization while
+ // correctly handling the "racy git" condition. If no index is provided,
+ // the function works without the optimization.
+ Index *index.Index
+}
+
// The node represents a file or a directory in a billy.Filesystem. It
// implements the interface noder.Noder of merkletrie package.
//
@@ -24,6 +34,8 @@ var ignore = map[string]bool{
type node struct {
fs billy.Filesystem
submodules map[string]plumbing.Hash
+ idx *index.Index
+ idxMap map[string]*index.Entry
path string
hash []byte
@@ -31,6 +43,7 @@ type node struct {
isDir bool
mode os.FileMode
size int64
+ modTime time.Time
}
// NewRootNode returns the root node based on a given billy.Filesystem.
@@ -42,7 +55,41 @@ func NewRootNode(
fs billy.Filesystem,
submodules map[string]plumbing.Hash,
) noder.Noder {
- return &node{fs: fs, submodules: submodules, isDir: true}
+ return NewRootNodeWithOptions(fs, submodules, Options{})
+}
+
+// NewRootNodeWithOptions returns the root node based on a given billy.Filesystem
+// with options to set an index. Providing an index enables the metadata-first
+// comparison optimization while correctly handling the "racy git" condition. If
+// no index is provided, the function works without the optimization.
+//
+// The index's ModTime field is used to detect the racy git condition. When a file's
+// mtime equals or is newer than the index ModTime, we must hash the file content
+// even if other metadata matches, because the file may have been modified in the
+// same second that the index was written.
+//
+// Reference: https://git-scm.com/docs/racy-git
+func NewRootNodeWithOptions(
+ fs billy.Filesystem,
+ submodules map[string]plumbing.Hash,
+ options Options,
+) noder.Noder {
+ var idxMap map[string]*index.Entry
+
+ if options.Index != nil {
+ idxMap = make(map[string]*index.Entry, len(options.Index.Entries))
+ for _, entry := range options.Index.Entries {
+ idxMap[entry.Name] = entry
+ }
+ }
+
+ return &node{
+ fs: fs,
+ submodules: submodules,
+ idx: options.Index,
+ idxMap: idxMap,
+ isDir: true,
+ }
}
// Hash the hash of a filesystem is the result of concatenating the computed
@@ -133,11 +180,14 @@ func (n *node) newChildNode(file os.FileInfo) (*node, error) {
node := &node{
fs: n.fs,
submodules: n.submodules,
+ idx: n.idx,
+ idxMap: n.idxMap,
- path: path,
- isDir: file.IsDir(),
- size: file.Size(),
- mode: file.Mode(),
+ path: path,
+ isDir: file.IsDir(),
+ size: file.Size(),
+ mode: file.Mode(),
+ modTime: file.ModTime(),
}
if _, isSubmodule := n.submodules[path]; isSubmodule {
@@ -161,6 +211,16 @@ func (n *node) calculateHash() {
n.hash = append(submoduleHash[:], filemode.Submodule.Bytes()...)
return
}
+
+ if n.idxMap != nil {
+ if entry, ok := n.idxMap[n.path]; ok {
+ if n.metadataMatches(entry) {
+ n.hash = append(entry.Hash[:], mode.Bytes()...)
+ return
+ }
+ }
+ }
+
var hash plumbing.Hash
if n.mode&os.ModeSymlink != 0 {
hash = n.doCalculateHashForSymlink()
@@ -170,6 +230,44 @@ func (n *node) calculateHash() {
n.hash = append(hash[:], mode.Bytes()...)
}
+func (n *node) metadataMatches(entry *index.Entry) bool {
+ if entry == nil {
+ return false
+ }
+
+ if uint32(n.size) != entry.Size {
+ return false
+ }
+
+ if !n.modTime.IsZero() && !n.modTime.Equal(entry.ModifiedAt) {
+ return false
+ }
+
+ mode, err := filemode.NewFromOSFileMode(n.mode)
+ if err != nil {
+ return false
+ }
+
+ if mode != entry.Mode {
+ return false
+ }
+
+ if n.idx != nil && !n.idx.ModTime.IsZero() && !n.modTime.IsZero() {
+ if !n.modTime.Before(n.idx.ModTime) {
+ return false
+ }
+ }
+
+ // If we couldn't perform the racy git check (idx is nil or idx.ModTime is zero),
+ // we cannot safely rely on metadata alone — force content hashing.
+ // This can occur with in-memory storage where the index file timestamp is unavailable.
+ if n.idx == nil || n.idx.ModTime.IsZero() {
+ return false
+ }
+
+ return true
+}
+
func (n *node) doCalculateHashForRegular() plumbing.Hash {
f, err := n.fs.Open(n.path)
if err != nil {
diff --git a/vendor/github.com/go-git/go-git/v5/worktree.go b/vendor/github.com/go-git/go-git/v5/worktree.go
index 5e9cd7bd9..55d7ebb1b 100644
--- a/vendor/github.com/go-git/go-git/v5/worktree.go
+++ b/vendor/github.com/go-git/go-git/v5/worktree.go
@@ -385,7 +385,8 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) ([]
return nil, err
}
- var removedFiles []string
+ removedFiles := make([]string, 0, len(changes))
+ filesMap := buildFilePathMap(files)
for _, ch := range changes {
a, err := ch.Action()
if err != nil {
@@ -407,7 +408,7 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) ([]
}
if len(files) > 0 {
- contains := inFiles(files, name)
+ contains := inFiles(filesMap, name)
if !contains {
continue
}
@@ -436,15 +437,11 @@ func (w *Worktree) resetIndex(t *object.Tree, dirs []string, files []string) ([]
return removedFiles, w.r.Storer.SetIndex(idx)
}
-func inFiles(files []string, v string) bool {
+// inFiles checks if the given file is in the list of files. The incoming filepaths in files should be cleaned before calling this function.
+func inFiles(files map[string]struct{}, v string) bool {
v = filepath.Clean(v)
- for _, s := range files {
- if filepath.Clean(s) == v {
- return true
- }
- }
-
- return false
+ _, exists := files[v]
+ return exists
}
func (w *Worktree) resetWorktree(t *object.Tree, files []string) error {
@@ -459,6 +456,7 @@ func (w *Worktree) resetWorktree(t *object.Tree, files []string) error {
}
b := newIndexBuilder(idx)
+ filesMap := buildFilePathMap(files)
for _, ch := range changes {
if err := w.validChange(ch); err != nil {
return err
@@ -476,7 +474,7 @@ func (w *Worktree) resetWorktree(t *object.Tree, files []string) error {
continue
}
- contains := inFiles(files, file)
+ contains := inFiles(filesMap, file)
if !contains {
continue
}
@@ -1206,3 +1204,16 @@ func (b *indexBuilder) Add(e *index.Entry) {
func (b *indexBuilder) Remove(name string) {
delete(b.entries, filepath.ToSlash(name))
}
+
+// buildFilePathMap creates a map of cleaned file paths for efficient lookup.
+// Returns nil if the input slice is empty.
+func buildFilePathMap(files []string) map[string]struct{} {
+ if len(files) == 0 {
+ return nil
+ }
+ filesMap := make(map[string]struct{}, len(files))
+ for _, f := range files {
+ filesMap[filepath.Clean(f)] = struct{}{}
+ }
+ return filesMap
+}
diff --git a/vendor/github.com/go-git/go-git/v5/worktree_status.go b/vendor/github.com/go-git/go-git/v5/worktree_status.go
index 7870d138d..e7a60747b 100644
--- a/vendor/github.com/go-git/go-git/v5/worktree_status.go
+++ b/vendor/github.com/go-git/go-git/v5/worktree_status.go
@@ -141,7 +141,7 @@ func (w *Worktree) diffStagingWithWorktree(reverse, excludeIgnoredChanges bool)
return nil, err
}
- to := filesystem.NewRootNode(w.Filesystem, submodules)
+ to := filesystem.NewRootNodeWithOptions(w.Filesystem, submodules, filesystem.Options{Index: idx})
var c merkletrie.Changes
if reverse {
diff --git a/vendor/github.com/go-jose/go-jose/v4/asymmetric.go b/vendor/github.com/go-jose/go-jose/v4/asymmetric.go
index f8d5774ef..7784cd458 100644
--- a/vendor/github.com/go-jose/go-jose/v4/asymmetric.go
+++ b/vendor/github.com/go-jose/go-jose/v4/asymmetric.go
@@ -414,6 +414,9 @@ func (ctx ecKeyGenerator) genKey() ([]byte, rawHeader, error) {
// Decrypt the given payload and return the content encryption key.
func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
+ if recipient == nil {
+ return nil, errors.New("go-jose/go-jose: missing recipient")
+ }
epk, err := headers.getEPK()
if err != nil {
return nil, errors.New("go-jose/go-jose: invalid epk header")
@@ -461,13 +464,18 @@ func (ctx ecDecrypterSigner) decryptKey(headers rawHeader, recipient *recipientI
return nil, ErrUnsupportedAlgorithm
}
+ encryptedKey := recipient.encryptedKey
+ if len(encryptedKey) == 0 {
+ return nil, errors.New("go-jose/go-jose: missing JWE Encrypted Key")
+ }
+
key := deriveKey(string(algorithm), keySize)
block, err := aes.NewCipher(key)
if err != nil {
return nil, err
}
- return josecipher.KeyUnwrap(block, recipient.encryptedKey)
+ return josecipher.KeyUnwrap(block, encryptedKey)
}
func (ctx edDecrypterSigner) signPayload(payload []byte, alg SignatureAlgorithm) (Signature, error) {
diff --git a/vendor/github.com/go-jose/go-jose/v4/cipher/key_wrap.go b/vendor/github.com/go-jose/go-jose/v4/cipher/key_wrap.go
index b9effbca8..a2f86e3db 100644
--- a/vendor/github.com/go-jose/go-jose/v4/cipher/key_wrap.go
+++ b/vendor/github.com/go-jose/go-jose/v4/cipher/key_wrap.go
@@ -66,12 +66,20 @@ func KeyWrap(block cipher.Block, cek []byte) ([]byte, error) {
}
// KeyUnwrap implements NIST key unwrapping; it unwraps a content encryption key (cek) with the given block cipher.
+//
+// https://datatracker.ietf.org/doc/html/rfc7518#section-4.4
+// https://datatracker.ietf.org/doc/html/rfc7518#section-4.6
+// https://datatracker.ietf.org/doc/html/rfc7518#section-4.8
func KeyUnwrap(block cipher.Block, ciphertext []byte) ([]byte, error) {
+ n := (len(ciphertext) / 8) - 1
+ if n <= 0 {
+ return nil, errors.New("go-jose/go-jose: JWE Encrypted Key too short")
+ }
+
if len(ciphertext)%8 != 0 {
return nil, errors.New("go-jose/go-jose: key wrap input must be 8 byte blocks")
}
- n := (len(ciphertext) / 8) - 1
r := make([][]byte, n)
for i := range r {
diff --git a/vendor/github.com/go-jose/go-jose/v4/symmetric.go b/vendor/github.com/go-jose/go-jose/v4/symmetric.go
index 09efefb26..f2ff29e17 100644
--- a/vendor/github.com/go-jose/go-jose/v4/symmetric.go
+++ b/vendor/github.com/go-jose/go-jose/v4/symmetric.go
@@ -366,11 +366,21 @@ func (ctx *symmetricKeyCipher) encryptKey(cek []byte, alg KeyAlgorithm) (recipie
// Decrypt the content encryption key.
func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipientInfo, generator keyGenerator) ([]byte, error) {
- switch headers.getAlgorithm() {
- case DIRECT:
- cek := make([]byte, len(ctx.key))
- copy(cek, ctx.key)
- return cek, nil
+ if recipient == nil {
+ return nil, fmt.Errorf("go-jose/go-jose: missing recipient")
+ }
+
+ alg := headers.getAlgorithm()
+ if alg == DIRECT {
+ return bytes.Clone(ctx.key), nil
+ }
+
+ encryptedKey := recipient.encryptedKey
+ if len(encryptedKey) == 0 {
+ return nil, fmt.Errorf("go-jose/go-jose: missing JWE Encrypted Key")
+ }
+
+ switch alg {
case A128GCMKW, A192GCMKW, A256GCMKW:
aead := newAESGCM(len(ctx.key))
@@ -385,7 +395,7 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
parts := &aeadParts{
iv: iv.bytes(),
- ciphertext: recipient.encryptedKey,
+ ciphertext: encryptedKey,
tag: tag.bytes(),
}
@@ -401,7 +411,7 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
return nil, err
}
- cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey)
+ cek, err := josecipher.KeyUnwrap(block, encryptedKey)
if err != nil {
return nil, err
}
@@ -445,7 +455,7 @@ func (ctx *symmetricKeyCipher) decryptKey(headers rawHeader, recipient *recipien
return nil, err
}
- cek, err := josecipher.KeyUnwrap(block, recipient.encryptedKey)
+ cek, err := josecipher.KeyUnwrap(block, encryptedKey)
if err != nil {
return nil, err
}
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/loader/loader.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/loader/loader.go
index 380a7689c..a77d18352 100644
--- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/loader/loader.go
+++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/loader/loader.go
@@ -26,10 +26,8 @@ import (
_ "github.com/opencloud-eu/reva/v2/pkg/auth/manager/json"
_ "github.com/opencloud-eu/reva/v2/pkg/auth/manager/ldap"
_ "github.com/opencloud-eu/reva/v2/pkg/auth/manager/machine"
- _ "github.com/opencloud-eu/reva/v2/pkg/auth/manager/nextcloud"
_ "github.com/opencloud-eu/reva/v2/pkg/auth/manager/ocmshares"
_ "github.com/opencloud-eu/reva/v2/pkg/auth/manager/oidc"
- _ "github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql"
_ "github.com/opencloud-eu/reva/v2/pkg/auth/manager/publicshares"
_ "github.com/opencloud-eu/reva/v2/pkg/auth/manager/serviceaccounts"
// Add your own here
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/nextcloud/nextcloud.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/nextcloud/nextcloud.go
deleted file mode 100644
index 43e7942ba..000000000
--- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/nextcloud/nextcloud.go
+++ /dev/null
@@ -1,197 +0,0 @@
-// Copyright 2018-2021 CERN
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// 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.
-//
-// In applying this license, CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-// Package nextcloud verifies a clientID and clientSecret against a Nextcloud backend.
-package nextcloud
-
-import (
- "context"
- "encoding/json"
- "io"
- "net/http"
- "strings"
-
- authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
- user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
- "github.com/mitchellh/mapstructure"
- "github.com/opencloud-eu/reva/v2/pkg/appctx"
- "github.com/opencloud-eu/reva/v2/pkg/auth"
- "github.com/opencloud-eu/reva/v2/pkg/auth/manager/registry"
- "github.com/pkg/errors"
-)
-
-func init() {
- registry.Register("nextcloud", New)
-}
-
-// Manager is the Nextcloud-based implementation of the auth.Manager interface
-// see https://github.com/cs3org/reva/blob/v1.13.0/pkg/auth/auth.go#L32-L35
-type Manager struct {
- client *http.Client
- sharedSecret string
- endPoint string
-}
-
-// AuthManagerConfig contains config for a Nextcloud-based AuthManager
-type AuthManagerConfig struct {
- EndPoint string `mapstructure:"endpoint" docs:";The Nextcloud backend endpoint for user check"`
- SharedSecret string `mapstructure:"shared_secret"`
- MockHTTP bool `mapstructure:"mock_http"`
-}
-
-// Action describes a REST request to forward to the Nextcloud backend
-type Action struct {
- verb string
- username string
- argS string
-}
-
-func (c *AuthManagerConfig) init() {
-}
-
-func parseConfig(m map[string]interface{}) (*AuthManagerConfig, error) {
- c := &AuthManagerConfig{}
- if err := mapstructure.Decode(m, c); err != nil {
- err = errors.Wrap(err, "error decoding conf")
- return nil, err
- }
- return c, nil
-}
-
-// New returns an auth manager implementation that verifies against a Nextcloud backend.
-func New(m map[string]interface{}) (auth.Manager, error) {
- c, err := parseConfig(m)
- if err != nil {
- return nil, err
- }
- c.init()
-
- return NewAuthManager(c)
-}
-
-// NewAuthManager returns a new Nextcloud-based AuthManager
-func NewAuthManager(c *AuthManagerConfig) (*Manager, error) {
- var client *http.Client
- if c.MockHTTP {
- // called := make([]string, 0)
- // nextcloudServerMock := GetNextcloudServerMock(&called)
- // client, _ = TestingHTTPClient(nextcloudServerMock)
-
- // Wait for SetHTTPClient to be called later
- client = nil
- } else {
- if len(c.EndPoint) == 0 {
- return nil, errors.New("Please specify 'endpoint' in '[grpc.services.authprovider.auth_managers.nextcloud]'")
- }
- client = &http.Client{}
- }
-
- return &Manager{
- endPoint: c.EndPoint, // e.g. "http://nc/apps/sciencemesh/"
- sharedSecret: c.SharedSecret,
- client: client,
- }, nil
-}
-
-// Configure method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/auth/auth.go#L32-L35
-func (am *Manager) Configure(ml map[string]interface{}) error {
- return nil
-}
-
-// SetHTTPClient sets the HTTP client
-func (am *Manager) SetHTTPClient(c *http.Client) {
- am.client = c
-}
-
-func (am *Manager) do(ctx context.Context, a Action) (int, []byte, error) {
- log := appctx.GetLogger(ctx)
- url := am.endPoint + "~" + a.username + "/api/auth/" + a.verb
- log.Info().Msgf("am.do %s %s %s", url, a.argS, am.sharedSecret)
- req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(a.argS))
- if err != nil {
- return 0, nil, err
- }
- req.Header.Set("X-Reva-Secret", am.sharedSecret)
-
- req.Header.Set("Content-Type", "application/json")
- resp, err := am.client.Do(req)
- if err != nil {
- return 0, nil, err
- }
-
- defer resp.Body.Close()
- body, err := io.ReadAll(resp.Body)
- if err != nil {
- return 0, nil, err
- }
-
- log.Info().Msgf("am.do response %d %s", resp.StatusCode, body)
- return resp.StatusCode, body, nil
-}
-
-// Authenticate method as defined in https://github.com/cs3org/reva/blob/28500a8/pkg/auth/auth.go#L31-L33
-func (am *Manager) Authenticate(ctx context.Context, clientID, clientSecret string) (*user.User, map[string]*authpb.Scope, error) {
- type paramsObj struct {
- ClientID string `json:"clientID"`
- ClientSecret string `json:"clientSecret"`
- // Scope authpb.Scope
- }
- bodyObj := ¶msObj{
- ClientID: clientID,
- ClientSecret: clientSecret,
- // Scope: authpb.Scope{
- // Resource: &types.OpaqueEntry{
- // Decoder: "json",
- // Value: []byte(`{"resource_id":{"storage_id":"storage-id","opaque_id":"opaque-id"},"path":"some/file/path.txt"}`),
- // },
- // Role: authpb.Role_ROLE_OWNER,
- // },
- }
- bodyStr, err := json.Marshal(bodyObj)
- if err != nil {
- return nil, nil, err
- }
- log := appctx.GetLogger(ctx)
- log.Info().Msgf("Authenticate %s %s", clientID, bodyStr)
-
- statusCode, body, err := am.do(ctx, Action{"Authenticate", clientID, string(bodyStr)})
-
- if err != nil {
- return nil, nil, err
- }
-
- if statusCode != 200 {
- return nil, nil, errors.New("Username/password not recognized by Nextcloud backend")
- }
-
- type resultsObj struct {
- User user.User `json:"user"`
- Scopes map[string]*authpb.Scope `json:"scopes"`
- }
- result := &resultsObj{}
- err = json.Unmarshal(body, &result)
- if err != nil {
- return nil, nil, err
- }
- var pointersMap = make(map[string]*authpb.Scope)
- for k := range result.Scopes {
- scope := result.Scopes[k]
- pointersMap[k] = scope
- }
- return &result.User, pointersMap, nil
-}
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/nextcloud/nextcloud_server_mock.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/nextcloud/nextcloud_server_mock.go
deleted file mode 100644
index f81e2fcec..000000000
--- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/nextcloud/nextcloud_server_mock.go
+++ /dev/null
@@ -1,99 +0,0 @@
-// Copyright 2018-2021 CERN
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// 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.
-//
-// In applying this license, CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-package nextcloud
-
-import (
- "context"
- "fmt"
- "io"
- "net"
- "net/http"
- "net/http/httptest"
- "strings"
-)
-
-// Response contains data for the Nextcloud mock server to respond
-// and to switch to a new server state
-type Response struct {
- code int
- body string
- newServerState string
-}
-
-const serverStateError = "ERROR"
-const serverStateEmpty = "EMPTY"
-const serverStateHome = "HOME"
-
-var serverState = serverStateEmpty
-
-var responses = map[string]Response{
- `POST /apps/sciencemesh/~einstein/api/auth/Authenticate {"clientID":"einstein","clientSecret":"relativity"}`: {200, `{"user":{"id":{"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}},"scopes":{"user":{"resource":{"decoder":"json","value":"eyJyZXNvdXJjZV9pZCI6eyJzdG9yYWdlX2lkIjoic3RvcmFnZS1pZCIsIm9wYXF1ZV9pZCI6Im9wYXF1ZS1pZCJ9LCJwYXRoIjoic29tZS9maWxlL3BhdGgudHh0In0="},"role":1}}}`, serverStateHome},
-}
-
-// GetNextcloudServerMock returns a handler that pretends to be a remote Nextcloud server
-func GetNextcloudServerMock(called *[]string) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- buf := new(strings.Builder)
- _, err := io.Copy(buf, r.Body)
- if err != nil {
- panic("Error reading response into buffer")
- }
- var key = fmt.Sprintf("%s %s %s", r.Method, r.URL, buf.String())
- *called = append(*called, key)
- response := responses[key]
- if (response == Response{}) {
- key = fmt.Sprintf("%s %s %s %s", r.Method, r.URL, buf.String(), serverState)
- response = responses[key]
- }
- if (response == Response{}) {
- fmt.Printf("%s %s %s %s", r.Method, r.URL, buf.String(), serverState)
- response = Response{500, fmt.Sprintf("response not defined! %s", key), serverStateEmpty}
- }
- serverState = responses[key].newServerState
- if serverState == `` {
- serverState = serverStateError
- }
- w.WriteHeader(response.code)
- // w.Header().Set("Etag", "mocker-etag")
- _, err = w.Write([]byte(responses[key].body))
- if err != nil {
- panic(err)
- }
- })
-}
-
-// TestingHTTPClient thanks to https://itnext.io/how-to-stub-requests-to-remote-hosts-with-go-6c2c1db32bf2
-// Ideally, this function would live in tests/helpers, but
-// if we put it there, it gets excluded by .dockerignore, and the
-// Docker build fails (see https://github.com/cs3org/reva/issues/1999)
-// So putting it here for now - open to suggestions if someone knows
-// a better way to inject this.
-func TestingHTTPClient(handler http.Handler) (*http.Client, func()) {
- s := httptest.NewServer(handler)
-
- cli := &http.Client{
- Transport: &http.Transport{
- DialContext: func(_ context.Context, network, _ string) (net.Conn, error) {
- return net.Dial(network, s.Listener.Addr().String())
- },
- },
- }
-
- return cli, s.Close
-}
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql/accounts/accounts.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql/accounts/accounts.go
deleted file mode 100644
index d3542cfc4..000000000
--- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql/accounts/accounts.go
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2018-2021 CERN
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// 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.
-//
-// In applying this license, CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-package accounts
-
-import (
- "context"
- "database/sql"
- "strings"
- "time"
-
- "github.com/opencloud-eu/reva/v2/pkg/appctx"
- "github.com/pkg/errors"
-)
-
-// Accounts represents oc10-style Accounts
-type Accounts struct {
- driver string
- db *sql.DB
- joinUsername, joinUUID, enableMedialSearch bool
- selectSQL string
-}
-
-// NewMysql returns a new accounts instance connecting to a MySQL database
-func NewMysql(dsn string, joinUsername, joinUUID, enableMedialSearch bool) (*Accounts, error) {
- sqldb, err := sql.Open("mysql", dsn)
- if err != nil {
- return nil, errors.Wrap(err, "error connecting to the database")
- }
-
- // FIXME make configurable
- sqldb.SetConnMaxLifetime(time.Minute * 3)
- sqldb.SetConnMaxIdleTime(time.Second * 30)
- sqldb.SetMaxOpenConns(100)
- sqldb.SetMaxIdleConns(10)
-
- err = sqldb.Ping()
- if err != nil {
- return nil, errors.Wrap(err, "error connecting to the database")
- }
-
- return New("mysql", sqldb, joinUsername, joinUUID, enableMedialSearch)
-}
-
-// New returns a new accounts instance connecting to the given sql.DB
-func New(driver string, sqldb *sql.DB, joinUsername, joinUUID, enableMedialSearch bool) (*Accounts, error) {
-
- sel := "SELECT id, email, user_id, display_name, quota, last_login, backend, home, state, password"
- from := `
- FROM oc_accounts a
- LEFT JOIN oc_users u
- ON a.user_id=u.uid
- `
- if joinUsername {
- sel += ", p.configvalue AS username"
- from += `LEFT JOIN oc_preferences p
- ON a.user_id=p.userid
- AND p.appid='core'
- AND p.configkey='username'`
- } else {
- // fallback to user_id as username
- sel += ", user_id AS username"
- }
- if joinUUID {
- sel += ", p2.configvalue AS ownclouduuid"
- from += `LEFT JOIN oc_preferences p2
- ON a.user_id=p2.userid
- AND p2.appid='core'
- AND p2.configkey='ownclouduuid'`
- } else {
- // fallback to user_id as ownclouduuid
- sel += ", user_id AS ownclouduuid"
- }
-
- return &Accounts{
- driver: driver,
- db: sqldb,
- joinUsername: joinUsername,
- joinUUID: joinUUID,
- enableMedialSearch: enableMedialSearch,
- selectSQL: sel + from,
- }, nil
-}
-
-// Account stores information about accounts.
-type Account struct {
- ID uint64
- Email sql.NullString
- UserID string
- DisplayName sql.NullString
- Quota sql.NullString
- LastLogin int
- Backend string
- Home string
- State int8
- PasswordHash string // from oc_users
- Username sql.NullString // optional comes from the oc_preferences
- OwnCloudUUID sql.NullString // optional comes from the oc_preferences
-}
-
-func (as *Accounts) rowToAccount(ctx context.Context, row Scannable) (*Account, error) {
- a := Account{}
- if err := row.Scan(&a.ID, &a.Email, &a.UserID, &a.DisplayName, &a.Quota, &a.LastLogin, &a.Backend, &a.Home, &a.State, &a.PasswordHash, &a.Username, &a.OwnCloudUUID); err != nil {
- appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
- return nil, err
- }
-
- return &a, nil
-}
-
-// Scannable describes the interface providing a Scan method
-type Scannable interface {
- Scan(...interface{}) error
-}
-
-// GetAccountByLogin fetches an account by mail or username
-func (as *Accounts) GetAccountByLogin(ctx context.Context, login string) (*Account, error) {
- var row *sql.Row
- username := strings.ToLower(login) // usernames are lowercased in owncloud classic
- if as.joinUsername {
- row = as.db.QueryRowContext(ctx, as.selectSQL+" WHERE a.email=? OR a.lower_user_id=? OR p.configvalue=?", login, username, login)
- } else {
- row = as.db.QueryRowContext(ctx, as.selectSQL+" WHERE a.email=? OR a.lower_user_id=?", login, username)
- }
-
- return as.rowToAccount(ctx, row)
-}
-
-// GetAccountGroups reads the groups for an account
-func (as *Accounts) GetAccountGroups(ctx context.Context, uid string) ([]string, error) {
- rows, err := as.db.QueryContext(ctx, "SELECT gid FROM oc_group_user WHERE uid=?", uid)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
-
- var group string
- groups := []string{}
- for rows.Next() {
- if err := rows.Scan(&group); err != nil {
- appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
- continue
- }
- groups = append(groups, group)
- }
- if err = rows.Err(); err != nil {
- return nil, err
- }
- return groups, nil
-}
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql/accounts/test.sqlite b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql/accounts/test.sqlite
deleted file mode 100644
index c68bb7537..000000000
Binary files a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql/accounts/test.sqlite and /dev/null differ
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql/owncloudsql.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql/owncloudsql.go
deleted file mode 100644
index 999aa6f43..000000000
--- a/vendor/github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql/owncloudsql.go
+++ /dev/null
@@ -1,192 +0,0 @@
-// Copyright 2018-2021 CERN
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// 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.
-//
-// In applying this license, CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-package owncloudsql
-
-import (
- "context"
- "crypto/hmac"
- "crypto/sha1"
- "encoding/hex"
- "fmt"
- "strings"
-
- "golang.org/x/crypto/bcrypt"
-
- authpb "github.com/cs3org/go-cs3apis/cs3/auth/provider/v1beta1"
- user "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
- "github.com/mitchellh/mapstructure"
- "github.com/opencloud-eu/reva/v2/pkg/appctx"
- "github.com/opencloud-eu/reva/v2/pkg/auth"
- "github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql/accounts"
- "github.com/opencloud-eu/reva/v2/pkg/auth/manager/registry"
- "github.com/opencloud-eu/reva/v2/pkg/auth/scope"
- "github.com/opencloud-eu/reva/v2/pkg/errtypes"
- "github.com/pkg/errors"
-
- // Provides mysql drivers
- _ "github.com/go-sql-driver/mysql"
-)
-
-func init() {
- registry.Register("owncloudsql", NewMysql)
-}
-
-type manager struct {
- c *config
- db *accounts.Accounts
-}
-
-type config struct {
- DbUsername string `mapstructure:"dbusername"`
- DbPassword string `mapstructure:"dbpassword"`
- DbHost string `mapstructure:"dbhost"`
- DbPort int `mapstructure:"dbport"`
- DbName string `mapstructure:"dbname"`
- Idp string `mapstructure:"idp"`
- Nobody int64 `mapstructure:"nobody"`
- LegacySalt string `mapstructure:"legacy_salt"`
- JoinUsername bool `mapstructure:"join_username"`
- JoinOwnCloudUUID bool `mapstructure:"join_ownclouduuid"`
-}
-
-// NewMysql returns a new auth manager connection to an owncloud mysql database
-func NewMysql(m map[string]interface{}) (auth.Manager, error) {
- mgr := &manager{}
- err := mgr.Configure(m)
- if err != nil {
- err = errors.Wrap(err, "error creating a new auth manager")
- return nil, err
- }
-
- mgr.db, err = accounts.NewMysql(
- fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", mgr.c.DbUsername, mgr.c.DbPassword, mgr.c.DbHost, mgr.c.DbPort, mgr.c.DbName),
- mgr.c.JoinUsername,
- mgr.c.JoinOwnCloudUUID,
- false,
- )
- if err != nil {
- return nil, err
- }
-
- return mgr, nil
-}
-
-func (m *manager) Configure(ml map[string]interface{}) error {
- c, err := parseConfig(ml)
- if err != nil {
- return err
- }
-
- if c.Nobody == 0 {
- c.Nobody = 99
- }
-
- m.c = c
- return nil
-}
-
-func parseConfig(m map[string]interface{}) (*config, error) {
- c := &config{}
- if err := mapstructure.Decode(m, &c); err != nil {
- return nil, err
- }
- return c, nil
-}
-
-func (m *manager) Authenticate(ctx context.Context, login, clientSecret string) (*user.User, map[string]*authpb.Scope, error) {
- log := appctx.GetLogger(ctx)
-
- // 1. find user by login
-
- account, err := m.db.GetAccountByLogin(ctx, login)
- if err != nil {
- return nil, nil, errtypes.NotFound(login)
- }
- // 2. verify the user password
- if !m.verify(clientSecret, account.PasswordHash) {
- return nil, nil, errtypes.InvalidCredentials(login)
- }
-
- userID := &user.UserId{
- Idp: m.c.Idp,
- OpaqueId: account.OwnCloudUUID.String,
- Type: user.UserType_USER_TYPE_PRIMARY, // TODO: assign the appropriate user type for guest accounts
- }
-
- u := &user.User{
- Id: userID,
- // TODO add more claims from the StandardClaims, eg EmailVerified and lastlogin
- Username: account.Username.String,
- Mail: account.Email.String,
- DisplayName: account.DisplayName.String,
- //UidNumber: uidNumber,
- //GidNumber: gidNumber,
- }
-
- if u.Groups, err = m.db.GetAccountGroups(ctx, account.UserID); err != nil {
- return nil, nil, err
- }
-
- var scopes map[string]*authpb.Scope
- if userID != nil && (userID.Type == user.UserType_USER_TYPE_LIGHTWEIGHT || userID.Type == user.UserType_USER_TYPE_FEDERATED) {
- scopes, err = scope.AddLightweightAccountScope(authpb.Role_ROLE_OWNER, nil)
- if err != nil {
- return nil, nil, err
- }
- } else {
- scopes, err = scope.AddOwnerScope(nil)
- if err != nil {
- return nil, nil, err
- }
- }
- // do not log password hash
- account.PasswordHash = "***redacted***"
- log.Debug().Interface("account", account).Interface("user", u).Msg("authenticated user")
-
- return u, scopes, nil
-}
-
-func (m *manager) verify(password, hash string) bool {
- splitHash := strings.SplitN(hash, "|", 2)
- switch len(splitHash) {
- case 2:
- if splitHash[0] == "1" {
- return m.verifyHashV1(password, splitHash[1])
- }
- case 1:
- return m.legacyHashVerify(password, hash)
- }
- return false
-}
-
-func (m *manager) legacyHashVerify(password, hash string) bool {
- // TODO rehash $newHash = $this->hash($message);
- switch len(hash) {
- case 60: // legacy PHPass hash
- return nil == bcrypt.CompareHashAndPassword([]byte(hash), []byte(password+m.c.LegacySalt))
- case 40: // legacy sha1 hash
- h := sha1.Sum([]byte(password))
- return hmac.Equal([]byte(hash), []byte(hex.EncodeToString(h[:])))
- }
- return false
-}
-func (m *manager) verifyHashV1(password, hash string) bool {
- // TODO implement password_needs_rehash
- return nil == bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
-}
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/upload/upload.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/upload/upload.go
index 699e34320..db5065b77 100644
--- a/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/upload/upload.go
+++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/upload/upload.go
@@ -435,6 +435,7 @@ func (session *DecomposedFsSession) Cleanup(revertNodeMetadata, cleanBin, cleanI
revisionNode, err := node.ReadNode(ctx, session.store.lu, session.SpaceID(), versionID, "", false, n.SpaceRoot, false)
if err != nil {
sublog.Error().Err(err).Str("versionID", versionID).Msg("reading revision node failed")
+ return
}
if !revisionNode.Exists {
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/loader/loader.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/loader/loader.go
index c1e016eba..bc176e863 100644
--- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/loader/loader.go
+++ b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/loader/loader.go
@@ -24,7 +24,5 @@ import (
_ "github.com/opencloud-eu/reva/v2/pkg/user/manager/json"
_ "github.com/opencloud-eu/reva/v2/pkg/user/manager/ldap"
_ "github.com/opencloud-eu/reva/v2/pkg/user/manager/memory"
- _ "github.com/opencloud-eu/reva/v2/pkg/user/manager/nextcloud"
- _ "github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql"
// Add your own here
)
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/nextcloud/nextcloud.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/nextcloud/nextcloud.go
deleted file mode 100644
index 051b3de2e..000000000
--- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/nextcloud/nextcloud.go
+++ /dev/null
@@ -1,249 +0,0 @@
-// Copyright 2018-2021 CERN
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// 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.
-//
-// In applying this license, CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-package nextcloud
-
-import (
- "context"
- "encoding/json"
- "fmt"
- "io"
- "net/http"
- "strings"
-
- ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
-
- "github.com/mitchellh/mapstructure"
- "github.com/opencloud-eu/reva/v2/pkg/errtypes"
- "github.com/opencloud-eu/reva/v2/pkg/user"
- "github.com/opencloud-eu/reva/v2/pkg/user/manager/registry"
- "github.com/pkg/errors"
-
- userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
- // "github.com/opencloud-eu/reva/v2/pkg/errtypes"
-)
-
-func init() {
- registry.Register("nextcloud", New)
-}
-
-// Manager is the Nextcloud-based implementation of the share.Manager interface
-// see https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
-type Manager struct {
- client *http.Client
- sharedSecret string
- endPoint string
-}
-
-// UserManagerConfig contains config for a Nextcloud-based UserManager
-type UserManagerConfig struct {
- EndPoint string `mapstructure:"endpoint" docs:";The Nextcloud backend endpoint for user management"`
- SharedSecret string `mapstructure:"shared_secret"`
- MockHTTP bool `mapstructure:"mock_http"`
-}
-
-func (c *UserManagerConfig) init() {
- if c.EndPoint == "" {
- c.EndPoint = "http://localhost/end/point?"
- }
-}
-
-func parseConfig(m map[string]interface{}) (*UserManagerConfig, error) {
- c := &UserManagerConfig{}
- if err := mapstructure.Decode(m, c); err != nil {
- err = errors.Wrap(err, "error decoding conf")
- return nil, err
- }
- c.init()
- return c, nil
-}
-
-// Action describes a REST request to forward to the Nextcloud backend
-type Action struct {
- verb string
- argS string
-}
-
-// New returns a user manager implementation that reads a json file to provide user metadata.
-func New(m map[string]interface{}) (user.Manager, error) {
- c, err := parseConfig(m)
- if err != nil {
- return nil, err
- }
- c.init()
-
- return NewUserManager(c)
-}
-
-// NewUserManager returns a new Nextcloud-based UserManager
-func NewUserManager(c *UserManagerConfig) (*Manager, error) {
- var client *http.Client
- if c.MockHTTP {
- // Wait for SetHTTPClient to be called later
- client = nil
- } else {
- if len(c.EndPoint) == 0 {
- return nil, errors.New("Please specify 'endpoint' in '[grpc.services.userprovider.drivers.nextcloud]'")
- }
- client = &http.Client{}
- }
-
- return &Manager{
- endPoint: c.EndPoint, // e.g. "http://nc/apps/sciencemesh/"
- sharedSecret: c.SharedSecret,
- client: client,
- }, nil
-}
-
-// SetHTTPClient sets the HTTP client
-func (um *Manager) SetHTTPClient(c *http.Client) {
- um.client = c
-}
-
-func getUser(ctx context.Context) (*userpb.User, error) {
- u, ok := ctxpkg.ContextGetUser(ctx)
- if !ok {
- err := errors.Wrap(errtypes.UserRequired(""), "nextcloud storage driver: error getting user from ctx")
- return nil, err
- }
- return u, nil
-}
-
-func (um *Manager) do(ctx context.Context, a Action, username string) (int, []byte, error) {
- url := um.endPoint + "~" + username + "/api/user/" + a.verb
- req, err := http.NewRequest(http.MethodPost, url, strings.NewReader(a.argS))
- if err != nil {
- panic(err)
- }
- req.Header.Set("X-Reva-Secret", um.sharedSecret)
-
- req.Header.Set("Content-Type", "application/json")
- fmt.Println(url)
- resp, err := um.client.Do(req)
- if err != nil {
- panic(err)
- }
-
- defer resp.Body.Close()
- body, err := io.ReadAll(resp.Body)
- return resp.StatusCode, body, err
-}
-
-// Configure method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
-func (um *Manager) Configure(ml map[string]interface{}) error {
- return nil
-}
-
-// GetUser method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
-func (um *Manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
- if uid.GetTenantId() != "" {
- return nil, errtypes.NotSupported("tenant filter not supported in nextcloud user manager")
- }
- bodyStr, _ := json.Marshal(uid)
- _, respBody, err := um.do(ctx, Action{"GetUser", string(bodyStr)}, "unauthenticated")
- if err != nil {
- return nil, err
- }
- result := &userpb.User{}
- err = json.Unmarshal(respBody, &result)
- if err != nil {
- return nil, err
- }
- return result, err
-}
-
-// GetUserByClaim method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
-func (um *Manager) GetUserByClaim(ctx context.Context, claim, value, tenantID string, skipFetchingGroups bool) (*userpb.User, error) {
- if tenantID != "" {
- return nil, errtypes.NotSupported("tenant filter not supported in nextcloud user manager")
- }
- type paramsObj struct {
- Claim string `json:"claim"`
- Value string `json:"value"`
- }
- bodyObj := ¶msObj{
- Claim: claim,
- Value: value,
- }
- user, err := getUser(ctx)
- if err != nil {
- return nil, err
- }
-
- bodyStr, _ := json.Marshal(bodyObj)
- _, respBody, err := um.do(ctx, Action{"GetUserByClaim", string(bodyStr)}, user.Username)
- if err != nil {
- return nil, err
- }
- result := &userpb.User{}
- err = json.Unmarshal(respBody, &result)
- if err != nil {
- return nil, err
- }
- return result, err
-}
-
-// GetUserGroups method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
-func (um *Manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) {
- bodyStr, err := json.Marshal(uid)
- if err != nil {
- return nil, err
- }
- user, err := getUser(ctx)
- if err != nil {
- return nil, err
- }
-
- _, respBody, err := um.do(ctx, Action{"GetUserGroups", string(bodyStr)}, user.Username)
- if err != nil {
- return nil, err
- }
- var gs []string
- err = json.Unmarshal(respBody, &gs)
- if err != nil {
- return nil, err
- }
- return gs, err
-}
-
-// FindUsers method as defined in https://github.com/cs3org/reva/blob/v1.13.0/pkg/user/user.go#L29-L35
-func (um *Manager) FindUsers(ctx context.Context, query, tenantID string, skipFetchingGroups bool) ([]*userpb.User, error) {
- if tenantID != "" {
- return nil, errtypes.NotSupported("tenant filter not supported in nextcloud user manager")
- }
-
- user, err := getUser(ctx)
- if err != nil {
- return nil, err
- }
-
- _, respBody, err := um.do(ctx, Action{"FindUsers", query}, user.Username)
- if err != nil {
- return nil, err
- }
- var respArr []userpb.User
- err = json.Unmarshal(respBody, &respArr)
- if err != nil {
- return nil, err
- }
- var pointers = make([]*userpb.User, len(respArr))
- for i := 0; i < len(respArr); i++ {
- pointers[i] = &respArr[i]
- }
- return pointers, err
-}
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/nextcloud/nextcloud_server_mock.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/nextcloud/nextcloud_server_mock.go
deleted file mode 100644
index 2d6ebc9a9..000000000
--- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/nextcloud/nextcloud_server_mock.go
+++ /dev/null
@@ -1,103 +0,0 @@
-// Copyright 2018-2021 CERN
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// 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.
-//
-// In applying this license, CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-package nextcloud
-
-import (
- "context"
- "fmt"
- "io"
- "net"
- "net/http"
- "net/http/httptest"
- "strings"
-)
-
-// Response contains data for the Nextcloud mock server to respond
-// and to switch to a new server state
-type Response struct {
- code int
- body string
- newServerState string
-}
-
-const serverStateError = "ERROR"
-const serverStateEmpty = "EMPTY"
-const serverStateHome = "HOME"
-
-var serverState = serverStateEmpty
-
-var responses = map[string]Response{
- `POST /apps/sciencemesh/~unauthenticated/api/user/GetUser {"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}`: {200, `{"id":{"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}}`, serverStateHome},
- `POST /apps/sciencemesh/~tester/api/user/GetUserByClaim {"claim":"claim-string","value":"value-string"}`: {200, `{"id":{"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}}`, serverStateHome},
- `POST /apps/sciencemesh/~tester/api/user/GetUserGroups {"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}`: {200, `["wine-lovers"]`, serverStateHome},
- `POST /apps/sciencemesh/~tester/api/user/FindUsers some-query`: {200, `[{"id":{"idp":"some-idp","opaque_id":"some-opaque-user-id","type":1}}]`, serverStateHome},
-}
-
-// GetNextcloudServerMock returns a handler that pretends to be a remote Nextcloud server
-func GetNextcloudServerMock(called *[]string) http.Handler {
- return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- buf := new(strings.Builder)
- _, err := io.Copy(buf, r.Body)
- if err != nil {
- panic("Error reading response into buffer")
- }
- var key = fmt.Sprintf("%s %s %s", r.Method, r.URL, buf.String())
- *called = append(*called, key)
- response := responses[key]
- if (response == Response{}) {
- key = fmt.Sprintf("%s %s %s %s", r.Method, r.URL, buf.String(), serverState)
- // *called = append(*called, key)
- response = responses[key]
- }
- if (response == Response{}) {
- fmt.Printf("%s %s %s %s", r.Method, r.URL, buf.String(), serverState)
- response = Response{500, fmt.Sprintf("response not defined! %s", key), serverStateEmpty}
- }
- serverState = responses[key].newServerState
- if serverState == `` {
- serverState = serverStateError
- }
- w.WriteHeader(response.code)
- // w.Header().Set("Etag", "mocker-etag")
- _, err = w.Write([]byte(responses[key].body))
- if err != nil {
- panic(err)
- }
- })
-}
-
-// TestingHTTPClient thanks to https://itnext.io/how-to-stub-requests-to-remote-hosts-with-go-6c2c1db32bf2
-// Ideally, this function would live in tests/helpers, but
-// if we put it there, it gets excluded by .dockerignore, and the
-// Docker build fails (see https://github.com/cs3org/reva/issues/1999)
-// So putting it here for now - open to suggestions if someone knows
-// a better way to inject this.
-func TestingHTTPClient(handler http.Handler) (*http.Client, func()) {
- s := httptest.NewServer(handler)
-
- cli := &http.Client{
- Transport: &http.Transport{
- DialContext: func(_ context.Context, network, _ string) (net.Conn, error) {
- return net.Dial(network, s.Listener.Addr().String())
- },
- },
- }
-
- return cli, s.Close
-}
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/accounts/accounts.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/accounts/accounts.go
deleted file mode 100644
index 41f7d6782..000000000
--- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/accounts/accounts.go
+++ /dev/null
@@ -1,228 +0,0 @@
-// Copyright 2018-2021 CERN
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// 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.
-//
-// In applying this license, CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-package accounts
-
-import (
- "context"
- "database/sql"
- "strings"
- "time"
-
- "github.com/opencloud-eu/reva/v2/pkg/appctx"
- "github.com/pkg/errors"
-)
-
-// Accounts represents oc10-style Accounts
-type Accounts struct {
- driver string
- db *sql.DB
- joinUsername, joinUUID, enableMedialSearch bool
- selectSQL string
-}
-
-// NewMysql returns a new Cache instance connecting to a MySQL database
-func NewMysql(dsn string, joinUsername, joinUUID, enableMedialSearch bool) (*Accounts, error) {
- sqldb, err := sql.Open("mysql", dsn)
- if err != nil {
- return nil, errors.Wrap(err, "error connecting to the database")
- }
-
- // FIXME make configurable
- sqldb.SetConnMaxLifetime(time.Minute * 3)
- sqldb.SetConnMaxIdleTime(time.Second * 30)
- sqldb.SetMaxOpenConns(100)
- sqldb.SetMaxIdleConns(10)
-
- err = sqldb.Ping()
- if err != nil {
- return nil, errors.Wrap(err, "error connecting to the database")
- }
-
- return New("mysql", sqldb, joinUsername, joinUUID, enableMedialSearch)
-}
-
-// New returns a new Cache instance connecting to the given sql.DB
-func New(driver string, sqldb *sql.DB, joinUsername, joinUUID, enableMedialSearch bool) (*Accounts, error) {
-
- sel := "SELECT id, email, user_id, display_name, quota, last_login, backend, home, state"
- from := `
- FROM oc_accounts a
- `
- if joinUsername {
- sel += ", p.configvalue AS username"
- from += `LEFT JOIN oc_preferences p
- ON a.user_id=p.userid
- AND p.appid='core'
- AND p.configkey='username'`
- } else {
- // fallback to user_id as username
- sel += ", user_id AS username"
- }
- if joinUUID {
- sel += ", p2.configvalue AS ownclouduuid"
- from += `LEFT JOIN oc_preferences p2
- ON a.user_id=p2.userid
- AND p2.appid='core'
- AND p2.configkey='ownclouduuid'`
- } else {
- // fallback to user_id as ownclouduuid
- sel += ", user_id AS ownclouduuid"
- }
-
- return &Accounts{
- driver: driver,
- db: sqldb,
- joinUsername: joinUsername,
- joinUUID: joinUUID,
- enableMedialSearch: enableMedialSearch,
- selectSQL: sel + from,
- }, nil
-}
-
-// Account stores information about accounts.
-type Account struct {
- ID uint64
- Email sql.NullString
- UserID string
- DisplayName sql.NullString
- Quota sql.NullString
- LastLogin int
- Backend string
- Home string
- State int8
- Username sql.NullString // optional comes from the oc_preferences
- OwnCloudUUID sql.NullString // optional comes from the oc_preferences
-}
-
-func (as *Accounts) rowToAccount(ctx context.Context, row Scannable) (*Account, error) {
- a := Account{}
- if err := row.Scan(&a.ID, &a.Email, &a.UserID, &a.DisplayName, &a.Quota, &a.LastLogin, &a.Backend, &a.Home, &a.State, &a.Username, &a.OwnCloudUUID); err != nil {
- appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
- return nil, err
- }
-
- return &a, nil
-}
-
-// Scannable describes the interface providing a Scan method
-type Scannable interface {
- Scan(...interface{}) error
-}
-
-// GetAccountByClaim fetches an account by mail, username or userid
-func (as *Accounts) GetAccountByClaim(ctx context.Context, claim, value string) (*Account, error) {
- // TODO align supported claims with rest driver and the others, maybe refactor into common mapping
- var row *sql.Row
- var where string
- switch claim {
- case "mail":
- where = "WHERE a.email=?"
- // case "uid":
- // claim = m.c.Schema.UIDNumber
- // case "gid":
- // claim = m.c.Schema.GIDNumber
- case "username":
- if as.joinUsername {
- where = "WHERE p.configvalue=?"
- } else {
- // use user_id as username
- where = "WHERE a.user_id=?"
- }
- case "userid":
- if as.joinUUID {
- where = "WHERE p2.configvalue=?"
- } else {
- // use user_id as uuid
- where = "WHERE a.user_id=?"
- }
- default:
- return nil, errors.New("owncloudsql: invalid field " + claim)
- }
-
- row = as.db.QueryRowContext(ctx, as.selectSQL+where, value)
-
- return as.rowToAccount(ctx, row)
-}
-
-func sanitizeWildcards(q string) string {
- return strings.ReplaceAll(strings.ReplaceAll(q, "%", `\%`), "_", `\_`)
-}
-
-// FindAccounts searches userid, displayname and email using the given query. The Wildcard caracters % and _ are escaped.
-func (as *Accounts) FindAccounts(ctx context.Context, query string) ([]Account, error) {
- if as.enableMedialSearch {
- query = "%" + sanitizeWildcards(query) + "%"
- }
- // TODO join oc_account_terms
- where := "WHERE a.user_id LIKE ? OR a.display_name LIKE ? OR a.email LIKE ?"
- args := []interface{}{query, query, query}
-
- if as.joinUsername {
- where += " OR p.configvalue LIKE ?"
- args = append(args, query)
- }
- if as.joinUUID {
- where += " OR p2.configvalue LIKE ?"
- args = append(args, query)
- }
-
- rows, err := as.db.QueryContext(ctx, as.selectSQL+where, args...)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
-
- accounts := []Account{}
- for rows.Next() {
- a := Account{}
- if err := rows.Scan(&a.ID, &a.Email, &a.UserID, &a.DisplayName, &a.Quota, &a.LastLogin, &a.Backend, &a.Home, &a.State, &a.Username, &a.OwnCloudUUID); err != nil {
- appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
- continue
- }
- accounts = append(accounts, a)
- }
- if err = rows.Err(); err != nil {
- return nil, err
- }
-
- return accounts, nil
-}
-
-// GetAccountGroups lasts the groups for an account
-func (as *Accounts) GetAccountGroups(ctx context.Context, uid string) ([]string, error) {
- rows, err := as.db.QueryContext(ctx, "SELECT gid FROM oc_group_user WHERE uid=?", uid)
- if err != nil {
- return nil, err
- }
- defer rows.Close()
-
- groups := []string{}
- for rows.Next() {
- var group string
- if err := rows.Scan(&group); err != nil {
- appctx.GetLogger(ctx).Error().Err(err).Msg("could not scan row, skipping")
- continue
- }
- groups = append(groups, group)
- }
- if err = rows.Err(); err != nil {
- return nil, err
- }
- return groups, nil
-}
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/accounts/test.sqlite b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/accounts/test.sqlite
deleted file mode 100644
index c68bb7537..000000000
Binary files a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/accounts/test.sqlite and /dev/null differ
diff --git a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/owncloudsql.go b/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/owncloudsql.go
deleted file mode 100644
index c5735b545..000000000
--- a/vendor/github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/owncloudsql.go
+++ /dev/null
@@ -1,199 +0,0 @@
-// Copyright 2018-2021 CERN
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// 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.
-//
-// In applying this license, CERN does not waive the privileges and immunities
-// granted to it by virtue of its status as an Intergovernmental Organization
-// or submit itself to any jurisdiction.
-
-package owncloudsql
-
-import (
- "context"
- "database/sql"
- "fmt"
-
- userpb "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1"
- "github.com/mitchellh/mapstructure"
- "github.com/opencloud-eu/reva/v2/pkg/appctx"
- "github.com/opencloud-eu/reva/v2/pkg/errtypes"
- "github.com/opencloud-eu/reva/v2/pkg/user"
- "github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/accounts"
- "github.com/opencloud-eu/reva/v2/pkg/user/manager/registry"
- "github.com/pkg/errors"
-
- // Provides mysql drivers
- _ "github.com/go-sql-driver/mysql"
-)
-
-func init() {
- registry.Register("owncloudsql", NewMysql)
-}
-
-type manager struct {
- c *config
- db *accounts.Accounts
-}
-
-type config struct {
- DbUsername string `mapstructure:"dbusername"`
- DbPassword string `mapstructure:"dbpassword"`
- DbHost string `mapstructure:"dbhost"`
- DbPort int `mapstructure:"dbport"`
- DbName string `mapstructure:"dbname"`
- Idp string `mapstructure:"idp"`
- Nobody int64 `mapstructure:"nobody"`
- JoinUsername bool `mapstructure:"join_username"`
- JoinOwnCloudUUID bool `mapstructure:"join_ownclouduuid"`
- EnableMedialSearch bool `mapstructure:"enable_medial_search"`
-}
-
-// NewMysql returns a new user manager connection to an owncloud mysql database
-func NewMysql(m map[string]interface{}) (user.Manager, error) {
- mgr := &manager{}
- err := mgr.Configure(m)
- if err != nil {
- err = errors.Wrap(err, "error creating a new manager")
- return nil, err
- }
-
- mgr.db, err = accounts.NewMysql(
- fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", mgr.c.DbUsername, mgr.c.DbPassword, mgr.c.DbHost, mgr.c.DbPort, mgr.c.DbName),
- mgr.c.JoinUsername,
- mgr.c.JoinOwnCloudUUID,
- mgr.c.EnableMedialSearch,
- )
- if err != nil {
- return nil, err
- }
-
- return mgr, nil
-}
-
-func (m *manager) Configure(ml map[string]interface{}) error {
- c, err := parseConfig(ml)
- if err != nil {
- return err
- }
-
- if c.Nobody == 0 {
- c.Nobody = 99
- }
-
- m.c = c
- return nil
-}
-
-func parseConfig(m map[string]interface{}) (*config, error) {
- c := &config{}
- if err := mapstructure.Decode(m, &c); err != nil {
- return nil, err
- }
- return c, nil
-}
-
-func (m *manager) GetUser(ctx context.Context, uid *userpb.UserId, skipFetchingGroups bool) (*userpb.User, error) {
- if uid.GetTenantId() != "" {
- return nil, errtypes.NotSupported("tenant filter not supported in opencloudsql user manager")
- }
- // search via the user_id
- a, err := m.db.GetAccountByClaim(ctx, "userid", uid.OpaqueId)
- if err == sql.ErrNoRows {
- return nil, errtypes.NotFound(uid.OpaqueId)
- }
- return m.convertToCS3User(ctx, a, skipFetchingGroups)
-}
-
-func (m *manager) GetUserByClaim(ctx context.Context, claim, value, tenantID string, skipFetchingGroups bool) (*userpb.User, error) {
- if tenantID != "" {
- return nil, errtypes.NotSupported("tenant filter not supported in opencloudsql user manager")
- }
-
- a, err := m.db.GetAccountByClaim(ctx, claim, value)
- if err == sql.ErrNoRows {
- return nil, errtypes.NotFound(claim + "=" + value)
- } else if err != nil {
- return nil, err
- }
- return m.convertToCS3User(ctx, a, skipFetchingGroups)
-}
-
-func (m *manager) FindUsers(ctx context.Context, query, tenantID string, skipFetchingGroups bool) ([]*userpb.User, error) {
- if tenantID != "" {
- return nil, errtypes.NotSupported("tenant filter not supported in opencloudsql user manager")
- }
-
- accounts, err := m.db.FindAccounts(ctx, query)
- if err == sql.ErrNoRows {
- return nil, errtypes.NotFound("no users found for " + query)
- } else if err != nil {
- return nil, err
- }
-
- users := make([]*userpb.User, 0, len(accounts))
- for i := range accounts {
- u, err := m.convertToCS3User(ctx, &accounts[i], skipFetchingGroups)
- if err != nil {
- appctx.GetLogger(ctx).Error().Err(err).Interface("account", accounts[i]).Msg("could not convert account, skipping")
- continue
- }
- users = append(users, u)
- }
-
- return users, nil
-}
-
-func (m *manager) GetUserGroups(ctx context.Context, uid *userpb.UserId) ([]string, error) {
- groups, err := m.db.GetAccountGroups(ctx, uid.OpaqueId)
- if err == sql.ErrNoRows {
- return nil, errtypes.NotFound("no groups found for uid " + uid.OpaqueId)
- } else if err != nil {
- return nil, err
- }
- return groups, nil
-}
-
-func (m *manager) convertToCS3User(ctx context.Context, a *accounts.Account, skipFetchingGroups bool) (*userpb.User, error) {
- u := &userpb.User{
- Id: &userpb.UserId{
- Idp: m.c.Idp,
- OpaqueId: a.OwnCloudUUID.String,
- Type: userpb.UserType_USER_TYPE_PRIMARY,
- },
- Username: a.Username.String,
- Mail: a.Email.String,
- DisplayName: a.DisplayName.String,
- //Groups: groups,
- GidNumber: m.c.Nobody,
- UidNumber: m.c.Nobody,
- }
- // https://github.com/cs3org/reva/pull/4135
- // fall back to userid
- if u.Id.OpaqueId == "" {
- u.Id.OpaqueId = a.UserID
- }
- if u.Username == "" {
- u.Username = u.Id.OpaqueId
- }
- if u.DisplayName == "" {
- u.DisplayName = u.Id.OpaqueId
- }
-
- if !skipFetchingGroups {
- var err error
- if u.Groups, err = m.GetUserGroups(ctx, u.Id); err != nil {
- return nil, err
- }
- }
- return u, nil
-}
diff --git a/vendor/github.com/rs/zerolog/.gitignore b/vendor/github.com/rs/zerolog/.gitignore
index 8ebe58b15..1a2fc2146 100644
--- a/vendor/github.com/rs/zerolog/.gitignore
+++ b/vendor/github.com/rs/zerolog/.gitignore
@@ -23,3 +23,5 @@ _testmain.go
*.exe
*.test
*.prof
+
+coverage.out
diff --git a/vendor/github.com/rs/zerolog/README.md b/vendor/github.com/rs/zerolog/README.md
index 9d4e8e8d2..0989c8022 100644
--- a/vendor/github.com/rs/zerolog/README.md
+++ b/vendor/github.com/rs/zerolog/README.md
@@ -18,17 +18,18 @@ Find out [who uses zerolog](https://github.com/rs/zerolog/wiki/Who-uses-zerolog)
## Features
-* [Blazing fast](#benchmarks)
-* [Low to zero allocation](#benchmarks)
-* [Leveled logging](#leveled-logging)
-* [Sampling](#log-sampling)
-* [Hooks](#hooks)
-* [Contextual fields](#contextual-logging)
-* [`context.Context` integration](#contextcontext-integration)
-* [Integration with `net/http`](#integration-with-nethttp)
-* [JSON and CBOR encoding formats](#binary-encoding)
-* [Pretty logging for development](#pretty-logging)
-* [Error Logging (with optional Stacktrace)](#error-logging)
+- [Blazing fast](#benchmarks)
+- [Low to zero allocation](#benchmarks)
+- [Leveled logging](#leveled-logging)
+- [Sampling](#log-sampling)
+- [Hooks](#hooks)
+- [Contextual fields](#contextual-logging)
+- [`context.Context` integration](#contextcontext-integration)
+- [Integration with `net/http`](#integration-with-nethttp)
+- [JSON and CBOR encoding formats](#binary-encoding)
+- [Pretty logging for development](#pretty-logging)
+- [Error Logging (with optional Stacktrace)](#error-logging)
+- [`log/slog` integration](#integration-with-logslog)
## Installation
@@ -59,8 +60,9 @@ func main() {
// Output: {"time":1516134303,"level":"debug","message":"hello world"}
```
+
> Note: By default log writes to `os.Stderr`
-> Note: The default log level for `log.Print` is *trace*
+> Note: The default log level for `log.Print` is _trace_
### Contextual Logging
@@ -81,7 +83,7 @@ func main() {
Str("Scale", "833 cents").
Float64("Interval", 833.09).
Msg("Fibonacci is everywhere")
-
+
log.Debug().
Str("Name", "Tom").
Send()
@@ -118,15 +120,15 @@ func main() {
**zerolog** allows for logging at the following levels (from highest to lowest):
-* panic (`zerolog.PanicLevel`, 5)
-* fatal (`zerolog.FatalLevel`, 4)
-* error (`zerolog.ErrorLevel`, 3)
-* warn (`zerolog.WarnLevel`, 2)
-* info (`zerolog.InfoLevel`, 1)
-* debug (`zerolog.DebugLevel`, 0)
-* trace (`zerolog.TraceLevel`, -1)
+- panic (`zerolog.PanicLevel`, 5)
+- fatal (`zerolog.FatalLevel`, 4)
+- error (`zerolog.ErrorLevel`, 3)
+- warn (`zerolog.WarnLevel`, 2)
+- info (`zerolog.InfoLevel`, 1)
+- debug (`zerolog.DebugLevel`, 0)
+- trace (`zerolog.TraceLevel`, -1)
-You can set the Global logging level to any of these options using the `SetGlobalLevel` function in the zerolog package, passing in one of the given constants above, e.g. `zerolog.InfoLevel` would be the "info" level. Whichever level is chosen, all logs with a level greater than or equal to that level will be written. To turn off logging entirely, pass the `zerolog.Disabled` constant.
+You can set the Global logging level to any of these options using the `SetGlobalLevel` function in the zerolog package, passing in one of the given constants above, e.g. `zerolog.InfoLevel` would be the "info" level. Whichever level is chosen, all logs with a level greater than or equal to that level will be written. To turn off logging entirely, pass the `zerolog.Disabled` constant.
#### Setting Global Log Level
@@ -212,17 +214,17 @@ You can log errors using the `Err` method
package main
import (
- "errors"
+ "errors"
- "github.com/rs/zerolog"
- "github.com/rs/zerolog/log"
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
)
func main() {
- zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
+ zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
- err := errors.New("seems we have an error here")
- log.Error().Err(err).Msg("")
+ err := errors.New("seems we have an error here")
+ log.Error().Err(err).Msg("")
}
// Output: {"level":"error","error":"seems we have an error here","time":1609085256}
@@ -232,45 +234,45 @@ func main() {
#### Error Logging with Stacktrace
-Using `github.com/pkg/errors`, you can add a formatted stacktrace to your errors.
+Using `github.com/pkg/errors`, you can add a formatted stacktrace to your errors.
```go
package main
import (
- "github.com/pkg/errors"
- "github.com/rs/zerolog/pkgerrors"
+ "github.com/pkg/errors"
+ "github.com/rs/zerolog/pkgerrors"
- "github.com/rs/zerolog"
- "github.com/rs/zerolog/log"
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
)
func main() {
- zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
- zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
+ zerolog.TimeFieldFormat = zerolog.TimeFormatUnix
+ zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack
- err := outer()
- log.Error().Stack().Err(err).Msg("")
+ err := outer()
+ log.Error().Stack().Err(err).Msg("")
}
func inner() error {
- return errors.New("seems we have an error here")
+ return errors.New("seems we have an error here")
}
func middle() error {
- err := inner()
- if err != nil {
- return err
- }
- return nil
+ err := inner()
+ if err != nil {
+ return err
+ }
+ return nil
}
func outer() error {
- err := middle()
- if err != nil {
- return err
- }
- return nil
+ err := middle()
+ if err != nil {
+ return err
+ }
+ return nil
}
// Output: {"level":"error","stack":[{"func":"inner","line":"20","source":"errors.go"},{"func":"middle","line":"24","source":"errors.go"},{"func":"outer","line":"32","source":"errors.go"},{"func":"main","line":"15","source":"errors.go"},{"func":"main","line":"204","source":"proc.go"},{"func":"goexit","line":"1374","source":"asm_amd64.s"}],"error":"seems we have an error here","time":1609086683}
@@ -308,7 +310,6 @@ func main() {
> NOTE: Using `Msgf` generates one allocation even when the logger is disabled.
-
### Create logger instance to manage different outputs
```go
@@ -393,7 +394,7 @@ log.Info().Str("foo", "bar").
Str("one", "test_one").
Str("three", "test_three").
Msg("Hello World")
-
+
// Output: INFO TEST_ONE test_two (test_three) Hello World foo:bar
```
@@ -457,8 +458,8 @@ If your writer might be slow or not thread-safe and you need your log producers
```go
wr := diode.NewWriter(os.Stdout, 1000, 10*time.Millisecond, func(missed int) {
- fmt.Printf("Logger Dropped %d messages", missed)
- })
+ fmt.Printf("Logger Dropped %d messages", missed)
+ })
log := zerolog.New(wr)
log.Print("test")
```
@@ -537,7 +538,7 @@ stdlog.Print("hello world")
### context.Context integration
Go contexts are commonly passed throughout Go code, and this can help you pass
-your Logger into places it might otherwise be hard to inject. The `Logger`
+your Logger into places it might otherwise be hard to inject. The `Logger`
instance may be attached to Go context (`context.Context`) using
`Logger.WithContext(ctx)` and extracted from it using `zerolog.Ctx(ctx)`.
For example:
@@ -562,7 +563,7 @@ func someFunc(ctx context.Context) {
```
A second form of `context.Context` integration allows you to pass the current
-context.Context into the logged event, and retrieve it from hooks. This can be
+`context.Context` into the logged event, and retrieve it from hooks. This can be
useful to log trace and span IDs or other information stored in the go context,
and facilitates the unification of logging and tracing in some systems:
@@ -640,17 +641,17 @@ if err := http.ListenAndServe(":8080", nil); err != nil {
```
## Multiple Log Output
-`zerolog.MultiLevelWriter` may be used to send the log message to multiple outputs.
-In this example, we send the log message to both `os.Stdout` and the in-built ConsoleWriter.
+
+`zerolog.MultiLevelWriter` may be used to send the log message to multiple outputs.
+
+In this example, we send the log message to both `os.Stdout` and the in-built `ConsoleWriter`.
+
```go
func main() {
- consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout}
-
- multi := zerolog.MultiLevelWriter(consoleWriter, os.Stdout)
-
- logger := zerolog.New(multi).With().Timestamp().Logger()
-
- logger.Info().Msg("Hello World!")
+ consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout}
+ multi := zerolog.MultiLevelWriter(consoleWriter, os.Stdout)
+ logger := zerolog.New(multi).With().Timestamp().Logger()
+ logger.Info().Msg("Hello World!")
}
// Output (Line 1: Console; Line 2: Stdout)
@@ -662,43 +663,45 @@ func main() {
Some settings can be changed and will be applied to all loggers:
-* `log.Logger`: You can set this value to customize the global logger (the one used by package level methods).
-* `zerolog.SetGlobalLevel`: Can raise the minimum level of all loggers. Call this with `zerolog.Disabled` to disable logging altogether (quiet mode).
-* `zerolog.DisableSampling`: If argument is `true`, all sampled loggers will stop sampling and issue 100% of their log events.
-* `zerolog.TimestampFieldName`: Can be set to customize `Timestamp` field name.
-* `zerolog.LevelFieldName`: Can be set to customize level field name.
-* `zerolog.MessageFieldName`: Can be set to customize message field name.
-* `zerolog.ErrorFieldName`: Can be set to customize `Err` field name.
-* `zerolog.TimeFieldFormat`: Can be set to customize `Time` field value formatting. If set with `zerolog.TimeFormatUnix`, `zerolog.TimeFormatUnixMs` or `zerolog.TimeFormatUnixMicro`, times are formatted as UNIX timestamp.
-* `zerolog.DurationFieldUnit`: Can be set to customize the unit for time.Duration type fields added by `Dur` (default: `time.Millisecond`).
-* `zerolog.DurationFieldInteger`: If set to `true`, `Dur` fields are formatted as integers instead of floats (default: `false`).
-* `zerolog.ErrorHandler`: Called whenever zerolog fails to write an event on its output. If not set, an error is printed on the stderr. This handler must be thread safe and non-blocking.
-* `zerolog.FloatingPointPrecision`: If set to a value other than -1, controls the number
-of digits when formatting float numbers in JSON. See
-[strconv.FormatFloat](https://pkg.go.dev/strconv#FormatFloat)
-for more details.
+- `log.Logger`: You can set this value to customize the global logger (the one used by package level methods).
+- `zerolog.SetGlobalLevel`: Can raise the minimum level of all loggers. Call this with `zerolog.Disabled` to disable logging altogether (quiet mode).
+- `zerolog.DisableSampling`: If argument is `true`, all sampled loggers will stop sampling and issue 100% of their log events.
+- `zerolog.TimestampFieldName`: Can be set to customize `Timestamp` field name.
+- `zerolog.LevelFieldName`: Can be set to customize level field name.
+- `zerolog.MessageFieldName`: Can be set to customize message field name.
+- `zerolog.ErrorFieldName`: Can be set to customize `Err` field name.
+- `zerolog.TimeFieldFormat`: Can be set to customize `Time` field value formatting. If set with `zerolog.TimeFormatUnix`, `zerolog.TimeFormatUnixMs` or `zerolog.TimeFormatUnixMicro`, times are formatted as UNIX timestamp.
+- `zerolog.DurationFieldUnit`: Can be set to customize the unit for time.Duration type fields added by `Dur` (default: `time.Millisecond`).
+- `zerolog.DurationFieldFormat`: Can be set to `DurationFormatFloat`, `DurationFormatInt`, or `DurationFormatString` (default: `DurationFormatFloat`) to append the `Duration` as a `Float64`, `Int64`, or by calling `String()` (respectively).
+- `zerolog.DurationFieldInteger`: If set to `true`, `Dur` fields are formatted as integers instead of floats (default: `false`). Deprecated: Use `zerolog.DurationFieldFormat = DurationFormatInt` instead.
+- `zerolog.ErrorHandler`: Called whenever zerolog fails to write an event on its output. If not set, an error is printed on the stderr. This handler must be thread safe and non-blocking.
+- `zerolog.FloatingPointPrecision`: If set to a value other than -1, controls the number of digits when formatting float numbers in JSON. See [strconv.FormatFloat](https://pkg.go.dev/strconv#FormatFloat)
+ for more details.
## Field Types
### Standard Types
-* `Str`
-* `Bool`
-* `Int`, `Int8`, `Int16`, `Int32`, `Int64`
-* `Uint`, `Uint8`, `Uint16`, `Uint32`, `Uint64`
-* `Float32`, `Float64`
+- `Str`
+- `Bool`
+- `Int`, `Int8`, `Int16`, `Int32`, `Int64`
+- `Uint`, `Uint8`, `Uint16`, `Uint32`, `Uint64`
+- `Float32`, `Float64`
### Advanced Fields
-* `Err`: Takes an `error` and renders it as a string using the `zerolog.ErrorFieldName` field name.
-* `Func`: Run a `func` only if the level is enabled.
-* `Timestamp`: Inserts a timestamp field with `zerolog.TimestampFieldName` field name, formatted using `zerolog.TimeFieldFormat`.
-* `Time`: Adds a field with time formatted with `zerolog.TimeFieldFormat`.
-* `Dur`: Adds a field with `time.Duration`.
-* `Dict`: Adds a sub-key/value as a field of the event.
-* `RawJSON`: Adds a field with an already encoded JSON (`[]byte`)
-* `Hex`: Adds a field with value formatted as a hexadecimal string (`[]byte`)
-* `Interface`: Uses reflection to marshal the type.
+- `Err`: Takes an `error` and renders it as a string using the `zerolog.ErrorFieldName` field name.
+- `Func`: Run a `func` only if the level is enabled.
+- `Timestamp`: Inserts a timestamp field with `zerolog.TimestampFieldName` field name, formatted using `zerolog.TimeFieldFormat`.
+- `Time`: Adds a field with time formatted with `zerolog.TimeFieldFormat`.
+- `Dur`: Adds a field with `time.Duration`.
+- `Dict`: Adds a sub-key/value as a field of the event.
+- `RawJSON`: Adds a field with an already encoded JSON (`[]byte`)
+- `Hex`: Adds a field with value formatted as a hexadecimal string (`[]byte`)
+- `Interface`: Uses reflection to marshal the type.
+- `IPAddr`: Adds a field with `net.IP`.
+- `IPPrefix`: Adds a field with `net.IPNet`.
+- `MACAddr`: Adds a field with `net.HardwareAddr`
Most fields are also available in the slice format (`Strs` for `[]string`, `Errs` for `[]error` etc.)
@@ -710,20 +713,48 @@ In addition to the default JSON encoding, `zerolog` can produce binary logs usin
go build -tags binary_log .
```
-To Decode binary encoded log files you can use any CBOR decoder. One has been tested to work
+To decode binary encoded log files you can use any CBOR decoder. One has been tested to work
with zerolog library is [CSD](https://github.com/toravir/csd/).
+## Integration with `log/slog`
+
+zerolog provides a `slog.Handler` implementation that routes `log/slog` records through a zerolog logger. This lets you use the standard library's `slog` API while keeping zerolog's performance and encoding:
+
+```go
+package main
+
+import (
+ "log/slog"
+
+ "github.com/rs/zerolog"
+ "github.com/rs/zerolog/log"
+)
+
+func main() {
+ zl := log.Logger
+ handler := zerolog.NewSlogHandler(zl)
+ logger := slog.New(handler)
+
+ logger.Info("user logged in", "user", "alice", "role", "admin")
+}
+
+// Output: {"level":"info","user":"alice","role":"admin","time":"...","message":"user logged in"}
+```
+
+The handler supports all `slog` features including `WithAttrs`, `WithGroup`, nested groups, and `LogValuer` resolution. slog levels are mapped to zerolog levels (e.g. `slog.LevelDebug` to `zerolog.DebugLevel`).
+
## Related Projects
-* [grpc-zerolog](https://github.com/cheapRoc/grpc-zerolog): Implementation of `grpclog.LoggerV2` interface using `zerolog`
-* [overlog](https://github.com/Trendyol/overlog): Implementation of `Mapped Diagnostic Context` interface using `zerolog`
-* [zerologr](https://github.com/go-logr/zerologr): Implementation of `logr.LogSink` interface using `zerolog`
+- [grpc-zerolog](https://github.com/cheapRoc/grpc-zerolog): Implementation of `grpclog.LoggerV2` interface using `zerolog`
+- [overlog](https://github.com/Trendyol/overlog): Implementation of `Mapped Diagnostic Context` interface using `zerolog`
+- [zerologr](https://github.com/go-logr/zerologr): Implementation of `logr.LogSink` interface using `zerolog`
+- [logze](https://github.com/maxbolgarin/logze): Implementation of `log/slog` interface using `zerolog`
## Benchmarks
See [logbench](http://bench.zerolog.io/) for more comprehensive and up-to-date benchmarks.
-All operations are allocation free (those numbers *include* JSON encoding):
+All operations are allocation free (those numbers _include_ JSON encoding):
```text
BenchmarkLogEmpty-8 100000000 19.1 ns/op 0 B/op 0 allocs/op
@@ -735,50 +766,50 @@ BenchmarkLogFields-8 10000000 184 ns/op 0 B/op 0 allocs/op
There are a few Go logging benchmarks and comparisons that include zerolog.
-* [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench)
-* [uber-common/zap](https://github.com/uber-go/zap#performance)
+- [imkira/go-loggers-bench](https://github.com/imkira/go-loggers-bench)
+- [uber-common/zap](https://github.com/uber-go/zap#performance)
Using Uber's zap comparison benchmark:
Log a message and 10 fields:
-| Library | Time | Bytes Allocated | Objects Allocated |
-| :--- | :---: | :---: | :---: |
-| zerolog | 767 ns/op | 552 B/op | 6 allocs/op |
-| :zap: zap | 848 ns/op | 704 B/op | 2 allocs/op |
-| :zap: zap (sugared) | 1363 ns/op | 1610 B/op | 20 allocs/op |
-| go-kit | 3614 ns/op | 2895 B/op | 66 allocs/op |
-| lion | 5392 ns/op | 5807 B/op | 63 allocs/op |
-| logrus | 5661 ns/op | 6092 B/op | 78 allocs/op |
-| apex/log | 15332 ns/op | 3832 B/op | 65 allocs/op |
-| log15 | 20657 ns/op | 5632 B/op | 93 allocs/op |
+| Library | Time | Bytes Allocated | Objects Allocated |
+| :------------------ | :---------: | :-------------: | :---------------: |
+| zerolog | 767 ns/op | 552 B/op | 6 allocs/op |
+| :zap: zap | 848 ns/op | 704 B/op | 2 allocs/op |
+| :zap: zap (sugared) | 1363 ns/op | 1610 B/op | 20 allocs/op |
+| go-kit | 3614 ns/op | 2895 B/op | 66 allocs/op |
+| lion | 5392 ns/op | 5807 B/op | 63 allocs/op |
+| logrus | 5661 ns/op | 6092 B/op | 78 allocs/op |
+| apex/log | 15332 ns/op | 3832 B/op | 65 allocs/op |
+| log15 | 20657 ns/op | 5632 B/op | 93 allocs/op |
Log a message with a logger that already has 10 fields of context:
-| Library | Time | Bytes Allocated | Objects Allocated |
-| :--- | :---: | :---: | :---: |
-| zerolog | 52 ns/op | 0 B/op | 0 allocs/op |
-| :zap: zap | 283 ns/op | 0 B/op | 0 allocs/op |
-| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op |
-| lion | 2702 ns/op | 4074 B/op | 38 allocs/op |
-| go-kit | 3378 ns/op | 3046 B/op | 52 allocs/op |
-| logrus | 4309 ns/op | 4564 B/op | 63 allocs/op |
-| apex/log | 13456 ns/op | 2898 B/op | 51 allocs/op |
-| log15 | 14179 ns/op | 2642 B/op | 44 allocs/op |
+| Library | Time | Bytes Allocated | Objects Allocated |
+| :------------------ | :---------: | :-------------: | :---------------: |
+| zerolog | 52 ns/op | 0 B/op | 0 allocs/op |
+| :zap: zap | 283 ns/op | 0 B/op | 0 allocs/op |
+| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op |
+| lion | 2702 ns/op | 4074 B/op | 38 allocs/op |
+| go-kit | 3378 ns/op | 3046 B/op | 52 allocs/op |
+| logrus | 4309 ns/op | 4564 B/op | 63 allocs/op |
+| apex/log | 13456 ns/op | 2898 B/op | 51 allocs/op |
+| log15 | 14179 ns/op | 2642 B/op | 44 allocs/op |
Log a static string, without any context or `printf`-style templating:
-| Library | Time | Bytes Allocated | Objects Allocated |
-| :--- | :---: | :---: | :---: |
-| zerolog | 50 ns/op | 0 B/op | 0 allocs/op |
-| :zap: zap | 236 ns/op | 0 B/op | 0 allocs/op |
-| standard library | 453 ns/op | 80 B/op | 2 allocs/op |
-| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op |
-| go-kit | 508 ns/op | 656 B/op | 13 allocs/op |
-| lion | 771 ns/op | 1224 B/op | 10 allocs/op |
-| logrus | 1244 ns/op | 1505 B/op | 27 allocs/op |
-| apex/log | 2751 ns/op | 584 B/op | 11 allocs/op |
-| log15 | 5181 ns/op | 1592 B/op | 26 allocs/op |
+| Library | Time | Bytes Allocated | Objects Allocated |
+| :------------------ | :--------: | :-------------: | :---------------: |
+| zerolog | 50 ns/op | 0 B/op | 0 allocs/op |
+| :zap: zap | 236 ns/op | 0 B/op | 0 allocs/op |
+| standard library | 453 ns/op | 80 B/op | 2 allocs/op |
+| :zap: zap (sugared) | 337 ns/op | 80 B/op | 2 allocs/op |
+| go-kit | 508 ns/op | 656 B/op | 13 allocs/op |
+| lion | 771 ns/op | 1224 B/op | 10 allocs/op |
+| logrus | 1244 ns/op | 1505 B/op | 27 allocs/op |
+| apex/log | 2751 ns/op | 584 B/op | 11 allocs/op |
+| log15 | 5181 ns/op | 1592 B/op | 26 allocs/op |
## Caveats
@@ -798,7 +829,7 @@ In this case, many consumers will take the last value, but this is not guarantee
### Concurrency safety
-Be careful when calling UpdateContext. It is not concurrency safe. Use the With method to create a child logger:
+Be careful when calling `UpdateContext`. It is not concurrency safe. Use the `With()` method to create a child logger:
```go
func handler(w http.ResponseWriter, r *http.Request) {
@@ -811,3 +842,17 @@ func handler(w http.ResponseWriter, r *http.Request) {
})
}
```
+
+The `Event` object returned from the `Logger` level-specific message functions (e.g. `Log()`, `Trace()`, `Debug()`, etc.)
+is allocated in `sync.Pool` memory that will be returned to the pool as soon as the `Msg()`, `Msgf()`, `Send()`,
+or `MsgFunc()` writes the message and **must not** be accessed afterwards.
+
+**Do not** hold a reference to the `*Event` while in callback functions or your own code. This is especially important in
+`Hook.Run()` and `HookFunc` functions or `MarshalZerologObject(e *Event)` callback (e.g. `LogObjectMarshaler` implementations).
+
+Any `Array` objects returned from `Context.CreateArray()` or `Event.CreateArray()` are from a `sync.Pool` so **do not** hold
+references to them from within any `MarshalZerologArray(a *Array)` callback (e.g. `LogArrayMarshaler` implementations) or your
+own code as they will be cleared and returned to the pool after being buffered by a call to `Context.Array()` or `Event.Array()`.
+
+Any _dictionary_ `Event` returned from `Context.CreateDict()` or `Event.CreateDict()` **must not** be referenced after being
+buffered by a call to `Array.Dict()`, `Context.Dict()`, or `Event.Dict()` as they will be cleared and returned to the pool.
diff --git a/vendor/github.com/rs/zerolog/array.go b/vendor/github.com/rs/zerolog/array.go
index ba35b2835..dbb0bda3b 100644
--- a/vendor/github.com/rs/zerolog/array.go
+++ b/vendor/github.com/rs/zerolog/array.go
@@ -1,6 +1,7 @@
package zerolog
import (
+ "context"
"net"
"sync"
"time"
@@ -17,10 +18,19 @@ var arrayPool = &sync.Pool{
// Array is used to prepopulate an array of items
// which can be re-used to add to log messages.
type Array struct {
- buf []byte
+ buf []byte
+ stack bool // enable error stack trace
+ ctx context.Context // Optional Go context
+ ch []Hook // hooks
}
func putArray(a *Array) {
+ // prevent any subsequent use of the Array contextual state and truncate the buffer
+ a.stack = false
+ a.ctx = nil
+ a.ch = nil
+ a.buf = a.buf[:0]
+
// Proper usage of a sync.Pool requires each entry to have approximately
// the same memory cost. To obtain this property when the stored type
// contains a variably-sized buffer, we add a hard limit on the maximum buffer
@@ -28,22 +38,28 @@ func putArray(a *Array) {
//
// See https://golang.org/issue/23199
const maxSize = 1 << 16 // 64KiB
- if cap(a.buf) > maxSize {
- return
+ if cap(a.buf) <= maxSize {
+ arrayPool.Put(a)
}
- arrayPool.Put(a)
}
// Arr creates an array to be added to an Event or Context.
+// WARNING: This function is deprecated because it does not preserve
+// the stack, hooks, and context from the parent event.
+// Deprecated: Use Event.CreateArray or Context.CreateArray instead.
func Arr() *Array {
a := arrayPool.Get().(*Array)
a.buf = a.buf[:0]
+ a.stack = false
+ a.ctx = nil
+ a.ch = nil
return a
}
// MarshalZerologArray method here is no-op - since data is
// already in the needed format.
func (*Array) MarshalZerologArray(*Array) {
+ // untestable: there's no code to be covered
}
func (a *Array) write(dst []byte) []byte {
@@ -59,11 +75,7 @@ func (a *Array) write(dst []byte) []byte {
// Object marshals an object that implement the LogObjectMarshaler
// interface and appends it to the array.
func (a *Array) Object(obj LogObjectMarshaler) *Array {
- e := Dict()
- obj.MarshalZerologObject(e)
- e.buf = enc.AppendEndMarker(e.buf)
- a.buf = append(enc.AppendArrayDelim(a.buf), e.buf...)
- putEvent(e)
+ a.buf = appendObject(enc.AppendArrayDelim(a.buf), obj, a.stack, a.ctx, a.ch)
return a
}
@@ -94,16 +106,12 @@ func (a *Array) RawJSON(val []byte) *Array {
// Err serializes and appends the err to the array.
func (a *Array) Err(err error) *Array {
switch m := ErrorMarshalFunc(err).(type) {
+ case nil:
+ a.buf = enc.AppendNil(enc.AppendArrayDelim(a.buf))
case LogObjectMarshaler:
- e := newEvent(nil, 0)
- e.buf = e.buf[:0]
- e.appendObject(m)
- a.buf = append(enc.AppendArrayDelim(a.buf), e.buf...)
- putEvent(e)
+ a = a.Object(m)
case error:
- if m == nil || isNilValue(m) {
- a.buf = enc.AppendNil(enc.AppendArrayDelim(a.buf))
- } else {
+ if !isNilValue(m) {
a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m.Error())
}
case string:
@@ -115,6 +123,27 @@ func (a *Array) Err(err error) *Array {
return a
}
+// Errs serializes and appends errors to the array.
+func (a *Array) Errs(errs []error) *Array {
+ for _, err := range errs {
+ switch m := ErrorMarshalFunc(err).(type) {
+ case nil:
+ a = a.Interface(nil)
+ case LogObjectMarshaler:
+ a = a.Object(m)
+ case error:
+ if !isNilValue(m) {
+ a = a.Str(m.Error())
+ }
+ case string:
+ a = a.Str(m)
+ default:
+ a = a.Interface(m)
+ }
+ }
+ return a
+}
+
// Bool appends the val as a bool to the array.
func (a *Array) Bool(b bool) *Array {
a.buf = enc.AppendBool(enc.AppendArrayDelim(a.buf), b)
@@ -201,7 +230,7 @@ func (a *Array) Time(t time.Time) *Array {
// Dur appends d to the array.
func (a *Array) Dur(d time.Duration) *Array {
- a.buf = enc.AppendDuration(enc.AppendArrayDelim(a.buf), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ a.buf = enc.AppendDuration(enc.AppendArrayDelim(a.buf), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
return a
}
@@ -214,19 +243,19 @@ func (a *Array) Interface(i interface{}) *Array {
return a
}
-// IPAddr adds IPv4 or IPv6 address to the array
+// IPAddr adds a net.IP IPv4 or IPv6 address to the array
func (a *Array) IPAddr(ip net.IP) *Array {
a.buf = enc.AppendIPAddr(enc.AppendArrayDelim(a.buf), ip)
return a
}
-// IPPrefix adds IPv4 or IPv6 Prefix (IP + mask) to the array
+// IPPrefix adds a net.IPNet IPv4 or IPv6 Prefix (IP + mask) to the array
func (a *Array) IPPrefix(pfx net.IPNet) *Array {
a.buf = enc.AppendIPPrefix(enc.AppendArrayDelim(a.buf), pfx)
return a
}
-// MACAddr adds a MAC (Ethernet) address to the array
+// MACAddr adds a net.HardwareAddr MAC (Ethernet) address to the array
func (a *Array) MACAddr(ha net.HardwareAddr) *Array {
a.buf = enc.AppendMACAddr(enc.AppendArrayDelim(a.buf), ha)
return a
@@ -236,5 +265,12 @@ func (a *Array) MACAddr(ha net.HardwareAddr) *Array {
func (a *Array) Dict(dict *Event) *Array {
dict.buf = enc.AppendEndMarker(dict.buf)
a.buf = append(enc.AppendArrayDelim(a.buf), dict.buf...)
+ putEvent(dict)
+ return a
+}
+
+// Type adds the val's type using reflection to the array.
+func (a *Array) Type(val interface{}) *Array {
+ a.buf = enc.AppendType(enc.AppendArrayDelim(a.buf), val)
return a
}
diff --git a/vendor/github.com/rs/zerolog/console.go b/vendor/github.com/rs/zerolog/console.go
index 6c881ef60..39bdad165 100644
--- a/vendor/github.com/rs/zerolog/console.go
+++ b/vendor/github.com/rs/zerolog/console.go
@@ -101,9 +101,9 @@ type ConsoleWriter struct {
// NewConsoleWriter creates and initializes a new ConsoleWriter.
func NewConsoleWriter(options ...func(w *ConsoleWriter)) ConsoleWriter {
w := ConsoleWriter{
- Out: os.Stdout,
- TimeFormat: consoleDefaultTimeFormat,
- PartsOrder: consoleDefaultPartsOrder(),
+ Out: os.Stdout,
+ TimeFormat: consoleDefaultTimeFormat,
+ PartsOrder: consoleDefaultPartsOrder(),
}
for _, opt := range options {
diff --git a/vendor/github.com/rs/zerolog/context.go b/vendor/github.com/rs/zerolog/context.go
index ff9a3ae27..c72b57139 100644
--- a/vendor/github.com/rs/zerolog/context.go
+++ b/vendor/github.com/rs/zerolog/context.go
@@ -3,7 +3,6 @@ package zerolog
import (
"context"
"fmt"
- "io"
"math"
"net"
"time"
@@ -23,7 +22,7 @@ func (c Context) Logger() Logger {
// Only map[string]interface{} and []interface{} are accepted. []interface{} must
// alternate string keys and arbitrary values, and extraneous ones are ignored.
func (c Context) Fields(fields interface{}) Context {
- c.l.context = appendFields(c.l.context, fields, c.l.stack)
+ c.l.context = appendFields(c.l.context, fields, c.l.stack, c.l.ctx, c.l.hooks)
return c
}
@@ -35,8 +34,28 @@ func (c Context) Dict(key string, dict *Event) Context {
return c
}
+// CreateDict creates an Event to be used with the Context.Dict method.
+// It preserves the stack, hooks, and context from the logger.
+// Call usual field methods like Str, Int etc to add fields to this
+// event and give it as argument the Context.Dict method.
+func (c Context) CreateDict() *Event {
+ return newEvent(nil, DebugLevel, c.l.stack, c.l.ctx, c.l.hooks)
+}
+
+// CreateArray creates an Array to be used with the Context.Array method.
+// It preserves the stack, hooks, and context from the logger.
+// Call usual field methods like Str, Int etc to add elements to this
+// array and give it as argument the Context.Array method.
+func (c Context) CreateArray() *Array {
+ a := Arr()
+ a.stack = c.l.stack
+ a.ctx = c.l.ctx
+ a.ch = c.l.hooks
+ return a
+}
+
// Array adds the field key with an array to the event context.
-// Use zerolog.Arr() to create the array or pass a type that
+// Use c.CreateArray() to create the array or pass a type that
// implement the LogArrayMarshaler interface.
func (c Context) Array(key string, arr LogArrayMarshaler) Context {
c.l.context = enc.AppendKey(c.l.context, key)
@@ -44,29 +63,44 @@ func (c Context) Array(key string, arr LogArrayMarshaler) Context {
c.l.context = arr.write(c.l.context)
return c
}
- var a *Array
- if aa, ok := arr.(*Array); ok {
- a = aa
- } else {
- a = Arr()
- arr.MarshalZerologArray(a)
- }
+ a := c.CreateArray()
+ arr.MarshalZerologArray(a)
c.l.context = a.write(c.l.context)
return c
}
// Object marshals an object that implement the LogObjectMarshaler interface.
func (c Context) Object(key string, obj LogObjectMarshaler) Context {
- e := newEvent(LevelWriterAdapter{io.Discard}, 0)
+ e := c.l.scratchEvent()
e.Object(key, obj)
c.l.context = enc.AppendObjectData(c.l.context, e.buf)
putEvent(e)
return c
}
+// Objects adds the field key with objs to the logger context as an array of
+// objects that implement the LogObjectMarshaler interface.
+//
+// This is the array version that accepts a slice of LogObjectMarshaler objects.
+func (c Context) Objects(key string, objs []LogObjectMarshaler) Context {
+ e := c.l.scratchEvent()
+ e.Objects(key, objs)
+ c.l.context = enc.AppendObjectData(c.l.context, e.buf)
+ putEvent(e)
+ return c
+}
+
+// ObjectsV adds the field key with objs to the logger context as an array of
+// objects that implement the LogObjectMarshaler interface.
+//
+// This is a variadic version that accepts a list of individual LogObjectMarshaler objects.
+func (c Context) ObjectsV(key string, objs ...LogObjectMarshaler) Context {
+ return c.Objects(key, objs)
+}
+
// EmbedObject marshals and Embeds an object that implement the LogObjectMarshaler interface.
func (c Context) EmbedObject(obj LogObjectMarshaler) Context {
- e := newEvent(LevelWriterAdapter{io.Discard}, 0)
+ e := c.l.scratchEvent()
e.EmbedObject(obj)
c.l.context = enc.AppendObjectData(c.l.context, e.buf)
putEvent(e)
@@ -80,11 +114,20 @@ func (c Context) Str(key, val string) Context {
}
// Strs adds the field key with val as a string to the logger context.
+//
+// This is the array version that accepts a slice of string values.
func (c Context) Strs(key string, vals []string) Context {
c.l.context = enc.AppendStrings(enc.AppendKey(c.l.context, key), vals)
return c
}
+// StrsV adds the field key with vals as a []string to the logger context.
+//
+// This is a variadic version that accepts a list of individual strings.
+func (c Context) StrsV(key string, vals ...string) Context {
+ return c.Strs(key, vals)
+}
+
// Stringer adds the field key with val.String() (or null if val is nil) to the logger context.
func (c Context) Stringer(key string, val fmt.Stringer) Context {
if val != nil {
@@ -96,6 +139,24 @@ func (c Context) Stringer(key string, val fmt.Stringer) Context {
return c
}
+// Stringers adds the field key with vals to the logger context where each
+// individual val is added by calling val.String().
+//
+// This is the array version that accepts a slice of fmt.Stringer values.
+func (c Context) Stringers(key string, vals []fmt.Stringer) Context {
+ c.l.context = enc.AppendStringers(enc.AppendKey(c.l.context, key), vals)
+ return c
+}
+
+// StringersV adds the field key with vals to the logger context where each
+// individual val is added by calling val.String().
+//
+// This is a variadic version that accepts a list of individual
+// fmt.Stringer values.
+func (c Context) StringersV(key string, vals ...fmt.Stringer) Context {
+ return c.Stringers(key, vals)
+}
+
// Bytes adds the field key with val as a []byte to the logger context.
func (c Context) Bytes(key string, val []byte) Context {
c.l.context = enc.AppendBytes(enc.AppendKey(c.l.context, key), val)
@@ -118,6 +179,7 @@ func (c Context) RawJSON(key string, b []byte) Context {
}
// AnErr adds the field key with serialized err to the logger context.
+// If err is nil, no field is added.
func (c Context) AnErr(key string, err error) Context {
switch m := ErrorMarshalFunc(err).(type) {
case nil:
@@ -125,11 +187,10 @@ func (c Context) AnErr(key string, err error) Context {
case LogObjectMarshaler:
return c.Object(key, m)
case error:
- if m == nil || isNilValue(m) {
+ if isNilValue(m) {
return c
- } else {
- return c.Str(key, m.Error())
}
+ return c.Str(key, m.Error())
case string:
return c.Str(key, m)
default:
@@ -140,24 +201,7 @@ func (c Context) AnErr(key string, err error) Context {
// Errs adds the field key with errs as an array of serialized errors to the
// logger context.
func (c Context) Errs(key string, errs []error) Context {
- arr := Arr()
- for _, err := range errs {
- switch m := ErrorMarshalFunc(err).(type) {
- case LogObjectMarshaler:
- arr = arr.Object(m)
- case error:
- if m == nil || isNilValue(m) {
- arr = arr.Interface(nil)
- } else {
- arr = arr.Str(m.Error())
- }
- case string:
- arr = arr.Str(m)
- default:
- arr = arr.Interface(m)
- }
- }
-
+ arr := c.CreateArray().Errs(errs)
return c.Array(key, arr)
}
@@ -166,12 +210,11 @@ func (c Context) Err(err error) Context {
if c.l.stack && ErrorStackMarshaler != nil {
switch m := ErrorStackMarshaler(err).(type) {
case nil:
+ return c // do nothing with nil errors
case LogObjectMarshaler:
c = c.Object(ErrorStackFieldName, m)
case error:
- if m != nil && !isNilValue(m) {
- c = c.Str(ErrorStackFieldName, m.Error())
- }
+ c = c.Str(ErrorStackFieldName, m.Error())
case string:
c = c.Str(ErrorStackFieldName, m)
default:
@@ -377,15 +420,15 @@ func (c Context) Times(key string, t []time.Time) Context {
return c
}
-// Dur adds the fields key with d divided by unit and stored as a float.
+// Dur adds the field key with d divided by unit and stored as a float.
func (c Context) Dur(key string, d time.Duration) Context {
- c.l.context = enc.AppendDuration(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ c.l.context = enc.AppendDuration(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
return c
}
-// Durs adds the fields key with d divided by unit and stored as a float.
+// Durs adds the field key with d divided by unit and stored as a float.
func (c Context) Durs(key string, d []time.Duration) Context {
- c.l.context = enc.AppendDurations(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ c.l.context = enc.AppendDurations(enc.AppendKey(c.l.context, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
return c
}
@@ -461,19 +504,31 @@ func (c Context) Stack() Context {
return c
}
-// IPAddr adds IPv4 or IPv6 Address to the context
+// IPAddr adds adds the field key with ip as a net.IP IPv4 or IPv6 Address to the context
func (c Context) IPAddr(key string, ip net.IP) Context {
c.l.context = enc.AppendIPAddr(enc.AppendKey(c.l.context, key), ip)
return c
}
-// IPPrefix adds IPv4 or IPv6 Prefix (address and mask) to the context
+// IPAddrs adds the field key with ip as a []net.IP array of IPv4 or IPv6 Address to the context
+func (c Context) IPAddrs(key string, ip []net.IP) Context {
+ c.l.context = enc.AppendIPAddrs(enc.AppendKey(c.l.context, key), ip)
+ return c
+}
+
+// IPPrefix adds adds the field key with pfx as a []net.IPNet IPv4 or IPv6 Prefix (address and mask) to the context
func (c Context) IPPrefix(key string, pfx net.IPNet) Context {
c.l.context = enc.AppendIPPrefix(enc.AppendKey(c.l.context, key), pfx)
return c
}
-// MACAddr adds MAC address to the context
+// IPPrefix adds adds the field key with pfx as a []net.IPNet array of IPv4 or IPv6 Prefix (address and mask) to the context
+func (c Context) IPPrefixes(key string, pfx []net.IPNet) Context {
+ c.l.context = enc.AppendIPPrefixes(enc.AppendKey(c.l.context, key), pfx)
+ return c
+}
+
+// MACAddr adds adds the field key with ha as a net.HardwareAddr MAC address to the context
func (c Context) MACAddr(key string, ha net.HardwareAddr) Context {
c.l.context = enc.AppendMACAddr(enc.AppendKey(c.l.context, key), ha)
return c
diff --git a/vendor/github.com/rs/zerolog/ctx.go b/vendor/github.com/rs/zerolog/ctx.go
index 60432d153..649191e9f 100644
--- a/vendor/github.com/rs/zerolog/ctx.go
+++ b/vendor/github.com/rs/zerolog/ctx.go
@@ -25,12 +25,11 @@ type ctxKey struct{}
// replacing it in a new Context), use UpdateContext with the following
// notation:
//
-// ctx := r.Context()
-// l := zerolog.Ctx(ctx)
-// l.UpdateContext(func(c Context) Context {
-// return c.Str("bar", "baz")
-// })
-//
+// ctx := r.Context()
+// l := zerolog.Ctx(ctx)
+// l.UpdateContext(func(c Context) Context {
+// return c.Str("bar", "baz")
+// })
func (l Logger) WithContext(ctx context.Context) context.Context {
if _, ok := ctx.Value(ctxKey{}).(*Logger); !ok && l.level == Disabled {
// Do not store disabled logger.
diff --git a/vendor/github.com/rs/zerolog/encoder.go b/vendor/github.com/rs/zerolog/encoder.go
index 4dbaf380e..652380c20 100644
--- a/vendor/github.com/rs/zerolog/encoder.go
+++ b/vendor/github.com/rs/zerolog/encoder.go
@@ -13,8 +13,8 @@ type encoder interface {
AppendBool(dst []byte, val bool) []byte
AppendBools(dst []byte, vals []bool) []byte
AppendBytes(dst, s []byte) []byte
- AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool, precision int) []byte
- AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool, precision int) []byte
+ AppendDuration(dst []byte, d time.Duration, unit time.Duration, format string, useInt bool, precision int) []byte
+ AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, format string, useInt bool, precision int) []byte
AppendEndMarker(dst []byte) []byte
AppendFloat32(dst []byte, val float32, precision int) []byte
AppendFloat64(dst []byte, val float64, precision int) []byte
diff --git a/vendor/github.com/rs/zerolog/event.go b/vendor/github.com/rs/zerolog/event.go
index 56de6061c..7538e66e9 100644
--- a/vendor/github.com/rs/zerolog/event.go
+++ b/vendor/github.com/rs/zerolog/event.go
@@ -32,6 +32,15 @@ type Event struct {
}
func putEvent(e *Event) {
+ // prevent any subsequent use of the Event contextual state and truncate the buffer
+ e.w = nil
+ e.done = nil
+ e.stack = false
+ e.ch = nil
+ e.skipFrame = 0
+ e.ctx = nil
+ e.buf = e.buf[:0]
+
// Proper usage of a sync.Pool requires each entry to have approximately
// the same memory cost. To obtain this property when the stored type
// contains a variably-sized buffer, we add a hard limit on the maximum buffer
@@ -39,10 +48,9 @@ func putEvent(e *Event) {
//
// See https://golang.org/issue/23199
const maxSize = 1 << 16 // 64KiB
- if cap(e.buf) > maxSize {
- return
+ if cap(e.buf) <= maxSize {
+ eventPool.Put(e)
}
- eventPool.Put(e)
}
// LogObjectMarshaler provides a strongly-typed and encoding-agnostic interface
@@ -57,14 +65,15 @@ type LogArrayMarshaler interface {
MarshalZerologArray(a *Array)
}
-func newEvent(w LevelWriter, level Level) *Event {
+func newEvent(w LevelWriter, level Level, stack bool, ctx context.Context, hooks []Hook) *Event {
e := eventPool.Get().(*Event)
e.buf = e.buf[:0]
- e.ch = nil
+ e.stack = stack
+ e.ctx = ctx
+ e.ch = hooks
e.buf = enc.AppendBeginMarker(e.buf)
e.w = w
e.level = level
- e.stack = false
e.skipFrame = 0
return e
}
@@ -164,31 +173,58 @@ func (e *Event) Fields(fields interface{}) *Event {
if e == nil {
return e
}
- e.buf = appendFields(e.buf, fields, e.stack)
+ e.buf = appendFields(e.buf, fields, e.stack, e.ctx, e.ch)
return e
}
// Dict adds the field key with a dict to the event context.
-// Use zerolog.Dict() to create the dictionary.
+// Use e.CreateDict() to create the dictionary.
func (e *Event) Dict(key string, dict *Event) *Event {
- if e == nil {
- return e
+ if e != nil {
+ dict.buf = enc.AppendEndMarker(dict.buf)
+ e.buf = append(enc.AppendKey(e.buf, key), dict.buf...)
}
- dict.buf = enc.AppendEndMarker(dict.buf)
- e.buf = append(enc.AppendKey(e.buf, key), dict.buf...)
putEvent(dict)
return e
}
+// CreateDict creates an Event to be used with the *Event.Dict method.
+// It preserves the stack, hooks, and context from the parent event.
+// Call usual field methods like Str, Int etc to add fields to this
+// event and give it as argument the *Event.Dict method.
+func (e *Event) CreateDict() *Event {
+ if e == nil {
+ return newEvent(nil, DebugLevel, false, nil, nil)
+ }
+ return newEvent(nil, DebugLevel, e.stack, e.ctx, e.ch)
+}
+
// Dict creates an Event to be used with the *Event.Dict method.
// Call usual field methods like Str, Int etc to add fields to this
// event and give it as argument the *Event.Dict method.
+// NOTE: This function is deprecated because it does not preserve
+// the stack, hooks, and context from the parent event.
+// Deprecated: Use Event.CreateDict instead.
func Dict() *Event {
- return newEvent(nil, 0)
+ return newEvent(nil, DebugLevel, false, nil, nil)
+}
+
+// CreateArray creates an Array to be used with the *Event.Array method.
+// It preserves the stack, hooks, and context from the parent event.
+// Call usual field methods like Str, Int etc to add elements to this
+// array and give it as argument the *Event.Array method.
+func (e *Event) CreateArray() *Array {
+ a := Arr()
+ if e != nil {
+ a.stack = e.stack
+ a.ctx = e.ctx
+ a.ch = e.ch
+ }
+ return a
}
// Array adds the field key with an array to the event context.
-// Use zerolog.Arr() to create the array or pass a type that
+// Use e.CreateArray() to create the array or pass a type that
// implement the LogArrayMarshaler interface.
func (e *Event) Array(key string, arr LogArrayMarshaler) *Event {
if e == nil {
@@ -199,7 +235,7 @@ func (e *Event) Array(key string, arr LogArrayMarshaler) *Event {
if aa, ok := arr.(*Array); ok {
a = aa
} else {
- a = Arr()
+ a = e.CreateArray()
arr.MarshalZerologArray(a)
}
e.buf = a.write(e.buf)
@@ -228,6 +264,33 @@ func (e *Event) Object(key string, obj LogObjectMarshaler) *Event {
return e
}
+// Objects adds the field key with objs as an array of objects that
+// implement the LogObjectMarshaler interface to the event.
+//
+// This is the array version that accepts a slice of LogObjectMarshaler objects.
+func (e *Event) Objects(key string, objs []LogObjectMarshaler) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendArrayStart(enc.AppendKey(e.buf, key))
+ for i, obj := range objs {
+ e.buf = appendObject(e.buf, obj, e.stack, e.ctx, e.ch)
+ if i < (len(objs) - 1) {
+ e.buf = enc.AppendArrayDelim(e.buf)
+ }
+ }
+ e.buf = enc.AppendArrayEnd(e.buf)
+ return e
+}
+
+// ObjectsV adds the field key with objs as an array of objects that
+// implement the LogObjectMarshaler interface to the event.
+//
+// This is a variadic version that accepts a list of individual LogObjectMarshaler objects.
+func (e *Event) ObjectsV(key string, objs ...LogObjectMarshaler) *Event {
+ return e.Objects(key, objs)
+}
+
// Func allows an anonymous func to run only if the event is enabled.
func (e *Event) Func(f func(e *Event)) *Event {
if e != nil && e.Enabled() {
@@ -258,6 +321,8 @@ func (e *Event) Str(key, val string) *Event {
}
// Strs adds the field key with vals as a []string to the *Event context.
+//
+// This is the array version that accepts a slice of string values.
func (e *Event) Strs(key string, vals []string) *Event {
if e == nil {
return e
@@ -266,8 +331,16 @@ func (e *Event) Strs(key string, vals []string) *Event {
return e
}
-// Stringer adds the field key with val.String() (or null if val is nil)
-// to the *Event context.
+// StrsV adds the field key with vals as a []string to the *Event context.
+//
+// This is a variadic version that accepts a list of individual strings.
+func (e *Event) StrsV(key string, vals ...string) *Event {
+ return e.Strs(key, vals)
+}
+
+// Stringer adds the field key and a val to the *Event context.
+// If val is not nil, it is added by calling val.String().
+// If val is nil, it is encoded as null without calling String().
func (e *Event) Stringer(key string, val fmt.Stringer) *Event {
if e == nil {
return e
@@ -276,9 +349,11 @@ func (e *Event) Stringer(key string, val fmt.Stringer) *Event {
return e
}
-// Stringers adds the field key with vals where each individual val
-// is used as val.String() (or null if val is empty) to the *Event
-// context.
+// Stringers adds the field key with vals to the *Event context.
+// If a val is not nil, it is added by calling val.String().
+// If a val is nil, it is encoded as null without calling String().
+//
+// This is the array version that accepts a slice of fmt.Stringer values.
func (e *Event) Stringers(key string, vals []fmt.Stringer) *Event {
if e == nil {
return e
@@ -287,6 +362,16 @@ func (e *Event) Stringers(key string, vals []fmt.Stringer) *Event {
return e
}
+// StringersV adds the field key with vals to the *Event context.
+// If a val is not nil, it is added by calling val.String().
+// If a val is nil, it is encoded as null without calling String().
+//
+// This is a variadic version that accepts a list of individual
+// fmt.Stringer values.
+func (e *Event) StringersV(key string, vals ...fmt.Stringer) *Event {
+ return e.Stringers(key, vals)
+}
+
// Bytes adds the field key with val as a string to the *Event context.
//
// Runes outside of normal ASCII ranges will be hex-encoded in the resulting
@@ -344,11 +429,10 @@ func (e *Event) AnErr(key string, err error) *Event {
case LogObjectMarshaler:
return e.Object(key, m)
case error:
- if m == nil || isNilValue(m) {
+ if isNilValue(m) {
return e
- } else {
- return e.Str(key, m.Error())
}
+ return e.Str(key, m.Error())
case string:
return e.Str(key, m)
default:
@@ -362,20 +446,7 @@ func (e *Event) Errs(key string, errs []error) *Event {
if e == nil {
return e
}
- arr := Arr()
- for _, err := range errs {
- switch m := ErrorMarshalFunc(err).(type) {
- case LogObjectMarshaler:
- arr = arr.Object(m)
- case error:
- arr = arr.Err(m)
- case string:
- arr = arr.Str(m)
- default:
- arr = arr.Interface(m)
- }
- }
-
+ arr := e.CreateArray().Errs(errs)
return e.Array(key, arr)
}
@@ -391,21 +462,22 @@ func (e *Event) Err(err error) *Event {
if e == nil {
return e
}
+
if e.stack && ErrorStackMarshaler != nil {
switch m := ErrorStackMarshaler(err).(type) {
case nil:
+ return e
case LogObjectMarshaler:
- e.Object(ErrorStackFieldName, m)
+ e = e.Object(ErrorStackFieldName, m)
case error:
- if m != nil && !isNilValue(m) {
- e.Str(ErrorStackFieldName, m.Error())
- }
+ e = e.Str(ErrorStackFieldName, m.Error())
case string:
- e.Str(ErrorStackFieldName, m)
+ e = e.Str(ErrorStackFieldName, m)
default:
- e.Interface(ErrorStackFieldName, m)
+ e = e.Interface(ErrorStackFieldName, m)
}
}
+
return e.AnErr(ErrorFieldName, err)
}
@@ -431,8 +503,8 @@ func (e *Event) Ctx(ctx context.Context) *Event {
}
// GetCtx retrieves the Go context.Context which is optionally stored in the
-// Event. This allows Hooks and functions passed to Func() to retrieve values
-// which are stored in the context.Context. This can be useful in tracing,
+// Event. This allows Hooks and functions passed to Func() to retrieve values
+// which are stored in the context.Context. This can be useful in tracing,
// where span information is commonly propagated in the context.Context.
func (e *Event) GetCtx() context.Context {
if e == nil || e.ctx == nil {
@@ -713,7 +785,7 @@ func (e *Event) Dur(key string, d time.Duration) *Event {
if e == nil {
return e
}
- e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
return e
}
@@ -724,7 +796,7 @@ func (e *Event) Durs(key string, d []time.Duration) *Event {
if e == nil {
return e
}
- e.buf = enc.AppendDurations(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ e.buf = enc.AppendDurations(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
return e
}
@@ -739,7 +811,7 @@ func (e *Event) TimeDiff(key string, t time.Time, start time.Time) *Event {
if t.After(start) {
d = t.Sub(start)
}
- e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ e.buf = enc.AppendDuration(enc.AppendKey(e.buf, key), d, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
return e
}
@@ -794,15 +866,13 @@ func (e *Event) caller(skip int) *Event {
if e == nil {
return e
}
- pc, file, line, ok := runtime.Caller(skip + e.skipFrame)
- if !ok {
- return e
+ if pc, file, line, ok := runtime.Caller(skip + e.skipFrame); ok {
+ e.buf = enc.AppendString(enc.AppendKey(e.buf, CallerFieldName), CallerMarshalFunc(pc, file, line))
}
- e.buf = enc.AppendString(enc.AppendKey(e.buf, CallerFieldName), CallerMarshalFunc(pc, file, line))
return e
}
-// IPAddr adds IPv4 or IPv6 Address to the event
+// IPAddr adds the field key with ip as a net.IP IPv4 or IPv6 Address to the event
func (e *Event) IPAddr(key string, ip net.IP) *Event {
if e == nil {
return e
@@ -811,7 +881,16 @@ func (e *Event) IPAddr(key string, ip net.IP) *Event {
return e
}
-// IPPrefix adds IPv4 or IPv6 Prefix (address and mask) to the event
+// IPAddrs adds the field key with ip as a net.IP array of IPv4 or IPv6 Address to the event
+func (e *Event) IPAddrs(key string, ip []net.IP) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendIPAddrs(enc.AppendKey(e.buf, key), ip)
+ return e
+}
+
+// IPPrefix adds the field key with pfx as a net.IPNet IPv4 or IPv6 Prefix (address and mask) to the event
func (e *Event) IPPrefix(key string, pfx net.IPNet) *Event {
if e == nil {
return e
@@ -820,7 +899,16 @@ func (e *Event) IPPrefix(key string, pfx net.IPNet) *Event {
return e
}
-// MACAddr adds MAC address to the event
+// IPPrefixes the field key with pfx as a net.IPNet array of IPv4 or IPv6 Prefixes (address and mask) to the event
+func (e *Event) IPPrefixes(key string, pfx []net.IPNet) *Event {
+ if e == nil {
+ return e
+ }
+ e.buf = enc.AppendIPPrefixes(enc.AppendKey(e.buf, key), pfx)
+ return e
+}
+
+// MACAddr the field key with ha as a net.HardwareAddr MAC address to the event
func (e *Event) MACAddr(key string, ha net.HardwareAddr) *Event {
if e == nil {
return e
diff --git a/vendor/github.com/rs/zerolog/fields.go b/vendor/github.com/rs/zerolog/fields.go
index 99f52718e..08759ef28 100644
--- a/vendor/github.com/rs/zerolog/fields.go
+++ b/vendor/github.com/rs/zerolog/fields.go
@@ -1,24 +1,31 @@
package zerolog
import (
+ "context"
"encoding/json"
+ "io"
"net"
+ "reflect"
"sort"
"time"
- "unsafe"
)
-func isNilValue(i interface{}) bool {
- return (*[2]uintptr)(unsafe.Pointer(&i))[1] == 0
+func isNilValue(e error) bool {
+ switch reflect.TypeOf(e).Kind() {
+ case reflect.Ptr:
+ return reflect.ValueOf(e).IsNil()
+ default:
+ return false
+ }
}
-func appendFields(dst []byte, fields interface{}, stack bool) []byte {
+func appendFields(dst []byte, fields interface{}, stack bool, ctx context.Context, hooks []Hook) []byte {
switch fields := fields.(type) {
case []interface{}:
if n := len(fields); n&0x1 == 1 { // odd number
fields = fields[:n-1]
}
- dst = appendFieldList(dst, fields, stack)
+ dst = appendFieldList(dst, fields, stack, ctx, hooks)
case map[string]interface{}:
keys := make([]string, 0, len(fields))
for key := range fields {
@@ -28,13 +35,22 @@ func appendFields(dst []byte, fields interface{}, stack bool) []byte {
kv := make([]interface{}, 2)
for _, key := range keys {
kv[0], kv[1] = key, fields[key]
- dst = appendFieldList(dst, kv, stack)
+ dst = appendFieldList(dst, kv, stack, ctx, hooks)
}
}
return dst
}
-func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
+func appendObject(dst []byte, obj LogObjectMarshaler, stack bool, ctx context.Context, hooks []Hook) []byte {
+ e := newEvent(LevelWriterAdapter{io.Discard}, DebugLevel, stack, ctx, hooks)
+ e.buf = e.buf[:0] // discard the beginning marker added by newEvent
+ e.appendObject(obj)
+ dst = append(dst, e.buf...)
+ putEvent(e)
+ return dst
+}
+
+func appendFieldList(dst []byte, kvList []interface{}, stack bool, ctx context.Context, hooks []Hook) []byte {
for i, n := 0, len(kvList); i < n; i += 2 {
key, val := kvList[i], kvList[i+1]
if key, ok := key.(string); ok {
@@ -42,14 +58,6 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
} else {
continue
}
- if val, ok := val.(LogObjectMarshaler); ok {
- e := newEvent(nil, 0)
- e.buf = e.buf[:0]
- e.appendObject(val)
- dst = append(dst, e.buf...)
- putEvent(e)
- continue
- }
switch val := val.(type) {
case string:
dst = enc.AppendString(dst, val)
@@ -57,16 +65,12 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
dst = enc.AppendBytes(dst, val)
case error:
switch m := ErrorMarshalFunc(val).(type) {
+ case nil:
+ dst = enc.AppendNil(dst)
case LogObjectMarshaler:
- e := newEvent(nil, 0)
- e.buf = e.buf[:0]
- e.appendObject(m)
- dst = append(dst, e.buf...)
- putEvent(e)
+ dst = appendObject(dst, m, stack, ctx, hooks)
case error:
- if m == nil || isNilValue(m) {
- dst = enc.AppendNil(dst)
- } else {
+ if !isNilValue(m) {
dst = enc.AppendString(dst, m.Error())
}
case string:
@@ -76,16 +80,20 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
}
if stack && ErrorStackMarshaler != nil {
- dst = enc.AppendKey(dst, ErrorStackFieldName)
switch m := ErrorStackMarshaler(val).(type) {
case nil:
+ return dst // do nothing with nil errors
+ case LogObjectMarshaler:
+ dst = enc.AppendKey(dst, ErrorStackFieldName)
+ dst = appendObject(dst, m, stack, ctx, hooks)
case error:
- if m != nil && !isNilValue(m) {
- dst = enc.AppendString(dst, m.Error())
- }
+ dst = enc.AppendKey(dst, ErrorStackFieldName)
+ dst = enc.AppendString(dst, m.Error())
case string:
+ dst = enc.AppendKey(dst, ErrorStackFieldName)
dst = enc.AppendString(dst, m)
default:
+ dst = enc.AppendKey(dst, ErrorStackFieldName)
dst = enc.AppendInterface(dst, m)
}
}
@@ -93,16 +101,12 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
dst = enc.AppendArrayStart(dst)
for i, err := range val {
switch m := ErrorMarshalFunc(err).(type) {
+ case nil:
+ dst = enc.AppendNil(dst)
case LogObjectMarshaler:
- e := newEvent(nil, 0)
- e.buf = e.buf[:0]
- e.appendObject(m)
- dst = append(dst, e.buf...)
- putEvent(e)
+ dst = appendObject(dst, m, stack, ctx, hooks)
case error:
- if m == nil || isNilValue(m) {
- dst = enc.AppendNil(dst)
- } else {
+ if !isNilValue(m) {
dst = enc.AppendString(dst, m.Error())
}
case string:
@@ -112,7 +116,16 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
}
if i < (len(val) - 1) {
- enc.AppendArrayDelim(dst)
+ dst = enc.AppendArrayDelim(dst)
+ }
+ }
+ dst = enc.AppendArrayEnd(dst)
+ case []LogObjectMarshaler:
+ dst = enc.AppendArrayStart(dst)
+ for i, obj := range val {
+ dst = appendObject(dst, obj, stack, ctx, hooks)
+ if i < (len(val) - 1) {
+ dst = enc.AppendArrayDelim(dst)
}
}
dst = enc.AppendArrayEnd(dst)
@@ -145,7 +158,7 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
case time.Time:
dst = enc.AppendTime(dst, val, TimeFieldFormat)
case time.Duration:
- dst = enc.AppendDuration(dst, val, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ dst = enc.AppendDuration(dst, val, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
case *string:
if val != nil {
dst = enc.AppendString(dst, *val)
@@ -238,7 +251,7 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
}
case *time.Duration:
if val != nil {
- dst = enc.AppendDuration(dst, *val, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ dst = enc.AppendDuration(dst, *val, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
} else {
dst = enc.AppendNil(dst)
}
@@ -258,8 +271,7 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
dst = enc.AppendInts64(dst, val)
case []uint:
dst = enc.AppendUints(dst, val)
- // case []uint8:
- // dst = enc.AppendUints8(dst, val)
+ // case []uint8: is handled as []byte above
case []uint16:
dst = enc.AppendUints16(dst, val)
case []uint32:
@@ -273,19 +285,27 @@ func appendFieldList(dst []byte, kvList []interface{}, stack bool) []byte {
case []time.Time:
dst = enc.AppendTimes(dst, val, TimeFieldFormat)
case []time.Duration:
- dst = enc.AppendDurations(dst, val, DurationFieldUnit, DurationFieldInteger, FloatingPointPrecision)
+ dst = enc.AppendDurations(dst, val, DurationFieldUnit, DurationFieldFormat, DurationFieldInteger, FloatingPointPrecision)
case nil:
dst = enc.AppendNil(dst)
case net.IP:
dst = enc.AppendIPAddr(dst, val)
+ case []net.IP:
+ dst = enc.AppendIPAddrs(dst, val)
case net.IPNet:
dst = enc.AppendIPPrefix(dst, val)
+ case []net.IPNet:
+ dst = enc.AppendIPPrefixes(dst, val)
case net.HardwareAddr:
dst = enc.AppendMACAddr(dst, val)
case json.RawMessage:
dst = appendJSON(dst, val)
default:
- dst = enc.AppendInterface(dst, val)
+ if lom, ok := val.(LogObjectMarshaler); ok {
+ dst = appendObject(dst, lom, stack, ctx, hooks)
+ } else {
+ dst = enc.AppendInterface(dst, val)
+ }
}
}
return dst
diff --git a/vendor/github.com/rs/zerolog/globals.go b/vendor/github.com/rs/zerolog/globals.go
index 9a9be713c..d23c127bd 100644
--- a/vendor/github.com/rs/zerolog/globals.go
+++ b/vendor/github.com/rs/zerolog/globals.go
@@ -24,6 +24,16 @@ const (
// TimeFormatUnixNano defines a time format that makes time fields to be
// serialized as Unix timestamp integers in nanoseconds.
TimeFormatUnixNano = "UNIXNANO"
+
+ // DurationFormatFloat defines a format for Duration fields that makes duration fields to be
+ // serialized as floating point numbers.
+ DurationFormatFloat = "float"
+ // DurationFormatInt defines a format for Duration fields that makes duration fields to be
+ // serialized as integers.
+ DurationFormatInt = "int"
+ // DurationFormatString defines a format for Duration fields that makes duration fields to be
+ // serialized as string.
+ DurationFormatString = "string"
)
var (
@@ -107,12 +117,16 @@ var (
// TimestampFunc defines the function called to generate a timestamp.
TimestampFunc = time.Now
+ // DurationFieldFormat defines the format of the Duration field type.
+ DurationFieldFormat = DurationFormatFloat
+
// DurationFieldUnit defines the unit for time.Duration type fields added
// using the Dur method.
DurationFieldUnit = time.Millisecond
// DurationFieldInteger renders Dur fields as integer instead of float if
// set to true.
+ // Deprecated: use DurationFieldFormat with DurationFormatInt instead.
DurationFieldInteger = false
// ErrorHandler is called whenever zerolog fails to write an event on its
@@ -120,6 +134,10 @@ var (
// be thread safe and non-blocking.
ErrorHandler func(err error)
+ // FatalExitFunc is called by log.Fatal() instead of os.Exit(1). If not set,
+ // os.Exit(1) is called.
+ FatalExitFunc func()
+
// DefaultContextLogger is returned from Ctx() if there is no logger associated
// with the context.
DefaultContextLogger *Logger
diff --git a/vendor/github.com/rs/zerolog/globals_118.go b/vendor/github.com/rs/zerolog/globals_118.go
new file mode 100644
index 000000000..968dd07d6
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/globals_118.go
@@ -0,0 +1,30 @@
+//go:build go1.18
+// +build go1.18
+
+package zerolog
+
+import (
+ "fmt"
+)
+
+func AsLogObjectMarshalers[T LogObjectMarshaler](objs []T) []LogObjectMarshaler {
+ if objs == nil {
+ return nil
+ }
+ s := make([]LogObjectMarshaler, len(objs))
+ for i, v := range objs {
+ s[i] = v
+ }
+ return s
+}
+
+func AsStringers[T fmt.Stringer](objs []T) []fmt.Stringer {
+ if objs == nil {
+ return nil
+ }
+ s := make([]fmt.Stringer, len(objs))
+ for i, v := range objs {
+ s[i] = v
+ }
+ return s
+}
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/cbor.go b/vendor/github.com/rs/zerolog/internal/cbor/cbor.go
index 1bf144380..e7509bd6e 100644
--- a/vendor/github.com/rs/zerolog/internal/cbor/cbor.go
+++ b/vendor/github.com/rs/zerolog/internal/cbor/cbor.go
@@ -55,12 +55,12 @@ const (
)
const (
- float32Nan = "\xfa\x7f\xc0\x00\x00"
- float32PosInfinity = "\xfa\x7f\x80\x00\x00"
- float32NegInfinity = "\xfa\xff\x80\x00\x00"
- float64Nan = "\xfb\x7f\xf8\x00\x00\x00\x00\x00\x00"
- float64PosInfinity = "\xfb\x7f\xf0\x00\x00\x00\x00\x00\x00"
- float64NegInfinity = "\xfb\xff\xf0\x00\x00\x00\x00\x00\x00"
+ float32Nan = "\x7f\xc0\x00\x00"
+ float32PosInfinity = "\x7f\x80\x00\x00"
+ float32NegInfinity = "\xff\x80\x00\x00"
+ float64Nan = "\x7f\xf8\x00\x00\x00\x00\x00\x00"
+ float64PosInfinity = "\x7f\xf0\x00\x00\x00\x00\x00\x00"
+ float64NegInfinity = "\xff\xf0\x00\x00\x00\x00\x00\x00"
)
// IntegerTimeFieldFormat indicates the format of timestamp decoded
@@ -72,7 +72,7 @@ var IntegerTimeFieldFormat = time.RFC3339
var NanoTimeFieldFormat = time.RFC3339Nano
func appendCborTypePrefix(dst []byte, major byte, number uint64) []byte {
- byteCount := 8
+ var byteCount int
var minor byte
switch {
case number < 256:
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go b/vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go
index 5633e6665..f21c24b5a 100644
--- a/vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go
+++ b/vendor/github.com/rs/zerolog/internal/cbor/decode_stream.go
@@ -490,7 +490,7 @@ func decodeTimeStamp(src *bufio.Reader) []byte {
tsb = append(tsb, '"')
return tsb
}
- panic(fmt.Errorf("TS format is neigther int nor float: %d", tsMajor))
+ panic(fmt.Errorf("TS format is neither int nor float: %d", tsMajor))
}
func decodeSimpleFloat(src *bufio.Reader) []byte {
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/string.go b/vendor/github.com/rs/zerolog/internal/cbor/string.go
index 9fc9a4f8e..c56e4fed1 100644
--- a/vendor/github.com/rs/zerolog/internal/cbor/string.go
+++ b/vendor/github.com/rs/zerolog/internal/cbor/string.go
@@ -35,7 +35,7 @@ func (Encoder) AppendString(dst []byte, s string) []byte {
// AppendStringers encodes and adds an array of Stringer values
// to the dst byte array.
func (e Encoder) AppendStringers(dst []byte, vals []fmt.Stringer) []byte {
- if len(vals) == 0 {
+ if vals == nil || len(vals) == 0 {
return e.AppendArrayEnd(e.AppendArrayStart(dst))
}
dst = e.AppendArrayStart(dst)
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/time.go b/vendor/github.com/rs/zerolog/internal/cbor/time.go
index 7c0eccee1..818f93023 100644
--- a/vendor/github.com/rs/zerolog/internal/cbor/time.go
+++ b/vendor/github.com/rs/zerolog/internal/cbor/time.go
@@ -4,6 +4,17 @@ import (
"time"
)
+const (
+ // Import from zerolog/global.go
+ timeFormatUnix = ""
+ timeFormatUnixMs = "UNIXMS"
+ timeFormatUnixMicro = "UNIXMICRO"
+ timeFormatUnixNano = "UNIXNANO"
+ durationFormatFloat = "float"
+ durationFormatInt = "int"
+ durationFormatString = "string"
+)
+
func appendIntegerTimestamp(dst []byte, t time.Time) []byte {
major := majorTypeTags
minor := additionalTypeTimestamp
@@ -27,8 +38,7 @@ func (e Encoder) appendFloatTimestamp(dst []byte, t time.Time) []byte {
dst = append(dst, major|minor)
secs := t.Unix()
nanos := t.Nanosecond()
- var val float64
- val = float64(secs)*1.0 + float64(nanos)*1e-9
+ val := float64(secs)*1.0 + float64(nanos)*1e-9
return e.AppendFloat64(dst, val, -1)
}
@@ -64,17 +74,25 @@ func (e Encoder) AppendTimes(dst []byte, vals []time.Time, unused string) []byte
// AppendDuration encodes and adds a duration to the dst byte array.
// useInt field indicates whether to store the duration as seconds (integer) or
// as seconds+nanoseconds (float).
-func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool, unused int) []byte {
+func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, format string, useInt bool, unused int) []byte {
if useInt {
return e.AppendInt64(dst, int64(d/unit))
}
+ switch format {
+ case durationFormatFloat:
+ return e.AppendFloat64(dst, float64(d)/float64(unit), unused)
+ case durationFormatInt:
+ return e.AppendInt64(dst, int64(d/unit))
+ case durationFormatString:
+ return e.AppendString(dst, d.String())
+ }
return e.AppendFloat64(dst, float64(d)/float64(unit), unused)
}
// AppendDurations encodes and adds an array of durations to the dst byte array.
// useInt field indicates whether to store the duration as seconds (integer) or
// as seconds+nanoseconds (float).
-func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool, unused int) []byte {
+func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, format string, useInt bool, unused int) []byte {
major := majorTypeArray
l := len(vals)
if l == 0 {
@@ -87,7 +105,7 @@ func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Dur
dst = appendCborTypePrefix(dst, major, uint64(l))
}
for _, d := range vals {
- dst = e.AppendDuration(dst, d, unit, useInt, unused)
+ dst = e.AppendDuration(dst, d, unit, format, useInt, unused)
}
return dst
}
diff --git a/vendor/github.com/rs/zerolog/internal/cbor/types.go b/vendor/github.com/rs/zerolog/internal/cbor/types.go
index 454d68bd9..892228f16 100644
--- a/vendor/github.com/rs/zerolog/internal/cbor/types.go
+++ b/vendor/github.com/rs/zerolog/internal/cbor/types.go
@@ -447,7 +447,7 @@ func (e Encoder) AppendType(dst []byte, i interface{}) []byte {
return e.AppendString(dst, reflect.TypeOf(i).String())
}
-// AppendIPAddr encodes and inserts an IP Address (IPv4 or IPv6).
+// AppendIPAddr adds a net.IP IPv4 or IPv6 address into the dst byte array.
func (e Encoder) AppendIPAddr(dst []byte, ip net.IP) []byte {
dst = append(dst, majorTypeTags|additionalTypeIntUint16)
dst = append(dst, byte(additionalTypeTagNetworkAddr>>8))
@@ -455,7 +455,26 @@ func (e Encoder) AppendIPAddr(dst []byte, ip net.IP) []byte {
return e.AppendBytes(dst, ip)
}
-// AppendIPPrefix encodes and inserts an IP Address Prefix (Address + Mask Length).
+// AppendIPAddrs adds a []net.IP array of IPv4 or IPv6 address into the dst byte array.
+func (e Encoder) AppendIPAddrs(dst []byte, ips []net.IP) []byte {
+ major := majorTypeArray
+ l := len(ips)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range ips {
+ dst = e.AppendIPAddr(dst, v)
+ }
+ return dst
+}
+
+// AppendIPPrefix adds a net.IPNet IPv4 or IPv6 Prefix (address & mask) into the dst byte array.
func (e Encoder) AppendIPPrefix(dst []byte, pfx net.IPNet) []byte {
dst = append(dst, majorTypeTags|additionalTypeIntUint16)
dst = append(dst, byte(additionalTypeTagNetworkPrefix>>8))
@@ -469,6 +488,25 @@ func (e Encoder) AppendIPPrefix(dst []byte, pfx net.IPNet) []byte {
return e.AppendUint8(dst, uint8(maskLen))
}
+// AppendIPPrefixes adds a []net.IPNet array of IPv4 or IPv6 Prefix (address & mask) into the dst byte array.
+func (e Encoder) AppendIPPrefixes(dst []byte, pfxs []net.IPNet) []byte {
+ major := majorTypeArray
+ l := len(pfxs)
+ if l == 0 {
+ return e.AppendArrayEnd(e.AppendArrayStart(dst))
+ }
+ if l <= additionalMax {
+ lb := byte(l)
+ dst = append(dst, major|lb)
+ } else {
+ dst = appendCborTypePrefix(dst, major, uint64(l))
+ }
+ for _, v := range pfxs {
+ dst = e.AppendIPPrefix(dst, v)
+ }
+ return dst
+}
+
// AppendMACAddr encodes and inserts a Hardware (MAC) address.
func (e Encoder) AppendMACAddr(dst []byte, ha net.HardwareAddr) []byte {
dst = append(dst, majorTypeTags|additionalTypeIntUint16)
diff --git a/vendor/github.com/rs/zerolog/internal/json/bytes.go b/vendor/github.com/rs/zerolog/internal/json/bytes.go
index de64120d1..2c624ebeb 100644
--- a/vendor/github.com/rs/zerolog/internal/json/bytes.go
+++ b/vendor/github.com/rs/zerolog/internal/json/bytes.go
@@ -23,7 +23,7 @@ func (Encoder) AppendBytes(dst, s []byte) []byte {
func (Encoder) AppendHex(dst, s []byte) []byte {
dst = append(dst, '"')
for _, v := range s {
- dst = append(dst, hex[v>>4], hex[v&0x0f])
+ dst = append(dst, hexCharacters[v>>4], hexCharacters[v&0x0f])
}
return append(dst, '"')
}
@@ -73,7 +73,7 @@ func appendBytesComplex(dst, s []byte, i int) []byte {
case '\t':
dst = append(dst, '\\', 't')
default:
- dst = append(dst, '\\', 'u', '0', '0', hex[b>>4], hex[b&0xF])
+ dst = append(dst, '\\', 'u', '0', '0', hexCharacters[b>>4], hexCharacters[b&0xF])
}
i++
start = i
diff --git a/vendor/github.com/rs/zerolog/internal/json/string.go b/vendor/github.com/rs/zerolog/internal/json/string.go
index fd7770f2f..215a8262c 100644
--- a/vendor/github.com/rs/zerolog/internal/json/string.go
+++ b/vendor/github.com/rs/zerolog/internal/json/string.go
@@ -5,7 +5,7 @@ import (
"unicode/utf8"
)
-const hex = "0123456789abcdef"
+const hexCharacters = "0123456789abcdef"
var noEscapeTable = [256]bool{}
@@ -66,7 +66,7 @@ func (Encoder) AppendString(dst []byte, s string) []byte {
// AppendStringers encodes the provided Stringer list to json and
// appends the encoded Stringer list to the input byte slice.
func (e Encoder) AppendStringers(dst []byte, vals []fmt.Stringer) []byte {
- if len(vals) == 0 {
+ if vals == nil || len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
@@ -88,7 +88,7 @@ func (e Encoder) AppendStringer(dst []byte, val fmt.Stringer) []byte {
return e.AppendString(dst, val.String())
}
-//// appendStringComplex is used by appendString to take over an in
+// appendStringComplex is used by appendString to take over an in
// progress JSON string encoding that encountered a character that needs
// to be encoded.
func appendStringComplex(dst []byte, s string, i int) []byte {
@@ -137,7 +137,7 @@ func appendStringComplex(dst []byte, s string, i int) []byte {
case '\t':
dst = append(dst, '\\', 't')
default:
- dst = append(dst, '\\', 'u', '0', '0', hex[b>>4], hex[b&0xF])
+ dst = append(dst, '\\', 'u', '0', '0', hexCharacters[b>>4], hexCharacters[b&0xF])
}
i++
start = i
diff --git a/vendor/github.com/rs/zerolog/internal/json/time.go b/vendor/github.com/rs/zerolog/internal/json/time.go
index 08cbbd9b6..2783d1079 100644
--- a/vendor/github.com/rs/zerolog/internal/json/time.go
+++ b/vendor/github.com/rs/zerolog/internal/json/time.go
@@ -7,10 +7,13 @@ import (
const (
// Import from zerolog/global.go
- timeFormatUnix = ""
- timeFormatUnixMs = "UNIXMS"
- timeFormatUnixMicro = "UNIXMICRO"
- timeFormatUnixNano = "UNIXNANO"
+ timeFormatUnix = ""
+ timeFormatUnixMs = "UNIXMS"
+ timeFormatUnixMicro = "UNIXMICRO"
+ timeFormatUnixNano = "UNIXNANO"
+ durationFormatFloat = "float"
+ durationFormatInt = "int"
+ durationFormatString = "string"
)
// AppendTime formats the input time with the given format
@@ -88,24 +91,32 @@ func appendUnixNanoTimes(dst []byte, vals []time.Time, div int64) []byte {
// AppendDuration formats the input duration with the given unit & format
// and appends the encoded string to the input byte slice.
-func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, useInt bool, precision int) []byte {
+func (e Encoder) AppendDuration(dst []byte, d time.Duration, unit time.Duration, format string, useInt bool, precision int) []byte {
if useInt {
return strconv.AppendInt(dst, int64(d/unit), 10)
}
+ switch format {
+ case durationFormatFloat:
+ return e.AppendFloat64(dst, float64(d)/float64(unit), precision)
+ case durationFormatInt:
+ return e.AppendInt64(dst, int64(d/unit))
+ case durationFormatString:
+ return e.AppendString(dst, d.String())
+ }
return e.AppendFloat64(dst, float64(d)/float64(unit), precision)
}
// AppendDurations formats the input durations with the given unit & format
// and appends the encoded string list to the input byte slice.
-func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, useInt bool, precision int) []byte {
+func (e Encoder) AppendDurations(dst []byte, vals []time.Duration, unit time.Duration, format string, useInt bool, precision int) []byte {
if len(vals) == 0 {
return append(dst, '[', ']')
}
dst = append(dst, '[')
- dst = e.AppendDuration(dst, vals[0], unit, useInt, precision)
+ dst = e.AppendDuration(dst, vals[0], unit, format, useInt, precision)
if len(vals) > 1 {
for _, d := range vals[1:] {
- dst = e.AppendDuration(append(dst, ','), d, unit, useInt, precision)
+ dst = e.AppendDuration(append(dst, ','), d, unit, format, useInt, precision)
}
}
dst = append(dst, ']')
diff --git a/vendor/github.com/rs/zerolog/internal/json/types.go b/vendor/github.com/rs/zerolog/internal/json/types.go
index 7930491a0..7be111aec 100644
--- a/vendor/github.com/rs/zerolog/internal/json/types.go
+++ b/vendor/github.com/rs/zerolog/internal/json/types.go
@@ -418,18 +418,49 @@ func (Encoder) AppendObjectData(dst []byte, o []byte) []byte {
return append(dst, o...)
}
-// AppendIPAddr adds IPv4 or IPv6 address to dst.
+// AppendIPAddr adds a net.IP IPv4 or IPv6 address to dst.
func (e Encoder) AppendIPAddr(dst []byte, ip net.IP) []byte {
return e.AppendString(dst, ip.String())
}
-// AppendIPPrefix adds IPv4 or IPv6 Prefix (address & mask) to dst.
-func (e Encoder) AppendIPPrefix(dst []byte, pfx net.IPNet) []byte {
- return e.AppendString(dst, pfx.String())
-
+// AppendIPAddrs adds a []net.IP array of IPv4 or IPv6 address to dst.
+func (e Encoder) AppendIPAddrs(dst []byte, ips []net.IP) []byte {
+ if len(ips) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = e.AppendString(dst, ips[0].String())
+ if len(ips) > 1 {
+ for _, ip := range ips[1:] {
+ dst = e.AppendString(append(dst, ','), ip.String())
+ }
+ }
+ dst = append(dst, ']')
+ return dst
}
-// AppendMACAddr adds MAC address to dst.
+// AppendIPPrefix adds a net.IPNet IPv4 or IPv6 Prefix (address & mask) to dst.
+func (e Encoder) AppendIPPrefix(dst []byte, pfx net.IPNet) []byte {
+ return e.AppendString(dst, pfx.String())
+}
+
+// AppendIPPrefixes adds a []net.IPNet array of IPv4 or IPv6 Prefix (address & mask) to dst.
+func (e Encoder) AppendIPPrefixes(dst []byte, pfxs []net.IPNet) []byte {
+ if len(pfxs) == 0 {
+ return append(dst, '[', ']')
+ }
+ dst = append(dst, '[')
+ dst = e.AppendString(dst, pfxs[0].String())
+ if len(pfxs) > 1 {
+ for _, pfx := range pfxs[1:] {
+ dst = e.AppendString(append(dst, ','), pfx.String())
+ }
+ }
+ dst = append(dst, ']')
+ return dst
+}
+
+// AppendMACAddr adds a net.HardwareAddr MAC address to dst.
func (e Encoder) AppendMACAddr(dst []byte, ha net.HardwareAddr) []byte {
return e.AppendString(dst, ha.String())
}
diff --git a/vendor/github.com/rs/zerolog/log.go b/vendor/github.com/rs/zerolog/log.go
index ea2cd62b5..f9ba5417a 100644
--- a/vendor/github.com/rs/zerolog/log.go
+++ b/vendor/github.com/rs/zerolog/log.go
@@ -2,85 +2,85 @@
//
// A global Logger can be use for simple logging:
//
-// import "github.com/rs/zerolog/log"
+// import "github.com/rs/zerolog/log"
//
-// log.Info().Msg("hello world")
-// // Output: {"time":1494567715,"level":"info","message":"hello world"}
+// log.Info().Msg("hello world")
+// // Output: {"time":1494567715,"level":"info","message":"hello world"}
//
// NOTE: To import the global logger, import the "log" subpackage "github.com/rs/zerolog/log".
//
// Fields can be added to log messages:
//
-// log.Info().Str("foo", "bar").Msg("hello world")
-// // Output: {"time":1494567715,"level":"info","message":"hello world","foo":"bar"}
+// log.Info().Str("foo", "bar").Msg("hello world")
+// // Output: {"time":1494567715,"level":"info","message":"hello world","foo":"bar"}
//
// Create logger instance to manage different outputs:
//
-// logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
-// logger.Info().
-// Str("foo", "bar").
-// Msg("hello world")
-// // Output: {"time":1494567715,"level":"info","message":"hello world","foo":"bar"}
+// logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
+// logger.Info().
+// Str("foo", "bar").
+// Msg("hello world")
+// // Output: {"time":1494567715,"level":"info","message":"hello world","foo":"bar"}
//
// Sub-loggers let you chain loggers with additional context:
//
-// sublogger := log.With().Str("component", "foo").Logger()
-// sublogger.Info().Msg("hello world")
-// // Output: {"time":1494567715,"level":"info","message":"hello world","component":"foo"}
+// sublogger := log.With().Str("component", "foo").Logger()
+// sublogger.Info().Msg("hello world")
+// // Output: {"time":1494567715,"level":"info","message":"hello world","component":"foo"}
//
// Level logging
//
-// zerolog.SetGlobalLevel(zerolog.InfoLevel)
+// zerolog.SetGlobalLevel(zerolog.InfoLevel)
//
-// log.Debug().Msg("filtered out message")
-// log.Info().Msg("routed message")
+// log.Debug().Msg("filtered out message")
+// log.Info().Msg("routed message")
//
-// if e := log.Debug(); e.Enabled() {
-// // Compute log output only if enabled.
-// value := compute()
-// e.Str("foo": value).Msg("some debug message")
-// }
-// // Output: {"level":"info","time":1494567715,"routed message"}
+// if e := log.Debug(); e.Enabled() {
+// // Compute log output only if enabled.
+// value := compute()
+// e.Str("foo": value).Msg("some debug message")
+// }
+// // Output: {"level":"info","time":1494567715,"routed message"}
//
// Customize automatic field names:
//
-// log.TimestampFieldName = "t"
-// log.LevelFieldName = "p"
-// log.MessageFieldName = "m"
+// log.TimestampFieldName = "t"
+// log.LevelFieldName = "p"
+// log.MessageFieldName = "m"
//
-// log.Info().Msg("hello world")
-// // Output: {"t":1494567715,"p":"info","m":"hello world"}
+// log.Info().Msg("hello world")
+// // Output: {"t":1494567715,"p":"info","m":"hello world"}
//
// Log with no level and message:
//
-// log.Log().Str("foo","bar").Msg("")
-// // Output: {"time":1494567715,"foo":"bar"}
+// log.Log().Str("foo","bar").Msg("")
+// // Output: {"time":1494567715,"foo":"bar"}
//
// Add contextual fields to global Logger:
//
-// log.Logger = log.With().Str("foo", "bar").Logger()
+// log.Logger = log.With().Str("foo", "bar").Logger()
//
// Sample logs:
//
-// sampled := log.Sample(&zerolog.BasicSampler{N: 10})
-// sampled.Info().Msg("will be logged every 10 messages")
+// sampled := log.Sample(&zerolog.BasicSampler{N: 10})
+// sampled.Info().Msg("will be logged every 10 messages")
//
// Log with contextual hooks:
//
-// // Create the hook:
-// type SeverityHook struct{}
+// // Create the hook:
+// type SeverityHook struct{}
//
-// func (h SeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
-// if level != zerolog.NoLevel {
-// e.Str("severity", level.String())
-// }
-// }
+// func (h SeverityHook) Run(e *zerolog.Event, level zerolog.Level, msg string) {
+// if level != zerolog.NoLevel {
+// e.Str("severity", level.String())
+// }
+// }
//
-// // And use it:
-// var h SeverityHook
-// log := zerolog.New(os.Stdout).Hook(h)
-// log.Warn().Msg("")
-// // Output: {"level":"warn","severity":"warn"}
+// // And use it:
+// var h SeverityHook
+// log := zerolog.New(os.Stdout).Hook(h)
+// log.Warn().Msg("")
+// // Output: {"level":"warn","severity":"warn"}
//
// # Caveats
//
@@ -89,11 +89,11 @@
// There is no fields deduplication out-of-the-box.
// Using the same key multiple times creates new key in final JSON each time.
//
-// logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
-// logger.Info().
-// Timestamp().
-// Msg("dup")
-// // Output: {"level":"info","time":1494567715,"time":1494567715,"message":"dup"}
+// logger := zerolog.New(os.Stderr).With().Timestamp().Logger()
+// logger.Info().
+// Timestamp().
+// Msg("dup")
+// // Output: {"level":"info","time":1494567715,"time":1494567715,"message":"dup"}
//
// In this case, many consumers will take the last value,
// but this is not guaranteed; check yours if in doubt.
@@ -102,15 +102,15 @@
//
// Be careful when calling UpdateContext. It is not concurrency safe. Use the With method to create a child logger:
//
-// func handler(w http.ResponseWriter, r *http.Request) {
-// // Create a child logger for concurrency safety
-// logger := log.Logger.With().Logger()
+// func handler(w http.ResponseWriter, r *http.Request) {
+// // Create a child logger for concurrency safety
+// logger := log.Logger.With().Logger()
//
-// // Add context fields, for example User-Agent from HTTP headers
-// logger.UpdateContext(func(c zerolog.Context) zerolog.Context {
-// ...
-// })
-// }
+// // Add context fields, for example User-Agent from HTTP headers
+// logger.UpdateContext(func(c zerolog.Context) zerolog.Context {
+// ...
+// })
+// }
package zerolog
import (
@@ -294,7 +294,7 @@ func (l Logger) With() Context {
// Caution: This method is not concurrency safe.
// Use the With method to create a child logger before modifying the context from concurrent goroutines.
func (l *Logger) UpdateContext(update func(c Context) Context) {
- if l == disabledLogger {
+ if l.disabled() {
return
}
if cap(l.context) == 0 {
@@ -382,18 +382,24 @@ func (l *Logger) Err(err error) *Event {
return l.Info()
}
-// Fatal starts a new message with fatal level. The os.Exit(1) function
-// is called by the Msg method, which terminates the program immediately.
+// Fatal starts a new message with fatal level. The FatalExitFunc interceptor function
+// is called by the Msg method, which by default terminates the program immediately
+// using os.Exit(1), any desired behavior can be implemented by setting FatalExitFunc.
//
// You must call Msg on the returned event in order to send the event.
func (l *Logger) Fatal() *Event {
return l.newEvent(FatalLevel, func(msg string) {
if closer, ok := l.w.(io.Closer); ok {
// Close the writer to flush any buffered message. Otherwise the message
- // will be lost as os.Exit() terminates the program immediately.
+ // could be lost if FatalExitFunc() terminates the program immediately or
+ // os.Exit(1) is called if not FatalExitFunc isn't set (default).
closer.Close()
}
- os.Exit(1)
+ if FatalExitFunc != nil {
+ FatalExitFunc()
+ } else {
+ os.Exit(1) // untestable: terminates the program, cannot be covered
+ }
})
}
@@ -487,25 +493,29 @@ func (l *Logger) newEvent(level Level, done func(string)) *Event {
}
return nil
}
- e := newEvent(l.w, level)
+ e := newEvent(l.w, level, l.stack, l.ctx, l.hooks)
e.done = done
- e.ch = l.hooks
- e.ctx = l.ctx
if level != NoLevel && LevelFieldName != "" {
e.Str(LevelFieldName, LevelFieldMarshalFunc(level))
}
if len(l.context) > 1 {
e.buf = enc.AppendObjectData(e.buf, l.context)
}
- if l.stack {
- e.Stack()
- }
return e
}
+func (l *Logger) scratchEvent() *Event {
+ return newEvent(LevelWriterAdapter{io.Discard}, DebugLevel, l.stack, l.ctx, l.hooks)
+}
+
+// disabled returns true if the logger is a disabled or nop logger.
+func (l *Logger) disabled() bool {
+ return l.w == nil || l.level == Disabled
+}
+
// should returns true if the log event should be logged.
func (l *Logger) should(lvl Level) bool {
- if l.w == nil {
+ if l.disabled() {
return false
}
if lvl < l.level || lvl < GlobalLevel() {
diff --git a/vendor/github.com/rs/zerolog/slog.go b/vendor/github.com/rs/zerolog/slog.go
new file mode 100644
index 000000000..eead9cced
--- /dev/null
+++ b/vendor/github.com/rs/zerolog/slog.go
@@ -0,0 +1,247 @@
+package zerolog
+
+import (
+ "context"
+ "log/slog"
+ "time"
+)
+
+// SlogHandler implements the slog.Handler interface using a zerolog.Logger
+// as the underlying log backend. This allows code that uses the standard
+// library's slog package to route log output through zerolog.
+type SlogHandler struct {
+ logger Logger
+ prefix string // group prefix for nested groups
+ attrs []slog.Attr
+}
+
+// NewSlogHandler creates a new slog.Handler that writes log records to the
+// given zerolog.Logger. The handler maps slog levels to zerolog levels and
+// converts slog attributes to zerolog fields.
+func NewSlogHandler(logger Logger) *SlogHandler {
+ return &SlogHandler{logger: logger}
+}
+
+// Enabled reports whether the handler handles records at the given level.
+// It mirrors Logger.should's level and writer checks (without sampling).
+func (h *SlogHandler) Enabled(_ context.Context, level slog.Level) bool {
+ if h.logger.w == nil {
+ return false
+ }
+ zl := slogToZerologLevel(level)
+ if zl < GlobalLevel() {
+ return false
+ }
+ return zl >= h.logger.level
+}
+
+// Handle handles the Record. It converts the slog.Record into a zerolog event
+// and writes it using the underlying zerolog.Logger.
+func (h *SlogHandler) Handle(ctx context.Context, record slog.Record) error {
+ zlevel := slogToZerologLevel(record.Level)
+ event := h.logger.WithLevel(zlevel)
+ if event == nil {
+ return nil
+ }
+
+ // Propagate slog context to the zerolog event so that hooks
+ // relying on Event.GetCtx() (e.g. tracing) can access it.
+ if ctx != nil {
+ event = event.Ctx(ctx)
+ }
+
+ // Add pre-attached attrs from WithAttrs
+ for _, a := range h.attrs {
+ event = appendSlogAttr(event, a, h.prefix)
+ }
+
+ // Add attrs from the record itself
+ record.Attrs(func(a slog.Attr) bool {
+ event = appendSlogAttr(event, a, h.prefix)
+ return true
+ })
+
+ // Add timestamp from the slog record, but only if the logger doesn't
+ // already have a timestampHook (added via .With().Timestamp()) to
+ // avoid duplicate timestamp keys in the output.
+ if !record.Time.IsZero() && !h.hasTimestampHook() {
+ event.Time(TimestampFieldName, record.Time)
+ }
+
+ event.Msg(record.Message)
+ return nil
+}
+
+// hasTimestampHook reports whether the logger has a timestampHook installed,
+// which would cause duplicate timestamp fields if we also emit record.Time.
+func (h *SlogHandler) hasTimestampHook() bool {
+ for _, hook := range h.logger.hooks {
+ if _, ok := hook.(timestampHook); ok {
+ return true
+ }
+ }
+ return false
+}
+
+// WithAttrs returns a new Handler with the given attributes pre-attached.
+// These attributes will be included in every subsequent log record.
+func (h *SlogHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
+ if len(attrs) == 0 {
+ return h
+ }
+ h2 := h.clone()
+ h2.attrs = append(h2.attrs, attrs...)
+ return h2
+}
+
+// WithGroup returns a new Handler with the given group name. All subsequent
+// attributes will be nested under this group name in the output.
+func (h *SlogHandler) WithGroup(name string) slog.Handler {
+ if name == "" {
+ return h
+ }
+ h2 := h.clone()
+ if h2.prefix != "" {
+ h2.prefix = h2.prefix + "." + name
+ } else {
+ h2.prefix = name
+ }
+ return h2
+}
+
+func (h *SlogHandler) clone() *SlogHandler {
+ h2 := &SlogHandler{
+ logger: h.logger,
+ prefix: h.prefix,
+ }
+ if len(h.attrs) > 0 {
+ h2.attrs = make([]slog.Attr, len(h.attrs))
+ copy(h2.attrs, h.attrs)
+ }
+ return h2
+}
+
+// slogToZerologLevel maps slog levels to zerolog levels.
+//
+// slog levels: Debug=-4, Info=0, Warn=4, Error=8
+// zerolog levels: Trace=-1, Debug=0, Info=1, Warn=2, Error=3, Fatal=4, Panic=5
+func slogToZerologLevel(level slog.Level) Level {
+ switch {
+ case level < slog.LevelDebug:
+ return TraceLevel
+ case level < slog.LevelInfo:
+ return DebugLevel
+ case level < slog.LevelWarn:
+ return InfoLevel
+ case level < slog.LevelError:
+ return WarnLevel
+ default:
+ return ErrorLevel
+ }
+}
+
+// zerologToSlogLevel maps zerolog levels to slog levels.
+func zerologToSlogLevel(level Level) slog.Level {
+ switch level {
+ case TraceLevel:
+ return slog.LevelDebug - 4
+ case DebugLevel:
+ return slog.LevelDebug
+ case InfoLevel:
+ return slog.LevelInfo
+ case WarnLevel:
+ return slog.LevelWarn
+ case ErrorLevel:
+ return slog.LevelError
+ case FatalLevel:
+ return slog.LevelError + 4
+ case PanicLevel:
+ return slog.LevelError + 8
+ default:
+ return slog.LevelInfo
+ }
+}
+
+// joinPrefix concatenates a prefix and key with a dot separator.
+// It avoids allocations when either prefix or key is empty.
+func joinPrefix(prefix, key string) string {
+ if prefix == "" {
+ return key
+ }
+ if key == "" {
+ return prefix
+ }
+ return prefix + "." + key
+}
+
+// appendSlogAttr appends a single slog.Attr to the zerolog event, handling
+// type-specific encoding to avoid reflection where possible.
+func appendSlogAttr(event *Event, attr slog.Attr, prefix string) *Event {
+ if event == nil {
+ return event
+ }
+
+ // Resolve the attribute to handle LogValuer types.
+ // This handles slog.KindLogValuer implicitly by unwrapping
+ // any values that implement slog.LogValuer to their resolved form.
+ attr.Value = attr.Value.Resolve()
+
+ // For group kinds, handle grouping before key concatenation
+ if attr.Value.Kind() == slog.KindGroup {
+ attrs := attr.Value.Group()
+ if len(attrs) == 0 {
+ return event
+ }
+ groupPrefix := joinPrefix(prefix, attr.Key)
+ for _, ga := range attrs {
+ event = appendSlogAttr(event, ga, groupPrefix)
+ }
+ return event
+ }
+
+ // Skip empty keys for non-group attributes
+ if attr.Key == "" {
+ return event
+ }
+
+ key := joinPrefix(prefix, attr.Key)
+ val := attr.Value
+
+ switch val.Kind() {
+ case slog.KindString:
+ event = event.Str(key, val.String())
+ case slog.KindInt64:
+ event = event.Int64(key, val.Int64())
+ case slog.KindUint64:
+ event = event.Uint64(key, val.Uint64())
+ case slog.KindFloat64:
+ event = event.Float64(key, val.Float64())
+ case slog.KindBool:
+ event = event.Bool(key, val.Bool())
+ case slog.KindDuration:
+ event = event.Dur(key, val.Duration())
+ case slog.KindTime:
+ event = event.Time(key, val.Time())
+ case slog.KindAny:
+ v := val.Any()
+ switch cv := v.(type) {
+ case error:
+ event = event.AnErr(key, cv)
+ case time.Duration:
+ event = event.Dur(key, cv)
+ case time.Time:
+ event = event.Time(key, cv)
+ case []byte:
+ event = event.Bytes(key, cv)
+ default:
+ event = event.Interface(key, v)
+ }
+ default:
+ event = event.Interface(key, val.Any())
+ }
+
+ return event
+}
+
+// Verify at compile time that SlogHandler satisfies the slog.Handler interface.
+var _ slog.Handler = (*SlogHandler)(nil)
diff --git a/vendor/github.com/samber/lo/.golangci.yml b/vendor/github.com/samber/lo/.golangci.yml
index 2a71979cb..2f6c810ff 100644
--- a/vendor/github.com/samber/lo/.golangci.yml
+++ b/vendor/github.com/samber/lo/.golangci.yml
@@ -40,7 +40,7 @@ linters:
# disable noisy/controversial ones which you might enable later
disable:
- - lll # line length — handled by gfmt/gofumpt
+ - lll # line length — handled by gofmt/gofumpt
settings:
dupl:
diff --git a/vendor/github.com/samber/lo/Makefile b/vendor/github.com/samber/lo/Makefile
index 5a6591d16..ad9267ba6 100644
--- a/vendor/github.com/samber/lo/Makefile
+++ b/vendor/github.com/samber/lo/Makefile
@@ -1,9 +1,14 @@
+# Only build/test/lint exp/simd when Go version is >= 1.26 (requires goexperiment.simd)
+GO_VERSION := $(shell go version 2>/dev/null | sed -n 's/.*go\([0-9]*\)\.\([0-9]*\).*/\1.\2/p')
+GO_SIMD_SUPPORT := $(shell ver="$(GO_VERSION)"; [ -n "$$ver" ] && [ "$$(printf '%s\n1.26\n' "$$ver" | sort -V | tail -1)" = "$$ver" ] && echo yes)
build:
go build -v ./...
+ @if [ -n "$(GO_SIMD_SUPPORT)" ]; then cd ./exp/simd && GOEXPERIMENT=simd go build -v ./; fi
test:
go test -race ./...
+ @if [ -n "$(GO_SIMD_SUPPORT)" ]; then cd ./exp/simd && GOEXPERIMENT=simd go test -race ./; fi
watch-test:
reflex -t 50ms -s -- sh -c 'gotest -race ./...'
@@ -32,9 +37,11 @@ tools:
lint:
golangci-lint run --timeout 60s --max-same-issues 50 ./...
+ @if [ -n "$(GO_SIMD_SUPPORT)" ]; then cd ./exp/simd && golangci-lint run --timeout 60s --max-same-issues 50 ./; fi
# mdsf verify --debug --log-level warn docs/
lint-fix:
golangci-lint run --timeout 60s --max-same-issues 50 --fix ./...
+ @if [ -n "$(GO_SIMD_SUPPORT)" ]; then cd ./exp/simd && golangci-lint run --timeout 60s --max-same-issues 50 --fix ./; fi
# mdsf format --debug --log-level warn docs/
audit:
diff --git a/vendor/github.com/samber/lo/README.md b/vendor/github.com/samber/lo/README.md
index 7a4ee33f3..c9ad48344 100644
--- a/vendor/github.com/samber/lo/README.md
+++ b/vendor/github.com/samber/lo/README.md
@@ -18,18 +18,28 @@ A utility library based on Go 1.18+ generics that makes it easier to work with s
**See also:**
+- [samber/ro](https://github.com/samber/ro): Reactive Programming for Go: declarative and composable API for event-driven applications
- [samber/do](https://github.com/samber/do): A dependency injection toolkit based on Go 1.18+ Generics
- [samber/mo](https://github.com/samber/mo): Monads based on Go 1.18+ Generics (Option, Result, Either...)
+What makes it different from **samber/ro**?
+- lo: synchronous helpers across finite sequences (maps, slices...)
+- ro: processing of infinite data streams for event-driven scenarios
+
----
-
💖 Support This Project
-
-
- I’m going all-in on open-source for the coming months.
-
- Help sustain development: Become an individual sponsor or join as a corporate sponsor.
-
+
----
@@ -37,7 +47,7 @@ A utility library based on Go 1.18+ generics that makes it easier to work with s
I wanted a **short name**, similar to "Lodash", and no Go package uses this name.
-
+
## 🚀 Install
@@ -47,7 +57,7 @@ go get github.com/samber/lo@v1
This library is v1 and follows SemVer strictly.
-No breaking changes will be made to exported APIs before v2.0.0.
+No breaking changes will be made to exported APIs before v2.0.0, except for experimental packages under `exp/`.
This library has no dependencies outside the Go standard library.
@@ -106,8 +116,11 @@ Supported helpers for slices:
- [GroupBy](#groupby)
- [GroupByMap](#groupbymap)
- [Chunk](#chunk)
+- [Window](#window)
+- [Sliding](#sliding)
- [PartitionBy](#partitionby)
- [Flatten](#flatten)
+- [Concat](#concat)
- [Interleave](#interleave)
- [Shuffle](#shuffle)
- [Reverse](#reverse)
@@ -118,6 +131,9 @@ Supported helpers for slices:
- [SliceToMap / Associate](#slicetomap-alias-associate)
- [FilterSliceToMap](#filterslicetomap)
- [Keyify](#keyify)
+- [Take](#take)
+- [TakeWhile](#takewhile)
+- [TakeFilter](#takefilter)
- [Drop](#drop)
- [DropRight](#dropright)
- [DropWhile](#dropwhile)
@@ -134,9 +150,10 @@ Supported helpers for slices:
- [Slice](#slice)
- [Replace](#replace)
- [ReplaceAll](#replaceall)
+- [Clone](#clone)
- [Compact](#compact)
- [IsSorted](#issorted)
-- [IsSortedByKey](#issortedbykey)
+- [IsSortedBy](#issortedby)
- [Splice](#Splice)
- [Cut](#Cut)
- [CutPrefix](#CutPrefix)
@@ -239,6 +256,7 @@ Supported intersection helpers:
- [None](#none)
- [NoneBy](#noneby)
- [Intersect](#intersect)
+- [IntersectBy](#intersectby)
- [Difference](#difference)
- [Union](#union)
- [Without](#without)
@@ -375,6 +393,17 @@ even := lo.Filter([]int{1, 2, 3, 4}, func(x int, index int) bool {
// []int{2, 4}
```
+```go
+// Use FilterErr when the predicate can return an error
+even, err := lo.FilterErr([]int{1, 2, 3, 4}, func(x int, _ int) (bool, error) {
+ if x == 3 {
+ return false, fmt.Errorf("number 3 is not allowed")
+ }
+ return x%2 == 0, nil
+})
+// []int(nil), error("number 3 is not allowed")
+```
+
[[play](https://go.dev/play/p/Apjg3WeSi7K)]
Mutable: like `lo.Filter()`, but the slice is updated in place.
@@ -407,9 +436,20 @@ lo.Map([]int64{1, 2, 3, 4}, func(x int64, index int) string {
// []string{"1", "2", "3", "4"}
```
+```go
+// Use MapErr when the transform function can return an error
+result, err := lo.MapErr([]int{1, 2, 3, 4}, func(x int, _ int) (string, error) {
+ if x == 3 {
+ return "", fmt.Errorf("number 3 is not allowed")
+ }
+ return strconv.Itoa(x), nil
+})
+// []string(nil), error("number 3 is not allowed")
+```
+
[[play](https://go.dev/play/p/OkPcYAhBo0D)]
-Parallel processing: like `lo.Map()`, but the mapper function is called in a goroutine. Results are returned in the same order.
+Parallel processing: like `lo.Map()`, but the transform function is called in a goroutine. Results are returned in the same order.
```go
import lop "github.com/samber/lo/parallel"
@@ -487,6 +527,17 @@ lo.FlatMap([]int64{0, 1, 2}, func(x int64, _ int) []string {
// []string{"0", "0", "1", "1", "2", "2"}
```
+```go
+// Use FlatMapErr when the transform function can return an error
+result, err := lo.FlatMapErr([]int64{0, 1, 2, 3}, func(x int64, _ int) ([]string, error) {
+ if x == 2 {
+ return nil, fmt.Errorf("number 2 is not allowed")
+ }
+ return []string{strconv.FormatInt(x, 10), strconv.FormatInt(x, 10)}, nil
+})
+// []string(nil), error("number 2 is not allowed")
+```
+
[[play](https://go.dev/play/p/YSoYmQTA8-U)]
### Reduce
@@ -500,6 +551,17 @@ sum := lo.Reduce([]int{1, 2, 3, 4}, func(agg int, item int, _ int) int {
// 10
```
+```go
+// Use ReduceErr when the accumulator function can return an error
+result, err := lo.ReduceErr([]int{1, 2, 3, 4}, func(agg int, item int, _ int) (int, error) {
+ if item == 3 {
+ return 0, fmt.Errorf("number 3 is not allowed")
+ }
+ return agg + item, nil
+}, 0)
+// 0, error("number 3 is not allowed")
+```
+
[[play](https://go.dev/play/p/R4UHXZNaaUG)]
### ReduceRight
@@ -513,6 +575,17 @@ result := lo.ReduceRight([][]int{{0, 1}, {2, 3}, {4, 5}}, func(agg []int, item [
// []int{4, 5, 2, 3, 0, 1}
```
+```go
+// Use ReduceRightErr when the accumulator function can return an error
+result, err := lo.ReduceRightErr([]int{1, 2, 3, 4}, func(agg int, item int, _ int) (int, error) {
+ if item == 2 {
+ return 0, fmt.Errorf("number 2 is not allowed")
+ }
+ return agg + item, nil
+}, 0)
+// 0, error("number 2 is not allowed")
+```
+
[[play](https://go.dev/play/p/Fq3W70l7wXF)]
### ForEach
@@ -609,6 +682,17 @@ uniqValues := lo.UniqBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
// []int{0, 1, 2}
```
+```go
+// Use UniqByErr when the iteratee function can return an error
+result, err := lo.UniqByErr([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, error) {
+ if i == 3 {
+ return 0, fmt.Errorf("number 3 is not allowed")
+ }
+ return i % 3, nil
+})
+// []int(nil), error("number 3 is not allowed")
+```
+
[[play](https://go.dev/play/p/g42Z3QSb53u)]
### GroupBy
@@ -624,6 +708,17 @@ groups := lo.GroupBy([]int{0, 1, 2, 3, 4, 5}, func(i int) int {
// map[int][]int{0: []int{0, 3}, 1: []int{1, 4}, 2: []int{2, 5}}
```
+```go
+// Use GroupByErr when the iteratee function can return an error
+result, err := lo.GroupByErr([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, error) {
+ if i == 3 {
+ return 0, fmt.Errorf("number 3 is not allowed")
+ }
+ return i % 3, nil
+})
+// map[int][]int(nil), error("number 3 is not allowed")
+```
+
[[play](https://go.dev/play/p/XnQBd_v6brd)]
Parallel processing: like `lo.GroupBy()`, but callback is called in goroutine.
@@ -650,6 +745,17 @@ groups := lo.GroupByMap([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, int) {
// map[int][]int{0: []int{0, 6}, 1: []int{2, 8}, 2: []int{4, 10}}
```
+```go
+// Use GroupByMapErr when the transform function can return an error
+result, err := lo.GroupByMapErr([]int{0, 1, 2, 3, 4, 5}, func(i int) (int, int, error) {
+ if i == 3 {
+ return 0, 0, fmt.Errorf("number 3 is not allowed")
+ }
+ return i % 3, i * 2, nil
+})
+// map[int][]int(nil), error("number 3 is not allowed")
+```
+
[[play](https://go.dev/play/p/iMeruQ3_W80)]
### Chunk
@@ -672,6 +778,36 @@ lo.Chunk([]int{0}, 2)
[[play](https://go.dev/play/p/kEMkFbdu85g)]
+### Window
+
+Creates a slice of sliding windows of a given size. Each window shares size-1 elements with the previous one. This is equivalent to `Sliding(collection, size, 1)`.
+
+```go
+lo.Window([]int{1, 2, 3, 4, 5}, 3)
+// [][]int{{1, 2, 3}, {2, 3, 4}, {3, 4, 5}}
+
+lo.Window([]float64{20, 22, 21, 23, 24}, 3)
+// [][]float64{{20, 22, 21}, {22, 21, 23}, {21, 23, 24}}
+```
+
+### Sliding
+
+Creates a slice of sliding windows of a given size with a given step. If step is equal to size, windows have no common elements (similar to Chunk). If step is less than size, windows share common elements.
+
+```go
+// Windows with shared elements (step < size)
+lo.Sliding([]int{1, 2, 3, 4, 5, 6}, 3, 1)
+// [][]int{{1, 2, 3}, {2, 3, 4}, {3, 4, 5}, {4, 5, 6}}
+
+// Windows with no shared elements (step == size, like Chunk)
+lo.Sliding([]int{1, 2, 3, 4, 5, 6}, 3, 3)
+// [][]int{{1, 2, 3}, {4, 5, 6}}
+
+// Step > size (skipping elements)
+lo.Sliding([]int{1, 2, 3, 4, 5, 6, 7, 8}, 2, 3)
+// [][]int{{1, 2}, {4, 5}, {7, 8}}
+```
+
### PartitionBy
Returns a slice of elements split into groups. The order of grouped values is determined by the order they occur in collection. The grouping is generated from the results of running each element of collection through iteratee.
@@ -690,6 +826,22 @@ partitions := lo.PartitionBy([]int{-2, -1, 0, 1, 2, 3, 4, 5}, func(x int) string
// [][]int{{-2, -1}, {0, 2, 4}, {1, 3, 5}}
```
+```go
+// Use PartitionByErr when the iteratee function can return an error
+result, err := lo.PartitionByErr([]int{-2, -1, 0, 1, 2}, func(x int) (string, error) {
+ if x == 0 {
+ return "", fmt.Errorf("zero is not allowed")
+ }
+ if x < 0 {
+ return "negative", nil
+ } else if x%2 == 0 {
+ return "even", nil
+ }
+ return "odd", nil
+})
+// [][]int(nil), error("zero is not allowed")
+```
+
[[play](https://go.dev/play/p/NfQ_nGjkgXW)]
Parallel processing: like `lo.PartitionBy()`, but callback is called in goroutine. Results are returned in the same order.
@@ -719,6 +871,20 @@ flat := lo.Flatten([][]int{{0, 1}, {2, 3, 4, 5}})
[[play](https://go.dev/play/p/rbp9ORaMpjw)]
+### Concat
+
+Returns a new slice containing all the elements in collections. Concat conserves the order of the elements.
+
+```go
+slice := lo.Concat([]int{1, 2}, []int{3, 4})
+// []int{1, 2, 3, 4}
+
+slice := lo.Concat(nil, []int{1, 2}, nil, []int{3, 4}, nil)
+// []int{1, 2, 3, 4}
+
+slice := lo.Concat[int]()
+// []int{}
+```
### Interleave
Round-robin alternating input slices and sequentially appending value at index into result.
@@ -825,6 +991,18 @@ slice := lo.RepeatBy(5, func(i int) string {
[[play](https://go.dev/play/p/ozZLCtX_hNU)]
+With error handling:
+
+```go
+slice, err := lo.RepeatByErr(5, func(i int) (string, error) {
+ if i == 3 {
+ return "", fmt.Errorf("index 3 is not allowed")
+ }
+ return fmt.Sprintf("item-%d", i), nil
+})
+// []string(nil), error("index 3 is not allowed")
+```
+
### KeyBy
Transforms a slice or a slice of structs to a map based on a pivot callback.
@@ -849,6 +1027,16 @@ result := lo.KeyBy(characters, func(char Character) string {
//map[a:{dir:left code:97} d:{dir:right code:100}]
```
+```go
+result, err := lo.KeyByErr([]string{"a", "aa", "aaa", ""}, func(str string) (int, error) {
+ if str == "" {
+ return 0, fmt.Errorf("empty string not allowed")
+ }
+ return len(str), nil
+})
+// map[int]string(nil), error("empty string not allowed")
+```
+
[[play](https://go.dev/play/p/mdaClUAT-zZ)]
### SliceToMap (alias: Associate)
@@ -901,6 +1089,50 @@ set := lo.Keyify([]int{1, 1, 2, 3, 4})
[[play](https://go.dev/play/p/RYhhM_csqIG)]
+### Take
+
+Takes the first n elements from a slice.
+
+```go
+l := lo.Take([]int{0, 1, 2, 3, 4, 5}, 3)
+// []int{0, 1, 2}
+
+l := lo.Take([]int{0, 1, 2}, 5)
+// []int{0, 1, 2}
+```
+
+### TakeWhile
+
+Takes elements from the beginning while the predicate returns true.
+
+```go
+l := lo.TakeWhile([]int{0, 1, 2, 3, 4, 5}, func(val int) bool {
+ return val < 3
+})
+// []int{0, 1, 2}
+
+l := lo.TakeWhile([]string{"a", "aa", "aaa", "aa"}, func(val string) bool {
+ return len(val) <= 2
+})
+// []string{"a", "aa"}
+```
+
+### TakeFilter
+
+Filters elements and takes the first n elements that match the predicate. Equivalent to calling Take(Filter(...)), but more efficient as it stops after finding n matches.
+
+```go
+l := lo.TakeFilter([]int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, 3, func(val int, index int) bool {
+ return val%2 == 0
+})
+// []int{2, 4, 6}
+
+l := lo.TakeFilter([]string{"a", "aa", "aaa", "aaaa"}, 2, func(val string, index int) bool {
+ return len(val) > 1
+})
+// []string{"aa", "aaa"}
+```
+
### Drop
Drops n elements from the beginning of a slice.
@@ -971,6 +1203,17 @@ odd := lo.Reject([]int{1, 2, 3, 4}, func(x int, _ int) bool {
// []int{1, 3}
```
+```go
+// Use RejectErr when the predicate can return an error
+odd, err := lo.RejectErr([]int{1, 2, 3, 4}, func(x int, _ int) (bool, error) {
+ if x == 3 {
+ return false, fmt.Errorf("number 3 is not allowed")
+ }
+ return x%2 == 0, nil
+})
+// []int(nil), error("number 3 is not allowed")
+```
+
[[play](https://go.dev/play/p/YkLMODy1WEL)]
### RejectMap
@@ -1023,6 +1266,17 @@ count := lo.CountBy([]int{1, 5, 1}, func(i int) bool {
// 2
```
+```go
+// Use CountByErr when the predicate can return an error
+count, err := lo.CountByErr([]int{1, 5, 1}, func(i int) (bool, error) {
+ if i == 5 {
+ return false, fmt.Errorf("5 not allowed")
+ }
+ return i < 4, nil
+})
+// 0, error("5 not allowed")
+```
+
[[play](https://go.dev/play/p/ByQbNYQQi4X)]
### CountValues
@@ -1158,6 +1412,20 @@ slice := lo.ReplaceAll(in, -1, 42)
[[play](https://go.dev/play/p/a9xZFUHfYcV)]
+### Clone
+
+Returns a shallow copy of the collection.
+
+```go
+in := []int{1, 2, 3, 4, 5}
+cloned := lo.Clone(in)
+// Verify it's a different slice by checking that modifying one doesn't affect the other
+in[0] = 99
+// cloned is []int{1, 2, 3, 4, 5}
+```
+
+[[play](https://go.dev/play/p/hgHmoOIxmuH)]
+
### Compact
Returns a slice of all non-zero elements.
@@ -1182,12 +1450,12 @@ slice := lo.IsSorted([]int{0, 1, 2, 3, 4, 5, 6, 7, 8, 9})
[[play](https://go.dev/play/p/mc3qR-t4mcx)]
-### IsSortedByKey
+### IsSortedBy
Checks if a slice is sorted by iteratee.
```go
-slice := lo.IsSortedByKey([]string{"a", "bb", "ccc"}, func(s string) int {
+slice := lo.IsSortedBy([]string{"a", "bb", "ccc"}, func(s string) int {
return len(s)
})
// true
@@ -1455,6 +1723,17 @@ m := lo.PickBy(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(key string, va
// map[string]int{"foo": 1, "baz": 3}
```
+```go
+// Use PickByErr when the predicate can return an error
+m, err := lo.PickByErr(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(key string, value int) (bool, error) {
+ if key == "bar" {
+ return false, fmt.Errorf("bar not allowed")
+ }
+ return value%2 == 1, nil
+})
+// map[string]int(nil), error("bar not allowed")
+```
+
[[play](https://go.dev/play/p/kdg8GR_QMmf)]
### PickByKeys
@@ -1490,6 +1769,17 @@ m := lo.OmitBy(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(key string, va
// map[string]int{"bar": 2}
```
+```go
+// Use OmitByErr when the predicate can return an error
+m, err := lo.OmitByErr(map[string]int{"foo": 1, "bar": 2, "baz": 3}, func(key string, value int) (bool, error) {
+ if key == "bar" {
+ return false, fmt.Errorf("bar not allowed")
+ }
+ return value%2 == 1, nil
+})
+// map[string]int(nil), error("bar not allowed")
+```
+
[[play](https://go.dev/play/p/EtBsR43bdsd)]
### OmitByKeys
@@ -1615,6 +1905,17 @@ m2 := lo.MapKeys(map[int]int{1: 1, 2: 2, 3: 3, 4: 4}, func(_ int, v int) string
// map[string]int{"1": 1, "2": 2, "3": 3, "4": 4}
```
+```go
+// Use MapKeysErr when the iteratee can return an error
+m2, err := lo.MapKeysErr(map[int]int{1: 1, 2: 2, 3: 3}, func(_ int, v int) (string, error) {
+ if v == 2 {
+ return "", fmt.Errorf("even number not allowed")
+ }
+ return strconv.FormatInt(int64(v), 10), nil
+})
+// map[string]int(nil), error("even number not allowed")
+```
+
[[play](https://go.dev/play/p/9_4WPIqOetJ)]
### MapValues
@@ -1630,6 +1931,18 @@ m2 := lo.MapValues(m1, func(x int64, _ int) string {
// map[int]string{1: "1", 2: "2", 3: "3"}
```
+```go
+// Use MapValuesErr when the iteratee can return an error
+m1 := map[int]int64{1: 1, 2: 2, 3: 3}
+m2, err := lo.MapValuesErr(m1, func(x int64, _ int) (string, error) {
+ if x == 2 {
+ return "", fmt.Errorf("even number not allowed")
+ }
+ return strconv.FormatInt(x, 10), nil
+})
+// map[int]string(nil), error("even number not allowed")
+```
+
[[play](https://go.dev/play/p/T_8xAfvcf0W)]
### MapEntries
@@ -1645,6 +1958,18 @@ out := lo.MapEntries(in, func(k string, v int) (int, string) {
// map[int]string{1: "foo", 2: "bar"}
```
+```go
+// Use MapEntriesErr when the iteratee can return an error
+in := map[string]int{"foo": 1, "bar": 2, "baz": 3}
+out, err := lo.MapEntriesErr(in, func(k string, v int) (int, string, error) {
+ if k == "bar" {
+ return 0, "", fmt.Errorf("bar not allowed")
+ }
+ return v, k, nil
+})
+// map[int]string(nil), error("bar not allowed")
+```
+
[[play](https://go.dev/play/p/VuvNQzxKimT)]
### MapToSlice
@@ -1660,6 +1985,18 @@ s := lo.MapToSlice(m, func(k int, v int64) string {
// []string{"1_4", "2_5", "3_6"}
```
+```go
+// Use MapToSliceErr when the iteratee can return an error
+m := map[int]int64{1: 4, 2: 5, 3: 6}
+s, err := lo.MapToSliceErr(m, func(k int, v int64) (string, error) {
+ if k == 2 {
+ return "", fmt.Errorf("key 2 not allowed")
+ }
+ return fmt.Sprintf("%d_%d", k, v), nil
+})
+// []string(nil), error("key 2 not allowed")
+```
+
[[play](https://go.dev/play/p/ZuiCZpDt6LD)]
### FilterMapToSlice
@@ -1677,6 +2014,18 @@ result := lo.FilterMapToSlice(kv, func(k int, v int64) (string, bool) {
// []{"2_2", "4_4"}
```
+```go
+kv := map[int]int64{1: 1, 2: 2, 3: 3, 4: 4}
+
+result, err := lo.FilterMapToSliceErr(kv, func(k int, v int64) (string, bool, error) {
+ if k == 3 {
+ return "", false, fmt.Errorf("key 3 not allowed")
+ }
+ return fmt.Sprintf("%d_%d", k, v), k%2 == 0, nil
+})
+// []string(nil), error("key 3 not allowed")
+```
+
### FilterKeys
Transforms a map into a slice based on predicate returns true for specific elements. It is a mix of `lo.Filter()` and `lo.Keys()`.
@@ -1690,6 +2039,17 @@ result := FilterKeys(kv, func(k int, v string) bool {
// [1]
```
+```go
+// Use FilterKeysErr when the predicate can return an error
+result, err := lo.FilterKeysErr(map[int]string{1: "foo", 2: "bar", 3: "baz"}, func(k int, v string) (bool, error) {
+ if k == 3 {
+ return false, fmt.Errorf("key 3 not allowed")
+ }
+ return v == "foo", nil
+})
+// []int(nil), error("key 3 not allowed")
+```
+
[[play](https://go.dev/play/p/OFlKXlPrBAe)]
### FilterValues
@@ -1705,6 +2065,17 @@ result := FilterValues(kv, func(k int, v string) bool {
// ["foo"]
```
+```go
+// Use FilterValuesErr when the predicate can return an error
+result, err := lo.FilterValuesErr(map[int]string{1: "foo", 2: "bar", 3: "baz"}, func(k int, v string) (bool, error) {
+ if k == 3 {
+ return false, fmt.Errorf("key 3 not allowed")
+ }
+ return v == "foo", nil
+})
+// []string(nil), error("key 3 not allowed")
+```
+
[[play](https://go.dev/play/p/YVD5r_h-LX-)]
### Range / RangeFrom / RangeWithSteps
@@ -1784,6 +2155,19 @@ sum := lo.SumBy(strings, func(item string) int {
// 6
```
+With error handling:
+
+```go
+strings := []string{"foo", "bar", "baz"}
+sum, err := lo.SumByErr(strings, func(item string) (int, error) {
+ if item == "bar" {
+ return 0, fmt.Errorf("invalid item: %s", item)
+ }
+ return len(item), nil
+})
+// sum: 3, err: invalid item: bar
+```
+
### Product
Calculates the product of the values in a collection.
@@ -1812,6 +2196,18 @@ product := lo.ProductBy(strings, func(item string) int {
// 9
```
+```go
+// Use ProductByErr when the transform function can return an error
+strings := []string{"foo", "bar", "baz"}
+product, err := lo.ProductByErr(strings, func(item string) (int, error) {
+ if item == "bar" {
+ return 0, fmt.Errorf("bar is not allowed")
+ }
+ return len(item), nil
+})
+// 3, error("bar is not allowed")
+```
+
[[play](https://go.dev/play/p/wadzrWr9Aer)]
### Mean
@@ -1850,6 +2246,18 @@ mean := lo.MeanBy([]float64{}, mapper)
// 0
```
+```go
+// Use MeanByErr when the transform function can return an error
+list := []string{"aa", "bbb", "cccc", "ddddd"}
+mean, err := lo.MeanByErr(list, func(item string) (float64, error) {
+ if item == "cccc" {
+ return 0, fmt.Errorf("cccc is not allowed")
+ }
+ return float64(len(item)), nil
+})
+// 0, error("cccc is not allowed")
+```
+
[[play](https://go.dev/play/p/j7TsVwBOZ7P)]
### Mode
@@ -1920,6 +2328,8 @@ lo.ChunkString("1", 2)
// []string{"1"}
```
+Note: `lo.ChunkString` and `lo.Chunk` functions behave inconsistently for empty input: `lo.ChunkString("", n)` returns `[""]` instead of `[]`. See [#788](https://github.com/samber/lo/issues/788).
+
[[play](https://go.dev/play/p/__FLTuJVz54)]
### RuneLength
@@ -2004,7 +2414,7 @@ str := lo.Capitalize("heLLO")
### Ellipsis
-Trims and truncates a string to a specified length in `bytes` and appends an ellipsis if truncated. If the string contains non-ASCII characters (which may occupy multiple bytes in UTF-8), truncating by byte length may split a character in the middle, potentially resulting in garbled output.
+Trims and truncates a string to a specified length in runes (Unicode code points) and appends an ellipsis if truncated. Multi-byte characters such as emoji or CJK ideographs are never split in the middle.
```go
str := lo.Ellipsis(" Lorem Ipsum ", 5)
@@ -2015,6 +2425,12 @@ str := lo.Ellipsis("Lorem Ipsum", 100)
str := lo.Ellipsis("Lorem Ipsum", 3)
// ...
+
+str := lo.Ellipsis("hello 世界! 你好", 8)
+// hello...
+
+str := lo.Ellipsis("🏠🐶🐱🌟", 4)
+// 🏠🐶🐱🌟
```
[[play](https://go.dev/play/p/qE93rgqe1TW)]
@@ -2079,6 +2495,18 @@ items := lo.ZipBy2([]string{"a", "b"}, []int{1, 2}, func(a string, b int) string
// []string{"a-1", "b-2"}
```
+With error handling:
+
+```go
+items, err := lo.ZipByErr2([]string{"a", "b"}, []int{1, 2}, func(a string, b int) (string, error) {
+ if b == 2 {
+ return "", fmt.Errorf("number 2 is not allowed")
+ }
+ return fmt.Sprintf("%s-%d", a, b), nil
+})
+// []string(nil), error("number 2 is not allowed")
+```
+
### Unzip2 -> Unzip9
Unzip accepts a slice of grouped elements and creates a slice regrouping the elements to their pre-zip configuration.
@@ -2103,6 +2531,18 @@ a, b := lo.UnzipBy2([]string{"hello", "john", "doe"}, func(str string) (string,
// []int{5, 4, 3}
```
+```go
+a, b, err := lo.UnzipByErr2([]string{"hello", "error", "world"}, func(str string) (string, int, error) {
+ if str == "error" {
+ return "", 0, fmt.Errorf("error string not allowed")
+ }
+ return str, len(str), nil
+})
+// []string{}
+// []int{}
+// error string not allowed
+```
+
### CrossJoin2 -> CrossJoin9
Combines every item from one list with every item from others. It is the cartesian product of lists received as arguments. Returns an empty list if a list is empty.
@@ -2119,7 +2559,7 @@ result := lo.CrossJoin2([]string{"hello", "john", "doe"}, []int{1, 2})
### CrossJoinBy2 -> CrossJoinBy9
-Combines every item from one list with every item from others. It is the cartesian product of lists received as arguments. The project function is used to create the output values. Returns an empty list if a list is empty.
+Combines every item from one list with every item from others. It is the cartesian product of lists received as arguments. The transform function is used to create the output values. Returns an empty list if a list is empty.
```go
result := lo.CrossJoinBy2([]string{"hello", "john", "doe"}, []int{1, 2}, func(a A, b B) string {
@@ -2133,6 +2573,18 @@ result := lo.CrossJoinBy2([]string{"hello", "john", "doe"}, []int{1, 2}, func(a
// "doe - 2"
```
+With error handling:
+
+```go
+result, err := lo.CrossJoinByErr2([]string{"hello", "john"}, []int{1, 2}, func(a string, b int) (string, error) {
+ if a == "john" {
+ return "", fmt.Errorf("john not allowed")
+ }
+ return fmt.Sprintf("%s - %d", a, b), nil
+})
+// []string(nil), error("john not allowed")
+```
+
### Duration
Returns the time taken to execute a function.
@@ -2550,7 +3002,7 @@ b := NoneBy([]int{1, 2, 3, 4}, func(x int) bool {
### Intersect
-Returns the intersection between two collections.
+Returns the intersection between collections.
```go
result1 := lo.Intersect([]int{0, 1, 2, 3, 4, 5}, []int{0, 2})
@@ -2561,6 +3013,31 @@ result2 := lo.Intersect([]int{0, 1, 2, 3, 4, 5}, []int{0, 6})
result3 := lo.Intersect([]int{0, 1, 2, 3, 4, 5}, []int{-1, 6})
// []int{}
+
+result4 := lo.Intersect([]int{0, 3, 5, 7}, []int{3, 5}, []int{0, 1, 2, 0, 3, 0})
+// []int{3}
+```
+
+### IntersectBy
+
+Returns the intersection between two collections using a custom key selector function.
+
+```go
+transform := func(v int) string {
+ return strconv.Itoa(v)
+}
+
+result1 := lo.IntersectBy(transform, []int{0, 1, 2, 3, 4, 5}, []int{0, 2})
+// []int{0, 2}
+
+result2 := lo.IntersectBy(transform, []int{0, 1, 2, 3, 4, 5}, []int{0, 6})
+// []int{0}
+
+result3 := lo.IntersectBy(transform, []int{0, 1, 2, 3, 4, 5}, []int{-1, 6})
+// []int{}
+
+result4 := lo.IntersectBy(transform, []int{0, 3, 5, 7}, []int{3, 5}, []int{0, 1, 2, 0, 3, 0})
+// []int{3}
```
### Difference
@@ -2608,7 +3085,7 @@ Filters a slice by excluding elements whose extracted keys match any in the excl
Returns a new slice containing only the elements whose keys are not in the exclude list.
```go
-type struct User {
+type User struct {
ID int
Name string
}
@@ -2633,6 +3110,30 @@ filteredUsers := lo.WithoutBy(users, getID, excludedIDs...)
// []User[{ID: 1, Name: "Alice"}]
```
+```go
+// Use WithoutByErr when the iteratee can return an error
+type struct User {
+ ID int
+ Name string
+}
+
+users := []User{
+ {ID: 1, Name: "Alice"},
+ {ID: 2, Name: "Bob"},
+ {ID: 3, Name: "Charlie"},
+}
+
+getID := func(user User) (int, error) {
+ if user.ID == 2 {
+ return 0, fmt.Errorf("Bob not allowed")
+ }
+ return user.ID, nil
+}
+
+filteredUsers, err := lo.WithoutByErr(users, getID, 2, 3)
+// []User(nil), error("Bob not allowed")
+```
+
### WithoutEmpty
Returns a slice excluding zero values.
@@ -2751,6 +3252,25 @@ str, ok := lo.Find([]string{"foobar"}, func(i string) bool {
// "", false
```
+```go
+// Use FindErr when the predicate can return an error
+str, err := lo.FindErr([]string{"a", "b", "c", "d"}, func(i string) (bool, error) {
+ if i == "c" {
+ return false, fmt.Errorf("c is not allowed")
+ }
+ return i == "b", nil
+})
+// "b", nil
+
+str, err = lo.FindErr([]string{"a", "b", "c"}, func(i string) (bool, error) {
+ if i == "b" {
+ return false, fmt.Errorf("b is not allowed")
+ }
+ return i == "b", nil
+})
+// "", error("b is not allowed")
+```
+
[[play](https://go.dev/play/p/Eo7W0lvKTky)]
### FindIndexOf
@@ -2879,6 +3399,18 @@ duplicatedValues := lo.FindDuplicatesBy([]int{3, 4, 5, 6, 7}, func(i int) int {
// []int{3, 4}
```
+With error handling:
+
+```go
+duplicatedValues, err := lo.FindDuplicatesByErr([]int{3, 4, 5, 6, 7}, func(i int) (int, error) {
+ if i == 5 {
+ return 0, fmt.Errorf("number 5 is not allowed")
+ }
+ return i % 3, nil
+})
+// []int(nil), error("number 5 is not allowed")
+```
+
### Min
Search the minimum value of a collection.
@@ -2935,6 +3467,17 @@ min := lo.MinBy([]string{}, func(item string, min string) bool {
// ""
```
+```go
+// Use MinByErr when the comparison function can return an error
+min, err := lo.MinByErr([]string{"s1", "string2", "s3"}, func(item string, min string) (bool, error) {
+ if item == "string2" {
+ return false, fmt.Errorf("string2 is not allowed")
+ }
+ return len(item) < len(min), nil
+})
+// "s1", error("string2 is not allowed")
+```
+
### MinIndexBy
Search the minimum value of a collection using the given comparison function and the index of the minimum value.
@@ -2955,6 +3498,16 @@ min, index := lo.MinIndexBy([]string{}, func(item string, min string) bool {
// "", -1
```
+```go
+min, index, err := lo.MinIndexByErr([]string{"s1", "string2", "s3"}, func(item string, min string) (bool, error) {
+ if item == "s2" {
+ return false, fmt.Errorf("s2 is not allowed")
+ }
+ return len(item) < len(min), nil
+})
+// "s1", 0, error("s2 is not allowed")
+```
+
### Earliest
Search the minimum time.Time of a collection.
@@ -2983,6 +3536,17 @@ earliest := lo.EarliestBy([]foo{{time.Now()}, {}}, func(i foo) time.Time {
// {bar:{2023-04-01 01:02:03 +0000 UTC}}
```
+```go
+// Use EarliestByErr when the iteratee function can return an error
+earliest, err := lo.EarliestByErr([]foo{{time.Now()}, {}}, func(i foo) (time.Time, error) {
+ if i.bar.IsZero() {
+ return time.Time{}, fmt.Errorf("zero time not allowed")
+ }
+ return i.bar, nil
+})
+// {bar:{...}}, error("zero time not allowed")
+```
+
### Max
Search the maximum value of a collection.
@@ -3037,6 +3601,19 @@ max := lo.MaxBy([]string{}, func(item string, max string) bool {
// ""
```
+```go
+// Use MaxByErr when the comparison function can return an error
+max, err := lo.MaxByErr([]string{"string1", "s2", "string3"}, func(item string, max string) (bool, error) {
+ if item == "s2" {
+ return false, fmt.Errorf("s2 is not allowed")
+ }
+ return len(item) > len(max), nil
+})
+// "string1", error("s2 is not allowed")
+```
+
+[[play](https://go.dev/play/p/JW1qu-ECwF7)]
+
### MaxIndexBy
Search the maximum value of a collection using the given comparison function and the index of the maximum value.
@@ -3057,6 +3634,19 @@ max, index := lo.MaxIndexBy([]string{}, func(item string, max string) bool {
// "", -1
```
+```go
+// Use MaxIndexByErr when the comparison function can return an error
+max, index, err := lo.MaxIndexByErr([]string{"string1", "s2", "string3"}, func(item string, max string) (bool, error) {
+ if item == "s2" {
+ return false, fmt.Errorf("s2 is not allowed")
+ }
+ return len(item) > len(max), nil
+})
+// "string1", 0, error("s2 is not allowed")
+```
+
+[[play](https://go.dev/play/p/uaUszc-c9QK)]
+
### Latest
Search the maximum time.Time of a collection.
@@ -3085,6 +3675,17 @@ latest := lo.LatestBy([]foo{{time.Now()}, {}}, func(i foo) time.Time {
// {bar:{2023-04-01 01:02:03 +0000 UTC}}
```
+```go
+// Use LatestByErr when the iteratee function can return an error
+result, err := lo.LatestByErr([]foo{{time.Now()}, {}}, func(i foo) (time.Time, error) {
+ if i.bar.IsZero() {
+ return time.Time{}, fmt.Errorf("zero time not allowed")
+ }
+ return i.bar, nil
+})
+// foo{}, error("zero time not allowed")
+```
+
### First
Returns the first element of a collection and check for availability of the first element.
diff --git a/vendor/github.com/samber/lo/channel.go b/vendor/github.com/samber/lo/channel.go
index 9a10852d3..5c7dd9ebd 100644
--- a/vendor/github.com/samber/lo/channel.go
+++ b/vendor/github.com/samber/lo/channel.go
@@ -26,12 +26,7 @@ func ChannelDispatcher[T any](stream <-chan T, count, channelBufferCap int, stra
var i uint64
- for {
- msg, ok := <-stream
- if !ok {
- return
- }
-
+ for msg := range stream {
destination := strategy(msg, i, roChildren) % count
children[destination] <- msg
@@ -107,8 +102,8 @@ func DispatchingStrategyRandom[T any](msg T, index uint64, channels []<-chan T)
func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy[T] {
seq := []int{}
- for i := 0; i < len(weights); i++ {
- for j := 0; j < weights[i]; j++ {
+ for i, weight := range weights {
+ for j := 0; j < weight; j++ {
seq = append(seq, i)
}
}
@@ -143,22 +138,22 @@ func DispatchingStrategyFirst[T any](msg T, index uint64, channels []<-chan T) i
// DispatchingStrategyLeast distributes messages in the emptiest channel.
// Play: https://go.dev/play/p/ypy0jrRcEe7
func DispatchingStrategyLeast[T any](msg T, index uint64, channels []<-chan T) int {
- seq := Range(len(channels))
-
- return MinBy(seq, func(item, mIn int) bool {
- return len(channels[item]) < len(channels[mIn])
+ _, i := MinIndexBy(channels, func(a, b <-chan T) bool {
+ return len(a) < len(b)
})
+
+ return i
}
// DispatchingStrategyMost distributes messages in the fullest channel.
// If the channel capacity is exceeded, the next channel will be selected and so on.
// Play: https://go.dev/play/p/erHHone7rF9
func DispatchingStrategyMost[T any](msg T, index uint64, channels []<-chan T) int {
- seq := Range(len(channels))
-
- return MaxBy(seq, func(item, mAx int) bool {
- return len(channels[item]) > len(channels[mAx]) && channelIsNotFull(channels[item])
+ _, i := MaxIndexBy(channels, func(a, b <-chan T) bool {
+ return len(a) > len(b) && channelIsNotFull(a)
})
+
+ return i
}
// SliceToChannel returns a read-only channel of collection elements.
@@ -213,10 +208,9 @@ func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan T {
// Play: https://go.dev/play/p/gPQ-6xmcKQI
func Buffer[T any](ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) {
buffer := make([]T, 0, size)
- index := 0
now := time.Now()
- for ; index < size; index++ {
+ for index := 0; index < size; index++ {
item, ok := <-ch
if !ok {
return buffer, index, time.Since(now), false
@@ -225,14 +219,7 @@ func Buffer[T any](ch <-chan T, size int) (collection []T, length int, readTime
buffer = append(buffer, item)
}
- return buffer, index, time.Since(now), true
-}
-
-// Batch creates a slice of n elements from a channel. Returns the slice and the slice length.
-//
-// Deprecated: Use [Buffer] instead.
-func Batch[T any](ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) {
- return Buffer(ch, size)
+ return buffer, size, time.Since(now), true
}
// BufferWithContext creates a slice of n elements from a channel, with context. Returns the slice and the slice length.
@@ -267,13 +254,6 @@ func BufferWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (col
return BufferWithContext(ctx, ch, size)
}
-// BatchWithTimeout creates a slice of n elements from a channel, with timeout. Returns the slice and the slice length.
-//
-// Deprecated: Use [BufferWithTimeout] instead.
-func BatchWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, ok bool) {
- return BufferWithTimeout(ch, size, timeout)
-}
-
// FanIn collects messages from multiple input channels into a single buffered channel.
// Output messages have no priority. When all upstream channels reach EOF, downstream channel closes.
// Play: https://go.dev/play/p/FH8Wq-T04Jb
@@ -300,14 +280,6 @@ func FanIn[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T {
return out
}
-// ChannelMerge collects messages from multiple input channels into a single buffered channel.
-// Output messages have no priority. When all upstream channels reach EOF, downstream channel closes.
-//
-// Deprecated: Use [FanIn] instead.
-func ChannelMerge[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T {
- return FanIn(channelBufferCap, upstreams...)
-}
-
// FanOut broadcasts all the upstream messages to multiple downstream channels.
// When upstream channel reaches EOF, downstream channels close. If any downstream
// channels is full, broadcasting is paused.
diff --git a/vendor/github.com/samber/lo/concurrency.go b/vendor/github.com/samber/lo/concurrency.go
index 35e1f7496..70871b286 100644
--- a/vendor/github.com/samber/lo/concurrency.go
+++ b/vendor/github.com/samber/lo/concurrency.go
@@ -10,9 +10,9 @@ type synchronize struct {
locker sync.Locker
}
-func (s *synchronize) Do(cb func()) {
+func (s *synchronize) Do(callback func()) {
s.locker.Lock()
- Try0(cb)
+ Try0(callback)
s.locker.Unlock()
}
@@ -52,13 +52,13 @@ func Async0(f func()) <-chan struct{} {
}
// Async1 is an alias to Async.
-// Play: https://go.dev/play/p/uo35gosuTLw
+// Play: https://go.dev/play/p/RBQWtIn4PsF
func Async1[A any](f func() A) <-chan A {
return Async(f)
}
// Async2 has the same behavior as Async, but returns the 2 results as a tuple inside the channel.
-// Play: https://go.dev/play/p/7W7mKQi0AhA
+// Play: https://go.dev/play/p/5SzzDjssXOH
func Async2[A, B any](f func() (A, B)) <-chan Tuple2[A, B] {
ch := make(chan Tuple2[A, B], 1)
go func() {
@@ -68,7 +68,7 @@ func Async2[A, B any](f func() (A, B)) <-chan Tuple2[A, B] {
}
// Async3 has the same behavior as Async, but returns the 3 results as a tuple inside the channel.
-// Play: https://go.dev/play/p/L1d6o6l6q0d
+// Play: https://go.dev/play/p/cZpZsDXNmlx
func Async3[A, B, C any](f func() (A, B, C)) <-chan Tuple3[A, B, C] {
ch := make(chan Tuple3[A, B, C], 1)
go func() {
@@ -78,7 +78,7 @@ func Async3[A, B, C any](f func() (A, B, C)) <-chan Tuple3[A, B, C] {
}
// Async4 has the same behavior as Async, but returns the 4 results as a tuple inside the channel.
-// Play: https://go.dev/play/p/1X7q6oL0TqF
+// Play: https://go.dev/play/p/9X5O2VrLzkR
func Async4[A, B, C, D any](f func() (A, B, C, D)) <-chan Tuple4[A, B, C, D] {
ch := make(chan Tuple4[A, B, C, D], 1)
go func() {
@@ -88,7 +88,7 @@ func Async4[A, B, C, D any](f func() (A, B, C, D)) <-chan Tuple4[A, B, C, D] {
}
// Async5 has the same behavior as Async, but returns the 5 results as a tuple inside the channel.
-// Play: https://go.dev/play/p/2W7q4oL1TqG
+// Play: https://go.dev/play/p/MqnUJpkmopA
func Async5[A, B, C, D, E any](f func() (A, B, C, D, E)) <-chan Tuple5[A, B, C, D, E] {
ch := make(chan Tuple5[A, B, C, D, E], 1)
go func() {
@@ -98,7 +98,7 @@ func Async5[A, B, C, D, E any](f func() (A, B, C, D, E)) <-chan Tuple5[A, B, C,
}
// Async6 has the same behavior as Async, but returns the 6 results as a tuple inside the channel.
-// Play: https://go.dev/play/p/3X8q5pM2UrH
+// Play: https://go.dev/play/p/kM1X67JPdSP
func Async6[A, B, C, D, E, F any](f func() (A, B, C, D, E, F)) <-chan Tuple6[A, B, C, D, E, F] {
ch := make(chan Tuple6[A, B, C, D, E, F], 1)
go func() {
diff --git a/vendor/github.com/samber/lo/condition.go b/vendor/github.com/samber/lo/condition.go
index 1eb017e6f..92ed36b0d 100644
--- a/vendor/github.com/samber/lo/condition.go
+++ b/vendor/github.com/samber/lo/condition.go
@@ -121,9 +121,9 @@ func (s *switchCase[T, R]) Case(val T, result R) *switchCase[T, R] {
// CaseF.
// Play: https://go.dev/play/p/TGbKUMAeRUd
-func (s *switchCase[T, R]) CaseF(val T, cb func() R) *switchCase[T, R] {
+func (s *switchCase[T, R]) CaseF(val T, callback func() R) *switchCase[T, R] {
if !s.done && s.predicate == val {
- s.result = cb()
+ s.result = callback()
s.done = true
}
@@ -142,9 +142,9 @@ func (s *switchCase[T, R]) Default(result R) R {
// DefaultF.
// Play: https://go.dev/play/p/TGbKUMAeRUd
-func (s *switchCase[T, R]) DefaultF(cb func() R) R {
+func (s *switchCase[T, R]) DefaultF(callback func() R) R {
if !s.done {
- s.result = cb()
+ s.result = callback()
}
return s.result
diff --git a/vendor/github.com/samber/lo/errors.go b/vendor/github.com/samber/lo/errors.go
index 135230a7d..8313edd7a 100644
--- a/vendor/github.com/samber/lo/errors.go
+++ b/vendor/github.com/samber/lo/errors.go
@@ -30,8 +30,8 @@ func messageFromMsgAndArgs(msgAndArgs ...any) string {
return ""
}
-// must panics if err is error or false.
-func must(err any, messageArgs ...any) {
+// MustChecker panics if err is error or false.
+var MustChecker = func(err any, messageArgs ...any) {
if err == nil {
return
}
@@ -63,14 +63,14 @@ func must(err any, messageArgs ...any) {
// and panics if err is error or false.
// Play: https://go.dev/play/p/fOqtX5HudtN
func Must[T any](val T, err any, messageArgs ...any) T {
- must(err, messageArgs...)
+ MustChecker(err, messageArgs...)
return val
}
// Must0 has the same behavior as Must, but callback returns no variable.
// Play: https://go.dev/play/p/TMoWrRp3DyC
func Must0(err any, messageArgs ...any) {
- must(err, messageArgs...)
+ MustChecker(err, messageArgs...)
}
// Must1 is an alias to Must.
@@ -82,35 +82,35 @@ func Must1[T any](val T, err any, messageArgs ...any) T {
// Must2 has the same behavior as Must, but callback returns 2 variables.
// Play: https://go.dev/play/p/TMoWrRp3DyC
func Must2[T1, T2 any](val1 T1, val2 T2, err any, messageArgs ...any) (T1, T2) {
- must(err, messageArgs...)
+ MustChecker(err, messageArgs...)
return val1, val2
}
// Must3 has the same behavior as Must, but callback returns 3 variables.
// Play: https://go.dev/play/p/TMoWrRp3DyC
func Must3[T1, T2, T3 any](val1 T1, val2 T2, val3 T3, err any, messageArgs ...any) (T1, T2, T3) {
- must(err, messageArgs...)
+ MustChecker(err, messageArgs...)
return val1, val2, val3
}
// Must4 has the same behavior as Must, but callback returns 4 variables.
// Play: https://go.dev/play/p/TMoWrRp3DyC
func Must4[T1, T2, T3, T4 any](val1 T1, val2 T2, val3 T3, val4 T4, err any, messageArgs ...any) (T1, T2, T3, T4) {
- must(err, messageArgs...)
+ MustChecker(err, messageArgs...)
return val1, val2, val3, val4
}
// Must5 has the same behavior as Must, but callback returns 5 variables.
// Play: https://go.dev/play/p/TMoWrRp3DyC
func Must5[T1, T2, T3, T4, T5 any](val1 T1, val2 T2, val3 T3, val4 T4, val5 T5, err any, messageArgs ...any) (T1, T2, T3, T4, T5) {
- must(err, messageArgs...)
+ MustChecker(err, messageArgs...)
return val1, val2, val3, val4, val5
}
// Must6 has the same behavior as Must, but callback returns 6 variables.
// Play: https://go.dev/play/p/TMoWrRp3DyC
func Must6[T1, T2, T3, T4, T5, T6 any](val1 T1, val2 T2, val3 T3, val4 T4, val5 T5, val6 T6, err any, messageArgs ...any) (T1, T2, T3, T4, T5, T6) {
- must(err, messageArgs...)
+ MustChecker(err, messageArgs...)
return val1, val2, val3, val4, val5, val6
}
@@ -356,7 +356,7 @@ func ErrorsAs[T error](err error) (T, bool) {
// Assert does nothing when the condition is true, otherwise it panics with an optional message.
// Play: https://go.dev/play/p/Xv8LLKBMNwI
-func Assert(condition bool, message ...string) {
+var Assert = func(condition bool, message ...string) {
if condition {
return
}
@@ -370,7 +370,7 @@ func Assert(condition bool, message ...string) {
// Assertf does nothing when the condition is true, otherwise it panics with a formatted message.
// Play: https://go.dev/play/p/TVPEmVcyrdY
-func Assertf(condition bool, format string, args ...any) {
+var Assertf = func(condition bool, format string, args ...any) {
if condition {
return
}
diff --git a/vendor/github.com/samber/lo/find.go b/vendor/github.com/samber/lo/find.go
index 534b4d75e..f5be8520c 100644
--- a/vendor/github.com/samber/lo/find.go
+++ b/vendor/github.com/samber/lo/find.go
@@ -1,7 +1,6 @@
package lo
import (
- "fmt"
"time"
"github.com/samber/lo/internal/constraints"
@@ -81,6 +80,26 @@ func Find[T any](collection []T, predicate func(item T) bool) (T, bool) {
return result, false
}
+// FindErr searches for an element in a slice based on a predicate that can return an error.
+// Returns the element and nil error if the element is found.
+// Returns zero value and nil error if the element is not found.
+// If the predicate returns an error, iteration stops immediately and returns zero value and the error.
+func FindErr[T any](collection []T, predicate func(item T) (bool, error)) (T, error) {
+ for i := range collection {
+ matches, err := predicate(collection[i])
+ if err != nil {
+ var result T
+ return result, err
+ }
+ if matches {
+ return collection[i], nil
+ }
+ }
+
+ var result T
+ return result, nil
+}
+
// FindIndexOf searches for an element in a slice based on a predicate and returns the index and true.
// Returns -1 and false if the element is not found.
// Play: https://go.dev/play/p/XWSEM4Ic_t0
@@ -152,16 +171,20 @@ func FindKeyBy[K comparable, V any](object map[K]V, predicate func(key K, value
func FindUniques[T comparable, Slice ~[]T](collection Slice) Slice {
isDupl := make(map[T]bool, len(collection))
+ duplicates := 0
+
for i := range collection {
- duplicated, ok := isDupl[collection[i]]
- if !ok {
- isDupl[collection[i]] = false
- } else if !duplicated {
- isDupl[collection[i]] = true
+ duplicated, seen := isDupl[collection[i]]
+ if !duplicated {
+ isDupl[collection[i]] = seen
+
+ if seen {
+ duplicates++
+ }
}
}
- result := make(Slice, 0, len(collection)-len(isDupl))
+ result := make(Slice, 0, len(isDupl)-duplicates)
for i := range collection {
if duplicated := isDupl[collection[i]]; !duplicated {
@@ -178,18 +201,22 @@ func FindUniques[T comparable, Slice ~[]T](collection Slice) Slice {
func FindUniquesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice {
isDupl := make(map[U]bool, len(collection))
+ duplicates := 0
+
for i := range collection {
key := iteratee(collection[i])
- duplicated, ok := isDupl[key]
- if !ok {
- isDupl[key] = false
- } else if !duplicated {
- isDupl[key] = true
+ duplicated, seen := isDupl[key]
+ if !duplicated {
+ isDupl[key] = seen
+
+ if seen {
+ duplicates++
+ }
}
}
- result := make(Slice, 0, len(collection)-len(isDupl))
+ result := make(Slice, 0, len(isDupl)-duplicates)
for i := range collection {
key := iteratee(collection[i])
@@ -207,16 +234,20 @@ func FindUniquesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee f
func FindDuplicates[T comparable, Slice ~[]T](collection Slice) Slice {
isDupl := make(map[T]bool, len(collection))
+ duplicates := 0
+
for i := range collection {
- duplicated, ok := isDupl[collection[i]]
- if !ok {
- isDupl[collection[i]] = false
- } else if !duplicated {
- isDupl[collection[i]] = true
+ duplicated, seen := isDupl[collection[i]]
+ if !duplicated {
+ isDupl[collection[i]] = seen
+
+ if seen {
+ duplicates++
+ }
}
}
- result := make(Slice, 0, len(collection)-len(isDupl))
+ result := make(Slice, 0, duplicates)
for i := range collection {
if duplicated := isDupl[collection[i]]; duplicated {
@@ -234,18 +265,22 @@ func FindDuplicates[T comparable, Slice ~[]T](collection Slice) Slice {
func FindDuplicatesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice {
isDupl := make(map[U]bool, len(collection))
+ duplicates := 0
+
for i := range collection {
key := iteratee(collection[i])
- duplicated, ok := isDupl[key]
- if !ok {
- isDupl[key] = false
- } else if !duplicated {
- isDupl[key] = true
+ duplicated, seen := isDupl[key]
+ if !duplicated {
+ isDupl[key] = seen
+
+ if seen {
+ duplicates++
+ }
}
}
- result := make(Slice, 0, len(collection)-len(isDupl))
+ result := make(Slice, 0, duplicates)
for i := range collection {
key := iteratee(collection[i])
@@ -259,6 +294,52 @@ func FindDuplicatesBy[T any, U comparable, Slice ~[]T](collection Slice, iterate
return result
}
+// FindDuplicatesByErr returns a slice with the first occurrence of each duplicated element in the collection.
+// The order of result values is determined by the order they occur in the slice. It accepts `iteratee` which is
+// invoked for each element in the slice to generate the criterion by which uniqueness is computed.
+// If the iteratee returns an error, iteration stops immediately and the error is returned with a nil slice.
+func FindDuplicatesByErr[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) (U, error)) (Slice, error) {
+ isDupl := make(map[U]bool, len(collection))
+
+ duplicates := 0
+
+ // First pass: identify duplicates
+ for i := range collection {
+ key, err := iteratee(collection[i])
+ if err != nil {
+ var result Slice
+ return result, err
+ }
+
+ duplicated, seen := isDupl[key]
+ if !duplicated {
+ isDupl[key] = seen
+
+ if seen {
+ duplicates++
+ }
+ }
+ }
+
+ result := make(Slice, 0, duplicates)
+
+ // Second pass: collect first occurrences of duplicates
+ for i := range collection {
+ key, err := iteratee(collection[i])
+ if err != nil {
+ var result Slice
+ return result, err
+ }
+
+ if duplicated := isDupl[key]; duplicated {
+ result = append(result, collection[i])
+ isDupl[key] = false
+ }
+ }
+
+ return result, nil
+}
+
// Min search the minimum value of a collection.
// Returns zero value when the collection is empty.
// Play: https://go.dev/play/p/r6e-Z8JozS8
@@ -311,7 +392,7 @@ func MinIndex[T constraints.Ordered](collection []T) (T, int) {
// MinBy search the minimum value of a collection using the given comparison function.
// If several values of the collection are equal to the smallest value, returns the first such value.
// Returns zero value when the collection is empty.
-func MinBy[T any](collection []T, comparison func(a, b T) bool) T {
+func MinBy[T any](collection []T, less func(a, b T) bool) T {
var mIn T
if len(collection) == 0 {
@@ -323,7 +404,7 @@ func MinBy[T any](collection []T, comparison func(a, b T) bool) T {
for i := 1; i < len(collection); i++ {
item := collection[i]
- if comparison(item, mIn) {
+ if less(item, mIn) {
mIn = item
}
}
@@ -331,10 +412,39 @@ func MinBy[T any](collection []T, comparison func(a, b T) bool) T {
return mIn
}
+// MinByErr search the minimum value of a collection using the given comparison function.
+// If several values of the collection are equal to the smallest value, returns the first such value.
+// Returns zero value and nil error when the collection is empty.
+// If the comparison function returns an error, iteration stops and the error is returned.
+func MinByErr[T any](collection []T, less func(a, b T) (bool, error)) (T, error) {
+ var mIn T
+
+ if len(collection) == 0 {
+ return mIn, nil
+ }
+
+ mIn = collection[0]
+
+ for i := 1; i < len(collection); i++ {
+ item := collection[i]
+
+ isLess, err := less(item, mIn)
+ if err != nil {
+ var zero T
+ return zero, err
+ }
+ if isLess {
+ mIn = item
+ }
+ }
+
+ return mIn, nil
+}
+
// MinIndexBy search the minimum value of a collection using the given comparison function and the index of the minimum value.
// If several values of the collection are equal to the smallest value, returns the first such value.
// Returns (zero value, -1) when the collection is empty.
-func MinIndexBy[T any](collection []T, comparison func(a, b T) bool) (T, int) {
+func MinIndexBy[T any](collection []T, less func(a, b T) bool) (T, int) {
var (
mIn T
index int
@@ -349,7 +459,7 @@ func MinIndexBy[T any](collection []T, comparison func(a, b T) bool) (T, int) {
for i := 1; i < len(collection); i++ {
item := collection[i]
- if comparison(item, mIn) {
+ if less(item, mIn) {
mIn = item
index = i
}
@@ -358,6 +468,40 @@ func MinIndexBy[T any](collection []T, comparison func(a, b T) bool) (T, int) {
return mIn, index
}
+// MinIndexByErr search the minimum value of a collection using the given comparison function and the index of the minimum value.
+// If several values of the collection are equal to the smallest value, returns the first such value.
+// Returns (zero value, -1) when the collection is empty.
+// Comparison function can return an error to stop iteration immediately.
+func MinIndexByErr[T any](collection []T, less func(a, b T) (bool, error)) (T, int, error) {
+ var (
+ mIn T
+ index int
+ )
+
+ if len(collection) == 0 {
+ return mIn, -1, nil
+ }
+
+ mIn = collection[0]
+
+ for i := 1; i < len(collection); i++ {
+ item := collection[i]
+
+ isLess, err := less(item, mIn)
+ if err != nil {
+ var zero T
+ return zero, -1, err
+ }
+
+ if isLess {
+ mIn = item
+ index = i
+ }
+ }
+
+ return mIn, index, nil
+}
+
// Earliest search the minimum time.Time of a collection.
// Returns zero value when the collection is empty.
func Earliest(times ...time.Time) time.Time {
@@ -404,6 +548,37 @@ func EarliestBy[T any](collection []T, iteratee func(item T) time.Time) T {
return earliest
}
+// EarliestByErr search the minimum time.Time of a collection using the given iteratee function.
+// Returns zero value and nil error when the collection is empty.
+// If the iteratee returns an error, iteration stops and the error is returned.
+func EarliestByErr[T any](collection []T, iteratee func(item T) (time.Time, error)) (T, error) {
+ var earliest T
+
+ if len(collection) == 0 {
+ return earliest, nil
+ }
+
+ earliestTime, err := iteratee(collection[0])
+ if err != nil {
+ return earliest, err
+ }
+ earliest = collection[0]
+
+ for i := 1; i < len(collection); i++ {
+ itemTime, err := iteratee(collection[i])
+ if err != nil {
+ return earliest, err
+ }
+
+ if itemTime.Before(earliestTime) {
+ earliest = collection[i]
+ earliestTime = itemTime
+ }
+ }
+
+ return earliest, nil
+}
+
// Max searches the maximum value of a collection.
// Returns zero value when the collection is empty.
// Play: https://go.dev/play/p/r6e-Z8JozS8
@@ -456,7 +631,12 @@ func MaxIndex[T constraints.Ordered](collection []T) (T, int) {
// MaxBy search the maximum value of a collection using the given comparison function.
// If several values of the collection are equal to the greatest value, returns the first such value.
// Returns zero value when the collection is empty.
-func MaxBy[T any](collection []T, comparison func(a, b T) bool) T {
+//
+// Note: the comparison function is inconsistent with most languages, since we use the opposite of the usual convention.
+// See https://github.com/samber/lo/issues/129
+//
+// Play: https://go.dev/play/p/JW1qu-ECwF7
+func MaxBy[T any](collection []T, greater func(a, b T) bool) T {
var mAx T
if len(collection) == 0 {
@@ -468,7 +648,7 @@ func MaxBy[T any](collection []T, comparison func(a, b T) bool) T {
for i := 1; i < len(collection); i++ {
item := collection[i]
- if comparison(item, mAx) {
+ if greater(item, mAx) {
mAx = item
}
}
@@ -476,10 +656,46 @@ func MaxBy[T any](collection []T, comparison func(a, b T) bool) T {
return mAx
}
+// MaxByErr search the maximum value of a collection using the given comparison function.
+// If several values of the collection are equal to the greatest value, returns the first such value.
+// Returns zero value and nil error when the collection is empty.
+// If the comparison function returns an error, iteration stops and the error is returned.
+//
+// Note: the comparison function is inconsistent with most languages, since we use the opposite of the usual convention.
+// See https://github.com/samber/lo/issues/129
+func MaxByErr[T any](collection []T, greater func(a, b T) (bool, error)) (T, error) {
+ var mAx T
+
+ if len(collection) == 0 {
+ return mAx, nil
+ }
+
+ mAx = collection[0]
+
+ for i := 1; i < len(collection); i++ {
+ item := collection[i]
+
+ isGreater, err := greater(item, mAx)
+ if err != nil {
+ return mAx, err
+ }
+ if isGreater {
+ mAx = item
+ }
+ }
+
+ return mAx, nil
+}
+
// MaxIndexBy search the maximum value of a collection using the given comparison function and the index of the maximum value.
// If several values of the collection are equal to the greatest value, returns the first such value.
// Returns (zero value, -1) when the collection is empty.
-func MaxIndexBy[T any](collection []T, comparison func(a, b T) bool) (T, int) {
+//
+// Note: the comparison function is inconsistent with most languages, since we use the opposite of the usual convention.
+// See https://github.com/samber/lo/issues/129
+//
+// Play: https://go.dev/play/p/uaUszc-c9QK
+func MaxIndexBy[T any](collection []T, greater func(a, b T) bool) (T, int) {
var (
mAx T
index int
@@ -494,7 +710,7 @@ func MaxIndexBy[T any](collection []T, comparison func(a, b T) bool) (T, int) {
for i := 1; i < len(collection); i++ {
item := collection[i]
- if comparison(item, mAx) {
+ if greater(item, mAx) {
mAx = item
index = i
}
@@ -503,6 +719,42 @@ func MaxIndexBy[T any](collection []T, comparison func(a, b T) bool) (T, int) {
return mAx, index
}
+// MaxIndexByErr search the maximum value of a collection using the given comparison function and the index of the maximum value.
+// If several values of the collection are equal to the greatest value, returns the first such value.
+// Returns (zero value, -1, nil) when the collection is empty.
+// If the comparison function returns an error, iteration stops and the error is returned.
+//
+// Note: the comparison function is inconsistent with most languages, since we use the opposite of the usual convention.
+// See https://github.com/samber/lo/issues/129
+func MaxIndexByErr[T any](collection []T, greater func(a, b T) (bool, error)) (T, int, error) {
+ var (
+ mAx T
+ index int
+ )
+
+ if len(collection) == 0 {
+ return mAx, -1, nil
+ }
+
+ mAx = collection[0]
+
+ for i := 1; i < len(collection); i++ {
+ item := collection[i]
+
+ isGreater, err := greater(item, mAx)
+ if err != nil {
+ var zero T
+ return zero, -1, err
+ }
+ if isGreater {
+ mAx = item
+ index = i
+ }
+ }
+
+ return mAx, index, nil
+}
+
// Latest search the maximum time.Time of a collection.
// Returns zero value when the collection is empty.
func Latest(times ...time.Time) time.Time {
@@ -549,6 +801,37 @@ func LatestBy[T any](collection []T, iteratee func(item T) time.Time) T {
return latest
}
+// LatestByErr search the maximum time.Time of a collection using the given iteratee function.
+// Returns zero value and nil error when the collection is empty.
+// If the iteratee returns an error, iteration stops and the error is returned.
+func LatestByErr[T any](collection []T, iteratee func(item T) (time.Time, error)) (T, error) {
+ var latest T
+
+ if len(collection) == 0 {
+ return latest, nil
+ }
+
+ latestTime, err := iteratee(collection[0])
+ if err != nil {
+ return latest, err
+ }
+ latest = collection[0]
+
+ for i := 1; i < len(collection); i++ {
+ itemTime, err := iteratee(collection[i])
+ if err != nil {
+ return latest, err
+ }
+
+ if itemTime.After(latestTime) {
+ latest = collection[i]
+ latestTime = itemTime
+ }
+ }
+
+ return latest, nil
+}
+
// First returns the first element of a collection and check for availability of the first element.
// Play: https://go.dev/play/p/ul45Z0y2EFO
func First[T any](collection []T) (T, bool) {
@@ -615,17 +898,22 @@ func LastOr[T any](collection []T, fallback T) T {
// from the end is returned. An error is returned when nth is out of slice bounds.
// Play: https://go.dev/play/p/sHoh88KWt6B
func Nth[T any, N constraints.Integer](collection []T, nth N) (T, error) {
+ value, ok := sliceNth(collection, nth)
+
+ return value, Validate(ok, "nth: %d out of slice bounds", nth)
+}
+
+func sliceNth[T any, N constraints.Integer](collection []T, nth N) (T, bool) {
n := int(nth)
l := len(collection)
if n >= l || -n > l {
- var t T
- return t, fmt.Errorf("nth: %d out of slice bounds", n)
+ return Empty[T](), false
}
if n >= 0 {
- return collection[n], nil
+ return collection[n], true
}
- return collection[l+n], nil
+ return collection[l+n], true
}
// NthOr returns the element at index `nth` of collection.
@@ -633,8 +921,8 @@ func Nth[T any, N constraints.Integer](collection []T, nth N) (T, error) {
// If `nth` is out of slice bounds, it returns the fallback value instead of an error.
// Play: https://go.dev/play/p/sHoh88KWt6B
func NthOr[T any, N constraints.Integer](collection []T, nth N, fallback T) T {
- value, err := Nth(collection, nth)
- if err != nil {
+ value, ok := sliceNth(collection, nth)
+ if !ok {
return fallback
}
return value
@@ -645,11 +933,7 @@ func NthOr[T any, N constraints.Integer](collection []T, nth N, fallback T) T {
// If `nth` is out of slice bounds, it returns the zero value (empty value) for that type.
// Play: https://go.dev/play/p/sHoh88KWt6B
func NthOrEmpty[T any, N constraints.Integer](collection []T, nth N) T {
- value, err := Nth(collection, nth)
- if err != nil {
- var zeroValue T
- return zeroValue
- }
+ value, _ := sliceNth(collection, nth)
return value
}
@@ -660,8 +944,7 @@ type randomIntGenerator func(n int) int
// Sample returns a random item from collection.
// Play: https://go.dev/play/p/vCcSJbh5s6l
func Sample[T any](collection []T) T {
- result := SampleBy(collection, xrand.IntN)
- return result
+ return SampleBy(collection, xrand.IntN)
}
// SampleBy returns a random item from collection, using randomIntGenerator as the random index generator.
@@ -677,29 +960,35 @@ func SampleBy[T any](collection []T, randomIntGenerator randomIntGenerator) T {
// Samples returns N random unique items from collection.
// Play: https://go.dev/play/p/vCcSJbh5s6l
func Samples[T any, Slice ~[]T](collection Slice, count int) Slice {
- results := SamplesBy(collection, count, xrand.IntN)
- return results
+ return SamplesBy(collection, count, xrand.IntN)
}
// SamplesBy returns N random unique items from collection, using randomIntGenerator as the random index generator.
// Play: https://go.dev/play/p/HDmKmMgq0XN
func SamplesBy[T any, Slice ~[]T](collection Slice, count int, randomIntGenerator randomIntGenerator) Slice {
+ if count <= 0 {
+ return Slice{}
+ }
+
size := len(collection)
- cOpy := append(Slice{}, collection...)
+ if size < count {
+ count = size
+ }
- results := Slice{}
+ indexes := Range(size)
+ results := make(Slice, count)
- for i := 0; i < size && i < count; i++ {
- copyLength := size - i
+ for i := range results {
+ n := len(indexes)
- index := randomIntGenerator(size - i)
- results = append(results, cOpy[index])
+ index := randomIntGenerator(n)
+ results[i] = collection[indexes[index]]
- // Removes element.
+ // Removes index.
// It is faster to swap with last element and remove it.
- cOpy[index] = cOpy[copyLength-1]
- cOpy = cOpy[:copyLength-1]
+ indexes[index] = indexes[n-1]
+ indexes = indexes[:n-1]
}
return results
diff --git a/vendor/github.com/samber/lo/intersect.go b/vendor/github.com/samber/lo/intersect.go
index 6c79f9997..469cbbbf3 100644
--- a/vendor/github.com/samber/lo/intersect.go
+++ b/vendor/github.com/samber/lo/intersect.go
@@ -27,8 +27,14 @@ func ContainsBy[T any](collection []T, predicate func(item T) bool) bool {
// Every returns true if all elements of a subset are contained in a collection or if the subset is empty.
// Play: https://go.dev/play/p/W1EvyqY6t9j
func Every[T comparable](collection, subset []T) bool {
- for i := range subset {
- if !Contains(collection, subset[i]) {
+ if len(subset) == 0 {
+ return true
+ }
+
+ seen := Keyify(collection)
+
+ for _, item := range subset {
+ if _, ok := seen[item]; !ok {
return false
}
}
@@ -52,8 +58,13 @@ func EveryBy[T any](collection []T, predicate func(item T) bool) bool {
// If the subset is empty Some returns false.
// Play: https://go.dev/play/p/Lj4ceFkeT9V
func Some[T comparable](collection, subset []T) bool {
- for i := range subset {
- if Contains(collection, subset[i]) {
+ if len(subset) == 0 {
+ return false
+ }
+
+ seen := Keyify(subset)
+ for i := range collection {
+ if _, ok := seen[collection[i]]; ok {
return true
}
}
@@ -77,8 +88,13 @@ func SomeBy[T any](collection []T, predicate func(item T) bool) bool {
// None returns true if no element of a subset is contained in a collection or if the subset is empty.
// Play: https://go.dev/play/p/fye7JsmxzPV
func None[T comparable](collection, subset []T) bool {
- for i := range subset {
- if Contains(collection, subset[i]) {
+ if len(subset) == 0 {
+ return true
+ }
+
+ seen := Keyify(subset)
+ for i := range collection {
+ if _, ok := seen[collection[i]]; ok {
return false
}
}
@@ -98,19 +114,88 @@ func NoneBy[T any](collection []T, predicate func(item T) bool) bool {
return true
}
-// Intersect returns the intersection between two collections.
+// Intersect returns the intersection between collections.
// Play: https://go.dev/play/p/uuElL9X9e58
-func Intersect[T comparable, Slice ~[]T](list1, list2 Slice) Slice {
- result := Slice{}
- seen := map[T]struct{}{}
-
- for i := range list1 {
- seen[list1[i]] = struct{}{}
+func Intersect[T comparable, Slice ~[]T](lists ...Slice) Slice {
+ if len(lists) == 0 {
+ return Slice{}
}
- for i := range list2 {
- if _, ok := seen[list2[i]]; ok {
- result = append(result, list2[i])
+ last := lists[len(lists)-1]
+
+ seen := make(map[T]bool, len(last))
+
+ for _, item := range last {
+ seen[item] = false
+ }
+
+ for i := len(lists) - 2; i > 0 && len(seen) != 0; i-- {
+ for _, item := range lists[i] {
+ if _, ok := seen[item]; ok {
+ seen[item] = true
+ }
+ }
+
+ for k, v := range seen {
+ if v {
+ seen[k] = false
+ } else {
+ delete(seen, k)
+ }
+ }
+ }
+
+ result := make(Slice, 0, len(seen))
+
+ for _, item := range lists[0] {
+ if _, ok := seen[item]; ok {
+ result = append(result, item)
+ delete(seen, item)
+ }
+ }
+
+ return result
+}
+
+// IntersectBy returns the intersection between two collections using a custom key selector function.
+func IntersectBy[T any, K comparable, Slice ~[]T](transform func(T) K, lists ...Slice) Slice {
+ if len(lists) == 0 {
+ return Slice{}
+ }
+
+ last := lists[len(lists)-1]
+
+ seen := make(map[K]bool, len(last))
+
+ for _, item := range last {
+ k := transform(item)
+ seen[k] = false
+ }
+
+ for i := len(lists) - 2; i > 0 && len(seen) != 0; i-- {
+ for _, item := range lists[i] {
+ k := transform(item)
+ if _, ok := seen[k]; ok {
+ seen[k] = true
+ }
+ }
+
+ for k, v := range seen {
+ if v {
+ seen[k] = false
+ } else {
+ delete(seen, k)
+ }
+ }
+ }
+
+ result := make(Slice, 0, len(seen))
+
+ for _, item := range lists[0] {
+ k := transform(item)
+ if _, ok := seen[k]; ok {
+ result = append(result, item)
+ delete(seen, k)
}
}
@@ -125,16 +210,8 @@ func Difference[T comparable, Slice ~[]T](list1, list2 Slice) (Slice, Slice) {
left := Slice{}
right := Slice{}
- seenLeft := map[T]struct{}{}
- seenRight := map[T]struct{}{}
-
- for i := range list1 {
- seenLeft[list1[i]] = struct{}{}
- }
-
- for i := range list2 {
- seenRight[list2[i]] = struct{}{}
- }
+ seenLeft := Keyify(list1)
+ seenRight := Keyify(list2)
for i := range list1 {
if _, ok := seenRight[list1[i]]; !ok {
@@ -179,10 +256,7 @@ func Union[T comparable, Slice ~[]T](lists ...Slice) Slice {
// Without returns a slice excluding all given values.
// Play: https://go.dev/play/p/5j30Ux8TaD0
func Without[T comparable, Slice ~[]T](collection Slice, exclude ...T) Slice {
- excludeMap := make(map[T]struct{}, len(exclude))
- for i := range exclude {
- excludeMap[exclude[i]] = struct{}{}
- }
+ excludeMap := Keyify(exclude)
result := make(Slice, 0, len(collection))
for i := range collection {
@@ -197,10 +271,7 @@ func Without[T comparable, Slice ~[]T](collection Slice, exclude ...T) Slice {
// Returns a new slice containing only the elements whose keys are not in the exclude list.
// Play: https://go.dev/play/p/VgWJOF01NbJ
func WithoutBy[T any, K comparable, Slice ~[]T](collection Slice, iteratee func(item T) K, exclude ...K) Slice {
- excludeMap := make(map[K]struct{}, len(exclude))
- for _, e := range exclude {
- excludeMap[e] = struct{}{}
- }
+ excludeMap := Keyify(exclude)
result := make(Slice, 0, len(collection))
for _, item := range collection {
@@ -211,6 +282,24 @@ func WithoutBy[T any, K comparable, Slice ~[]T](collection Slice, iteratee func(
return result
}
+// WithoutByErr filters a slice by excluding elements whose extracted keys match any in the exclude list.
+// It returns the first error returned by the iteratee.
+func WithoutByErr[T any, K comparable, Slice ~[]T](collection Slice, iteratee func(item T) (K, error), exclude ...K) (Slice, error) {
+ excludeMap := Keyify(exclude)
+
+ result := make(Slice, 0, len(collection))
+ for _, item := range collection {
+ key, err := iteratee(item)
+ if err != nil {
+ return nil, err
+ }
+ if _, ok := excludeMap[key]; !ok {
+ result = append(result, item)
+ }
+ }
+ return result, nil
+}
+
// WithoutEmpty returns a slice excluding zero values.
//
// Deprecated: Use lo.Compact instead.
@@ -220,15 +309,8 @@ func WithoutEmpty[T comparable, Slice ~[]T](collection Slice) Slice {
// WithoutNth returns a slice excluding the nth value.
// Play: https://go.dev/play/p/5g3F9R2H1xL
-func WithoutNth[T comparable, Slice ~[]T](collection Slice, nths ...int) Slice {
- length := len(collection)
-
- toRemove := make(map[int]struct{}, len(nths))
- for i := range nths {
- if nths[i] >= 0 && nths[i] <= length-1 {
- toRemove[nths[i]] = struct{}{}
- }
- }
+func WithoutNth[T any, Slice ~[]T](collection Slice, nths ...int) Slice {
+ toRemove := Keyify(nths)
result := make(Slice, 0, len(collection))
for i := range collection {
diff --git a/vendor/github.com/samber/lo/map.go b/vendor/github.com/samber/lo/map.go
index cd0e9d72d..e5c90e69b 100644
--- a/vendor/github.com/samber/lo/map.go
+++ b/vendor/github.com/samber/lo/map.go
@@ -112,6 +112,22 @@ func PickBy[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, val
return r
}
+// PickByErr returns same map type filtered by given predicate.
+// It returns the first error returned by the predicate.
+func PickByErr[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, value V) (bool, error)) (Map, error) {
+ r := Map{}
+ for k, v := range in {
+ ok, err := predicate(k, v)
+ if err != nil {
+ return nil, err
+ }
+ if ok {
+ r[k] = v
+ }
+ }
+ return r, nil
+}
+
// PickByKeys returns same map type filtered by given keys.
// Play: https://go.dev/play/p/R1imbuci9qU
func PickByKeys[K comparable, V any, Map ~map[K]V](in Map, keys []K) Map {
@@ -128,8 +144,10 @@ func PickByKeys[K comparable, V any, Map ~map[K]V](in Map, keys []K) Map {
// Play: https://go.dev/play/p/1zdzSvbfsJc
func PickByValues[K, V comparable, Map ~map[K]V](in Map, values []V) Map {
r := Map{}
+
+ seen := Keyify(values)
for k, v := range in {
- if Contains(values, v) {
+ if _, ok := seen[v]; ok {
r[k] = v
}
}
@@ -148,6 +166,22 @@ func OmitBy[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, val
return r
}
+// OmitByErr returns same map type filtered by given predicate.
+// It returns the first error returned by the predicate.
+func OmitByErr[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, value V) (bool, error)) (Map, error) {
+ r := Map{}
+ for k, v := range in {
+ ok, err := predicate(k, v)
+ if err != nil {
+ return nil, err
+ }
+ if !ok {
+ r[k] = v
+ }
+ }
+ return r, nil
+}
+
// OmitByKeys returns same map type filtered by given keys.
// Play: https://go.dev/play/p/t1QjCrs-ysk
func OmitByKeys[K comparable, V any, Map ~map[K]V](in Map, keys []K) Map {
@@ -165,11 +199,14 @@ func OmitByKeys[K comparable, V any, Map ~map[K]V](in Map, keys []K) Map {
// Play: https://go.dev/play/p/9UYZi-hrs8j
func OmitByValues[K, V comparable, Map ~map[K]V](in Map, values []V) Map {
r := Map{}
+
+ seen := Keyify(values)
for k, v := range in {
- if !Contains(values, v) {
+ if _, ok := seen[v]; !ok {
r[k] = v
}
}
+
return r
}
@@ -259,12 +296,7 @@ func ChunkEntries[K comparable, V any](m map[K]V, size int) []map[K]V {
return []map[K]V{}
}
- chunksNum := count / size
- if count%size != 0 {
- chunksNum++
- }
-
- result := make([]map[K]V, 0, chunksNum)
+ result := make([]map[K]V, 0, ((count-1)/size)+1)
for k, v := range m {
if len(result) == 0 || len(result[len(result)-1]) == size {
@@ -289,6 +321,22 @@ func MapKeys[K comparable, V any, R comparable](in map[K]V, iteratee func(value
return result
}
+// MapKeysErr manipulates map keys and transforms it to a map of another type.
+// It returns the first error returned by the iteratee.
+func MapKeysErr[K comparable, V any, R comparable](in map[K]V, iteratee func(value V, key K) (R, error)) (map[R]V, error) {
+ result := make(map[R]V, len(in))
+
+ for k, v := range in {
+ r, err := iteratee(v, k)
+ if err != nil {
+ return nil, err
+ }
+ result[r] = v
+ }
+
+ return result, nil
+}
+
// MapValues manipulates map values and transforms it to a map of another type.
// Play: https://go.dev/play/p/T_8xAfvcf0W
func MapValues[K comparable, V, R any](in map[K]V, iteratee func(value V, key K) R) map[K]R {
@@ -301,6 +349,22 @@ func MapValues[K comparable, V, R any](in map[K]V, iteratee func(value V, key K)
return result
}
+// MapValuesErr manipulates map values and transforms it to a map of another type.
+// It returns the first error returned by the iteratee.
+func MapValuesErr[K comparable, V, R any](in map[K]V, iteratee func(value V, key K) (R, error)) (map[K]R, error) {
+ result := make(map[K]R, len(in))
+
+ for k, v := range in {
+ r, err := iteratee(v, k)
+ if err != nil {
+ return nil, err
+ }
+ result[k] = r
+ }
+
+ return result, nil
+}
+
// MapEntries manipulates map entries and transforms it to a map of another type.
// Play: https://go.dev/play/p/VuvNQzxKimT
func MapEntries[K1 comparable, V1 any, K2 comparable, V2 any](in map[K1]V1, iteratee func(key K1, value V1) (K2, V2)) map[K2]V2 {
@@ -314,6 +378,22 @@ func MapEntries[K1 comparable, V1 any, K2 comparable, V2 any](in map[K1]V1, iter
return result
}
+// MapEntriesErr manipulates map entries and transforms it to a map of another type.
+// It returns the first error returned by the iteratee.
+func MapEntriesErr[K1 comparable, V1 any, K2 comparable, V2 any](in map[K1]V1, iteratee func(key K1, value V1) (K2, V2, error)) (map[K2]V2, error) {
+ result := make(map[K2]V2, len(in))
+
+ for k1 := range in {
+ k2, v2, err := iteratee(k1, in[k1])
+ if err != nil {
+ return nil, err
+ }
+ result[k2] = v2
+ }
+
+ return result, nil
+}
+
// MapToSlice transforms a map into a slice based on specified iteratee.
// Play: https://go.dev/play/p/ZuiCZpDt6LD
func MapToSlice[K comparable, V, R any](in map[K]V, iteratee func(key K, value V) R) []R {
@@ -326,6 +406,22 @@ func MapToSlice[K comparable, V, R any](in map[K]V, iteratee func(key K, value V
return result
}
+// MapToSliceErr transforms a map into a slice based on specified iteratee.
+// It returns the first error returned by the iteratee.
+func MapToSliceErr[K comparable, V, R any](in map[K]V, iteratee func(key K, value V) (R, error)) ([]R, error) {
+ result := make([]R, 0, len(in))
+
+ for k, v := range in {
+ r, err := iteratee(k, v)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, r)
+ }
+
+ return result, nil
+}
+
// FilterMapToSlice transforms a map into a slice based on specified iteratee.
// The iteratee returns a value and a boolean. If the boolean is true, the value is added to the result slice.
// If the boolean is false, the value is not added to the result slice.
@@ -343,6 +439,27 @@ func FilterMapToSlice[K comparable, V, R any](in map[K]V, iteratee func(key K, v
return result
}
+// FilterMapToSliceErr transforms a map into a slice based on specified iteratee.
+// The iteratee returns a value, a boolean, and an error. If the boolean is true, the value is added to the result slice.
+// If the boolean is false, the value is not added to the result slice.
+// If an error is returned, iteration stops immediately and returns the error.
+// The order of the keys in the input map is not specified and the order of the keys in the output slice is not guaranteed.
+func FilterMapToSliceErr[K comparable, V, R any](in map[K]V, iteratee func(key K, value V) (R, bool, error)) ([]R, error) {
+ result := make([]R, 0, len(in))
+
+ for k, v := range in {
+ r, ok, err := iteratee(k, v)
+ if err != nil {
+ return nil, err
+ }
+ if ok {
+ result = append(result, r)
+ }
+ }
+
+ return result, nil
+}
+
// FilterKeys transforms a map into a slice based on predicate returns true for specific elements.
// It is a mix of lo.Filter() and lo.Keys().
// Play: https://go.dev/play/p/OFlKXlPrBAe
@@ -372,3 +489,45 @@ func FilterValues[K comparable, V any](in map[K]V, predicate func(key K, value V
return result
}
+
+// FilterKeysErr transforms a map into a slice of keys based on predicate that can return an error.
+// It is a mix of lo.Filter() and lo.Keys() with error handling.
+// If the predicate returns true, the key is added to the result slice.
+// If the predicate returns an error, iteration stops immediately and returns the error.
+// The order of the keys in the input map is not specified.
+func FilterKeysErr[K comparable, V any](in map[K]V, predicate func(key K, value V) (bool, error)) ([]K, error) {
+ result := make([]K, 0)
+
+ for k, v := range in {
+ ok, err := predicate(k, v)
+ if err != nil {
+ return nil, err
+ }
+ if ok {
+ result = append(result, k)
+ }
+ }
+
+ return result, nil
+}
+
+// FilterValuesErr transforms a map into a slice of values based on predicate that can return an error.
+// It is a mix of lo.Filter() and lo.Values() with error handling.
+// If the predicate returns true, the value is added to the result slice.
+// If the predicate returns an error, iteration stops immediately and returns the error.
+// The order of the keys in the input map is not specified.
+func FilterValuesErr[K comparable, V any](in map[K]V, predicate func(key K, value V) (bool, error)) ([]V, error) {
+ result := make([]V, 0)
+
+ for k, v := range in {
+ ok, err := predicate(k, v)
+ if err != nil {
+ return nil, err
+ }
+ if ok {
+ result = append(result, v)
+ }
+ }
+
+ return result, nil
+}
diff --git a/vendor/github.com/samber/lo/math.go b/vendor/github.com/samber/lo/math.go
index 9342b59e7..f08f22789 100644
--- a/vendor/github.com/samber/lo/math.go
+++ b/vendor/github.com/samber/lo/math.go
@@ -1,15 +1,17 @@
package lo
import (
+ "math"
+
"github.com/samber/lo/internal/constraints"
)
// Range creates a slice of numbers (positive and/or negative) with given length.
// Play: https://go.dev/play/p/0r6VimXAi9H
func Range(elementNum int) []int {
- length := If(elementNum < 0, -elementNum).Else(elementNum)
+ step := Ternary(elementNum < 0, -1, 1)
+ length := elementNum * step
result := make([]int, length)
- step := If(elementNum < 0, -1).Else(1)
for i, j := 0, 0; i < length; i, j = i+1, j+step {
result[i] = j
}
@@ -19,9 +21,9 @@ func Range(elementNum int) []int {
// RangeFrom creates a slice of numbers from start with specified length.
// Play: https://go.dev/play/p/0r6VimXAi9H
func RangeFrom[T constraints.Integer | constraints.Float](start T, elementNum int) []T {
- length := If(elementNum < 0, -elementNum).Else(elementNum)
+ step := Ternary(elementNum < 0, -1, 1)
+ length := elementNum * step
result := make([]T, length)
- step := If(elementNum < 0, -1).Else(1)
for i, j := 0, start; i < length; i, j = i+1, j+T(step) {
result[i] = j
}
@@ -32,22 +34,32 @@ func RangeFrom[T constraints.Integer | constraints.Float](start T, elementNum in
// step set to zero will return an empty slice.
// Play: https://go.dev/play/p/0r6VimXAi9H
func RangeWithSteps[T constraints.Integer | constraints.Float](start, end, step T) []T {
- result := []T{}
if start == end || step == 0 {
- return result
+ return []T{}
}
+
+ capacity := func(count, delta T) int {
+ // Use math.Ceil instead of (count-1)/delta+1 because integer division
+ // fails for floats (e.g., 5.5/2.5=2.2 → ceil=3, not 2).
+ return int(math.Ceil(float64(count) / float64(delta)))
+ }
+
if start < end {
if step < 0 {
- return result
+ return []T{}
}
+
+ result := make([]T, 0, capacity(end-start, step))
for i := start; i < end; i += step {
result = append(result, i)
}
return result
}
if step > 0 {
- return result
+ return []T{}
}
+
+ result := make([]T, 0, capacity(start-end, -step))
for i := start; i > end; i += step {
result = append(result, i)
}
@@ -85,17 +97,24 @@ func SumBy[T any, R constraints.Float | constraints.Integer | constraints.Comple
return sum
}
+// SumByErr summarizes the values in a collection using the given return value from the iteration function.
+// If the iteratee returns an error, iteration stops and the error is returned.
+// If collection is empty 0 and nil error are returned.
+func SumByErr[T any, R constraints.Float | constraints.Integer | constraints.Complex](collection []T, iteratee func(item T) (R, error)) (R, error) {
+ var sum R
+ for i := range collection {
+ v, err := iteratee(collection[i])
+ if err != nil {
+ return sum, err
+ }
+ sum += v
+ }
+ return sum, nil
+}
+
// Product gets the product of the values in a collection. If collection is empty 1 is returned.
// Play: https://go.dev/play/p/2_kjM_smtAH
func Product[T constraints.Float | constraints.Integer | constraints.Complex](collection []T) T {
- if collection == nil {
- return 1
- }
-
- if len(collection) == 0 {
- return 1
- }
-
var product T = 1
for i := range collection {
product *= collection[i]
@@ -106,14 +125,6 @@ func Product[T constraints.Float | constraints.Integer | constraints.Complex](co
// ProductBy summarizes the values in a collection using the given return value from the iteration function. If collection is empty 1 is returned.
// Play: https://go.dev/play/p/wadzrWr9Aer
func ProductBy[T any, R constraints.Float | constraints.Integer | constraints.Complex](collection []T, iteratee func(item T) R) R {
- if collection == nil {
- return 1
- }
-
- if len(collection) == 0 {
- return 1
- }
-
var product R = 1
for i := range collection {
product *= iteratee(collection[i])
@@ -121,6 +132,21 @@ func ProductBy[T any, R constraints.Float | constraints.Integer | constraints.Co
return product
}
+// ProductByErr summarizes the values in a collection using the given return value from the iteration function.
+// If the iteratee returns an error, iteration stops and the error is returned.
+// If collection is empty 1 and nil error are returned.
+func ProductByErr[T any, R constraints.Float | constraints.Integer | constraints.Complex](collection []T, iteratee func(item T) (R, error)) (R, error) {
+ var product R = 1
+ for i := range collection {
+ v, err := iteratee(collection[i])
+ if err != nil {
+ return product, err
+ }
+ product *= v
+ }
+ return product, nil
+}
+
// Mean calculates the mean of a collection of numbers.
// Play: https://go.dev/play/p/tPURSuteUsP
func Mean[T constraints.Float | constraints.Integer](collection []T) T {
@@ -143,6 +169,21 @@ func MeanBy[T any, R constraints.Float | constraints.Integer](collection []T, it
return sum / length
}
+// MeanByErr calculates the mean of a collection of numbers using the given return value from the iteration function.
+// If the iteratee returns an error, iteration stops and the error is returned.
+// If collection is empty 0 and nil error are returned.
+func MeanByErr[T any, R constraints.Float | constraints.Integer](collection []T, iteratee func(item T) (R, error)) (R, error) {
+ length := R(len(collection))
+ if length == 0 {
+ return 0, nil
+ }
+ sum, err := SumByErr(collection, iteratee)
+ if err != nil {
+ return 0, err
+ }
+ return sum / length, nil
+}
+
// Mode returns the mode (most frequent value) of a collection.
// If multiple values have the same highest frequency, then multiple values are returned.
// If the collection is empty, then the zero value of T is returned.
diff --git a/vendor/github.com/samber/lo/mutable/slice.go b/vendor/github.com/samber/lo/mutable/slice.go
index f3412c103..4b8e916c4 100644
--- a/vendor/github.com/samber/lo/mutable/slice.go
+++ b/vendor/github.com/samber/lo/mutable/slice.go
@@ -10,9 +10,9 @@ import "github.com/samber/lo/internal/xrand"
// Play: https://go.dev/play/p/0jY3Z0B7O_5
func Filter[T any, Slice ~[]T](collection Slice, predicate func(item T) bool) Slice {
j := 0
- for _, item := range collection {
- if predicate(item) {
- collection[j] = item
+ for i := range collection {
+ if predicate(collection[i]) {
+ collection[j] = collection[i]
j++
}
}
@@ -26,9 +26,9 @@ func Filter[T any, Slice ~[]T](collection Slice, predicate func(item T) bool) Sl
// Note that the order of elements in the original slice is preserved in the output.
func FilterI[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) bool) Slice {
j := 0
- for i, item := range collection {
- if predicate(item, i) {
- collection[j] = item
+ for i := range collection {
+ if predicate(collection[i], i) {
+ collection[j] = collection[i]
j++
}
}
@@ -38,17 +38,17 @@ func FilterI[T any, Slice ~[]T](collection Slice, predicate func(item T, index i
// Map is a generic function that modifies the input slice in-place to contain the result of applying the provided
// function to each element of the slice. The function returns the modified slice, which has the same length as the original.
// Play: https://go.dev/play/p/0jY3Z0B7O_5
-func Map[T any, Slice ~[]T](collection Slice, fn func(item T) T) {
+func Map[T any, Slice ~[]T](collection Slice, transform func(item T) T) {
for i := range collection {
- collection[i] = fn(collection[i])
+ collection[i] = transform(collection[i])
}
}
// MapI is a generic function that modifies the input slice in-place to contain the result of applying the provided
// function to each element of the slice. The function returns the modified slice, which has the same length as the original.
-func MapI[T any, Slice ~[]T](collection Slice, fn func(item T, index int) T) {
+func MapI[T any, Slice ~[]T](collection Slice, transform func(item T, index int) T) {
for i := range collection {
- collection[i] = fn(collection[i], i)
+ collection[i] = transform(collection[i], i)
}
}
diff --git a/vendor/github.com/samber/lo/retry.go b/vendor/github.com/samber/lo/retry.go
index e8b98c477..a4779f60f 100644
--- a/vendor/github.com/samber/lo/retry.go
+++ b/vendor/github.com/samber/lo/retry.go
@@ -315,10 +315,6 @@ func (th *throttleBy[T]) throttledFunc(key T) {
th.mu.Lock()
defer th.mu.Unlock()
- if _, ok := th.count[key]; !ok {
- th.count[key] = 0
- }
-
if th.count[key] < th.countLimit {
th.count[key]++
@@ -361,7 +357,7 @@ func NewThrottleWithCount(interval time.Duration, count int, f ...func()) (throt
}
})
- throttleFn, reset := NewThrottleByWithCount[struct{}](interval, count, callbacks...)
+ throttleFn, reset := NewThrottleByWithCount(interval, count, callbacks...)
return func() {
throttleFn(struct{}{})
}, reset
@@ -371,7 +367,7 @@ func NewThrottleWithCount(interval time.Duration, count int, f ...func()) (throt
// This returns 2 functions, First one is throttled function and Second one is a function to reset interval.
// Play: https://go.dev/play/p/0Wv6oX7dHdC
func NewThrottleBy[T comparable](interval time.Duration, f ...func(key T)) (throttle func(key T), reset func()) {
- return NewThrottleByWithCount[T](interval, 1, f...)
+ return NewThrottleByWithCount(interval, 1, f...)
}
// NewThrottleByWithCount is NewThrottleBy with count limit, throttled function will be invoked count times in every interval.
diff --git a/vendor/github.com/samber/lo/slice.go b/vendor/github.com/samber/lo/slice.go
index d35d27669..a53da54f8 100644
--- a/vendor/github.com/samber/lo/slice.go
+++ b/vendor/github.com/samber/lo/slice.go
@@ -21,32 +21,66 @@ func Filter[T any, Slice ~[]T](collection Slice, predicate func(item T, index in
return result
}
+// FilterErr iterates over elements of collection, returning a slice of all elements predicate returns true for.
+// If the predicate returns an error, iteration stops immediately and returns the error.
+// Play: https://go.dev/play/p/Apjg3WeSi7K
+func FilterErr[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) (bool, error)) (Slice, error) {
+ result := make(Slice, 0, len(collection))
+
+ for i := range collection {
+ ok, err := predicate(collection[i], i)
+ if err != nil {
+ return nil, err
+ }
+ if ok {
+ result = append(result, collection[i])
+ }
+ }
+
+ return result, nil
+}
+
// Map manipulates a slice and transforms it to a slice of another type.
// Play: https://go.dev/play/p/OkPcYAhBo0D
-func Map[T, R any](collection []T, iteratee func(item T, index int) R) []R {
+func Map[T, R any](collection []T, transform func(item T, index int) R) []R {
result := make([]R, len(collection))
for i := range collection {
- result[i] = iteratee(collection[i], i)
+ result[i] = transform(collection[i], i)
}
return result
}
+// MapErr manipulates a slice and transforms it to a slice of another type.
+// It returns the first error returned by the transform function.
+func MapErr[T, R any](collection []T, transform func(item T, index int) (R, error)) ([]R, error) {
+ result := make([]R, len(collection))
+
+ for i := range collection {
+ r, err := transform(collection[i], i)
+ if err != nil {
+ return nil, err
+ }
+ result[i] = r
+ }
+
+ return result, nil
+}
+
// UniqMap manipulates a slice and transforms it to a slice of another type with unique values.
// Play: https://go.dev/play/p/fygzLBhvUdB
-func UniqMap[T any, R comparable](collection []T, iteratee func(item T, index int) R) []R {
- result := make([]R, 0, len(collection))
+func UniqMap[T any, R comparable](collection []T, transform func(item T, index int) R) []R {
seen := make(map[R]struct{}, len(collection))
for i := range collection {
- r := iteratee(collection[i], i)
+ r := transform(collection[i], i)
if _, ok := seen[r]; !ok {
- result = append(result, r)
seen[r] = struct{}{}
}
}
- return result
+
+ return Keys(seen)
}
// FilterMap returns a slice obtained after both filtering and mapping using the given callback function.
@@ -71,16 +105,34 @@ func FilterMap[T, R any](collection []T, callback func(item T, index int) (R, bo
// The transform function can either return a slice or a `nil`, and in the `nil` case
// no value is added to the final slice.
// Play: https://go.dev/play/p/pFCF5WVB225
-func FlatMap[T, R any](collection []T, iteratee func(item T, index int) []R) []R {
+func FlatMap[T, R any](collection []T, transform func(item T, index int) []R) []R {
result := make([]R, 0, len(collection))
for i := range collection {
- result = append(result, iteratee(collection[i], i)...)
+ result = append(result, transform(collection[i], i)...)
}
return result
}
+// FlatMapErr manipulates a slice and transforms and flattens it to a slice of another type.
+// The transform function can either return a slice or a `nil`, and in the `nil` case
+// no value is added to the final slice.
+// It returns the first error returned by the transform function.
+func FlatMapErr[T, R any](collection []T, transform func(item T, index int) ([]R, error)) ([]R, error) {
+ result := make([]R, 0, len(collection))
+
+ for i := range collection {
+ r, err := transform(collection[i], i)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, r...)
+ }
+
+ return result, nil
+}
+
// Reduce reduces collection to a value which is the accumulated result of running each element in collection
// through accumulator, where each successive invocation is supplied the return value of the previous.
// Play: https://go.dev/play/p/CgHYNUpOd1I
@@ -92,6 +144,22 @@ func Reduce[T, R any](collection []T, accumulator func(agg R, item T, index int)
return initial
}
+// ReduceErr reduces collection to a value which is the accumulated result of running each element in collection
+// through accumulator, where each successive invocation is supplied the return value of the previous.
+// It returns the first error returned by the accumulator function.
+func ReduceErr[T, R any](collection []T, accumulator func(agg R, item T, index int) (R, error), initial R) (R, error) {
+ for i := range collection {
+ result, err := accumulator(initial, collection[i], i)
+ if err != nil {
+ var zero R
+ return zero, err
+ }
+ initial = result
+ }
+
+ return initial, nil
+}
+
// ReduceRight is like Reduce except that it iterates over elements of collection from right to left.
// Play: https://go.dev/play/p/Fq3W70l7wXF
func ReduceRight[T, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) R {
@@ -102,20 +170,35 @@ func ReduceRight[T, R any](collection []T, accumulator func(agg R, item T, index
return initial
}
-// ForEach iterates over elements of collection and invokes iteratee for each element.
+// ReduceRightErr is like ReduceRight except that the accumulator function can return an error.
+// It returns the first error returned by the accumulator function.
+func ReduceRightErr[T, R any](collection []T, accumulator func(agg R, item T, index int) (R, error), initial R) (R, error) {
+ for i := len(collection) - 1; i >= 0; i-- {
+ result, err := accumulator(initial, collection[i], i)
+ if err != nil {
+ var zero R
+ return zero, err
+ }
+ initial = result
+ }
+
+ return initial, nil
+}
+
+// ForEach iterates over elements of collection and invokes callback for each element.
// Play: https://go.dev/play/p/oofyiUPRf8t
-func ForEach[T any](collection []T, iteratee func(item T, index int)) {
+func ForEach[T any](collection []T, callback func(item T, index int)) {
for i := range collection {
- iteratee(collection[i], i)
+ callback(collection[i], i)
}
}
-// ForEachWhile iterates over elements of collection and invokes iteratee for each element
+// ForEachWhile iterates over elements of collection and invokes predicate for each element
// collection return value decide to continue or break, like do while().
// Play: https://go.dev/play/p/QnLGt35tnow
-func ForEachWhile[T any](collection []T, iteratee func(item T, index int) bool) {
+func ForEachWhile[T any](collection []T, predicate func(item T, index int) bool) {
for i := range collection {
- if !iteratee(collection[i], i) {
+ if !predicate(collection[i], i) {
break
}
}
@@ -175,6 +258,31 @@ func UniqBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(ite
return result
}
+// UniqByErr returns a duplicate-free version of a slice, in which only the first occurrence of each element is kept.
+// The order of result values is determined by the order they occur in the slice. It accepts `iteratee` which is
+// invoked for each element in the slice to generate the criterion by which uniqueness is computed.
+// It returns the first error returned by the iteratee function.
+func UniqByErr[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) (U, error)) (Slice, error) {
+ result := make(Slice, 0, len(collection))
+ seen := make(map[U]struct{}, len(collection))
+
+ for i := range collection {
+ key, err := iteratee(collection[i])
+ if err != nil {
+ return nil, err
+ }
+
+ if _, ok := seen[key]; ok {
+ continue
+ }
+
+ seen[key] = struct{}{}
+ result = append(result, collection[i])
+ }
+
+ return result, nil
+}
+
// GroupBy returns an object composed of keys generated from the results of running each element of collection through iteratee.
// Play: https://go.dev/play/p/XnQBd_v6brd
func GroupBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) map[U]Slice {
@@ -189,13 +297,30 @@ func GroupBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(it
return result
}
-// GroupByMap returns an object composed of keys generated from the results of running each element of collection through iteratee.
+// GroupByErr returns an object composed of keys generated from the results of running each element of collection through iteratee.
+// It returns the first error returned by the iteratee function.
+func GroupByErr[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) (U, error)) (map[U]Slice, error) {
+ result := map[U]Slice{}
+
+ for i := range collection {
+ key, err := iteratee(collection[i])
+ if err != nil {
+ return nil, err
+ }
+
+ result[key] = append(result[key], collection[i])
+ }
+
+ return result, nil
+}
+
+// GroupByMap returns an object composed of keys generated from the results of running each element of collection through transform.
// Play: https://go.dev/play/p/iMeruQ3_W80
-func GroupByMap[T any, K comparable, V any](collection []T, iteratee func(item T) (K, V)) map[K][]V {
+func GroupByMap[T any, K comparable, V any](collection []T, transform func(item T) (K, V)) map[K][]V {
result := map[K][]V{}
for i := range collection {
- k, v := iteratee(collection[i])
+ k, v := transform(collection[i])
result[k] = append(result[k], v)
}
@@ -203,6 +328,23 @@ func GroupByMap[T any, K comparable, V any](collection []T, iteratee func(item T
return result
}
+// GroupByMapErr returns an object composed of keys generated from the results of running each element of collection through transform.
+// It returns the first error returned by the transform function.
+func GroupByMapErr[T any, K comparable, V any](collection []T, transform func(item T) (K, V, error)) (map[K][]V, error) {
+ result := map[K][]V{}
+
+ for i := range collection {
+ k, v, err := transform(collection[i])
+ if err != nil {
+ return nil, err
+ }
+
+ result[k] = append(result[k], v)
+ }
+
+ return result, nil
+}
+
// Chunk returns a slice of elements split into groups of length size. If the slice can't be split evenly,
// the final chunk will be the remaining elements.
// Play: https://go.dev/play/p/kEMkFbdu85g
@@ -245,13 +387,12 @@ func PartitionBy[T any, K comparable, Slice ~[]T](collection Slice, iteratee fun
key := iteratee(collection[i])
resultIndex, ok := seen[key]
- if !ok {
- resultIndex = len(result)
- seen[key] = resultIndex
- result = append(result, Slice{})
+ if ok {
+ result[resultIndex] = append(result[resultIndex], collection[i])
+ } else {
+ seen[key] = len(result)
+ result = append(result, Slice{collection[i]})
}
-
- result[resultIndex] = append(result[resultIndex], collection[i])
}
return result
@@ -261,7 +402,34 @@ func PartitionBy[T any, K comparable, Slice ~[]T](collection Slice, iteratee fun
// return Values[K, []T](groups)
}
+// PartitionByErr partitions a slice into groups determined by a key computed from each element.
+// The order of the partitions is determined by the order they occur in collection. The grouping
+// is generated from the results of running each element of collection through iteratee.
+// It returns the first error returned by the iteratee function.
+func PartitionByErr[T any, K comparable, Slice ~[]T](collection Slice, iteratee func(item T) (K, error)) ([]Slice, error) {
+ result := []Slice{}
+ seen := map[K]int{}
+
+ for i := range collection {
+ key, err := iteratee(collection[i])
+ if err != nil {
+ return nil, err
+ }
+
+ resultIndex, ok := seen[key]
+ if ok {
+ result[resultIndex] = append(result[resultIndex], collection[i])
+ } else {
+ seen[key] = len(result)
+ result = append(result, Slice{collection[i]})
+ }
+ }
+
+ return result, nil
+}
+
// Flatten returns a slice a single level deep.
+// See also: Concat
// Play: https://go.dev/play/p/rbp9ORaMpjw
func Flatten[T any, Slice ~[]T](collection []Slice) Slice {
totalLen := 0
@@ -277,6 +445,50 @@ func Flatten[T any, Slice ~[]T](collection []Slice) Slice {
return result
}
+// Concat returns a new slice containing all the elements in collections. Concat conserves the order of the elements.
+// See also: Flatten, Union.
+func Concat[T any, Slice ~[]T](collections ...Slice) Slice {
+ return Flatten(collections)
+}
+
+// Window creates a slice of sliding windows of a given size.
+// Each window overlaps with the previous one by size-1 elements.
+// This is equivalent to Sliding(collection, size, 1).
+func Window[T any, Slice ~[]T](collection Slice, size int) []Slice {
+ if size <= 0 {
+ panic("lo.Window: size must be greater than 0")
+ }
+ return Sliding(collection, size, 1)
+}
+
+// Sliding creates a slice of sliding windows of a given size with a given step.
+// If step is equal to size, windows don't overlap (similar to Chunk).
+// If step is less than size, windows overlap.
+func Sliding[T any, Slice ~[]T](collection Slice, size, step int) []Slice {
+ if size <= 0 {
+ panic("lo.Sliding: size must be greater than 0")
+ }
+
+ if step <= 0 {
+ panic("lo.Sliding: step must be greater than 0")
+ }
+
+ n := len(collection) - size
+ if n < 0 {
+ return []Slice{}
+ }
+
+ result := make([]Slice, 0, n/step+1)
+
+ for i := 0; i <= n; i += step {
+ window := make(Slice, size)
+ copy(window, collection[i:i+size])
+ result = append(result, window)
+ }
+
+ return result
+}
+
// Interleave round-robin alternating input slices and sequentially appending value at index into result.
// Play: https://go.dev/play/p/-RJkTLQEDVt
func Interleave[T any, Slice ~[]T](collections ...Slice) Slice {
@@ -359,16 +571,32 @@ func Repeat[T Clonable[T]](count int, initial T) []T {
// RepeatBy builds a slice with values returned by N calls of callback.
// Play: https://go.dev/play/p/ozZLCtX_hNU
-func RepeatBy[T any](count int, predicate func(index int) T) []T {
+func RepeatBy[T any](count int, callback func(index int) T) []T {
result := make([]T, 0, count)
for i := 0; i < count; i++ {
- result = append(result, predicate(i))
+ result = append(result, callback(i))
}
return result
}
+// RepeatByErr builds a slice with values returned by N calls of callback.
+// It returns the first error returned by the callback function.
+func RepeatByErr[T any](count int, callback func(index int) (T, error)) ([]T, error) {
+ result := make([]T, 0, count)
+
+ for i := 0; i < count; i++ {
+ r, err := callback(i)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, r)
+ }
+
+ return result, nil
+}
+
// KeyBy transforms a slice or a slice of structs to a map based on a pivot callback.
// Play: https://go.dev/play/p/ccUiUL_Lnel
func KeyBy[K comparable, V any](collection []V, iteratee func(item V) K) map[K]V {
@@ -382,6 +610,23 @@ func KeyBy[K comparable, V any](collection []V, iteratee func(item V) K) map[K]V
return result
}
+// KeyByErr transforms a slice or a slice of structs to a map based on a pivot callback to compute keys.
+// Iteratee can return an error to stop iteration immediately.
+// Play: https://go.dev/play/p/ccUiUL_Lnel
+func KeyByErr[K comparable, V any](collection []V, iteratee func(item V) (K, error)) (map[K]V, error) {
+ result := make(map[K]V, len(collection))
+
+ for i := range collection {
+ k, err := iteratee(collection[i])
+ if err != nil {
+ return nil, err
+ }
+ result[k] = collection[i]
+ }
+
+ return result, nil
+}
+
// Associate returns a map containing key-value pairs provided by transform function applied to elements of the given slice.
// If any of two pairs have the same key the last one gets added to the map.
// The order of keys in returned map is not specified and is not guaranteed to be the same from the original slice.
@@ -525,35 +770,110 @@ func DropRightWhile[T any, Slice ~[]T](collection Slice, predicate func(item T)
return append(result, collection[:i+1]...)
}
+// Take takes the first n elements from a slice.
+func Take[T any, Slice ~[]T](collection Slice, n int) Slice {
+ if n < 0 {
+ panic("lo.Take: n must not be negative")
+ }
+
+ if n == 0 {
+ return make(Slice, 0)
+ }
+
+ size := len(collection)
+ if size == 0 {
+ return make(Slice, 0)
+ }
+
+ if n >= size {
+ result := make(Slice, size)
+ copy(result, collection)
+ return result
+ }
+
+ result := make(Slice, n)
+ copy(result, collection)
+ return result
+}
+
+// TakeWhile takes elements from the beginning of a slice while the predicate returns true.
+func TakeWhile[T any, Slice ~[]T](collection Slice, predicate func(item T) bool) Slice {
+ i := 0
+ for ; i < len(collection); i++ {
+ if !predicate(collection[i]) {
+ break
+ }
+ }
+
+ result := make(Slice, i)
+ copy(result, collection[:i])
+ return result
+}
+
// DropByIndex drops elements from a slice by the index.
// A negative index will drop elements from the end of the slice.
// Play: https://go.dev/play/p/bPIH4npZRxS
func DropByIndex[T any, Slice ~[]T](collection Slice, indexes ...int) Slice {
initialSize := len(collection)
if initialSize == 0 {
+ return Slice{}
+ }
+
+ // do not change the input
+ indexes = append(make([]int, 0, len(indexes)), indexes...)
+
+ for i, index := range indexes {
+ if index < 0 {
+ indexes[i] += initialSize
+ }
+ }
+
+ sort.Ints(indexes)
+
+ prev := -1
+ indexes = mutable.Filter(indexes, func(index int) bool {
+ ok := index != prev && // uniq
+ uint(index) < uint(initialSize) // in range
+
+ prev = index
+ return ok
+ })
+
+ result := make(Slice, 0, initialSize-len(indexes))
+
+ i := 0
+ for _, index := range indexes {
+ result = append(result, collection[i:index]...)
+ i = index + 1
+ }
+
+ return append(result, collection[i:]...)
+}
+
+// TakeFilter filters elements and takes the first n elements that match the predicate.
+// Equivalent to calling Take(Filter(...)), but more efficient as it stops after finding n matches.
+func TakeFilter[T any, Slice ~[]T](collection Slice, n int, predicate func(item T, index int) bool) Slice {
+ if n < 0 {
+ panic("lo.TakeFilter: n must not be negative")
+ }
+
+ if n == 0 {
return make(Slice, 0)
}
- for i := range indexes {
- if indexes[i] < 0 {
- indexes[i] = initialSize + indexes[i]
+ result := make(Slice, 0, n)
+ count := 0
+
+ for i := range collection {
+ if predicate(collection[i], i) {
+ result = append(result, collection[i])
+ count++
+ if count >= n {
+ break
+ }
}
}
- indexes = Uniq(indexes)
- sort.Ints(indexes)
-
- result := make(Slice, 0, initialSize)
- result = append(result, collection...)
-
- for i := range indexes {
- if indexes[i]-i < 0 || indexes[i]-i >= initialSize-i {
- continue
- }
-
- result = append(result[:indexes[i]-i], result[indexes[i]-i+1:]...)
- }
-
return result
}
@@ -571,6 +891,25 @@ func Reject[T any, Slice ~[]T](collection Slice, predicate func(item T, index in
return result
}
+// RejectErr is the opposite of FilterErr, this method returns the elements of collection that predicate does not return true for.
+// If the predicate returns an error, iteration stops immediately and returns the error.
+// Play: https://go.dev/play/p/pFCF5WVB225
+func RejectErr[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) (bool, error)) (Slice, error) {
+ result := Slice{}
+
+ for i := range collection {
+ match, err := predicate(collection[i], i)
+ if err != nil {
+ return nil, err
+ }
+ if !match {
+ result = append(result, collection[i])
+ }
+ }
+
+ return result, nil
+}
+
// RejectMap is the opposite of FilterMap, this method returns a slice obtained after both filtering and mapping using the given callback function.
// The callback function should return two values:
// - the result of the mapping operation and
@@ -635,6 +974,24 @@ func CountBy[T any](collection []T, predicate func(item T) bool) int {
return count
}
+// CountByErr counts the number of elements in the collection for which predicate is true.
+// It returns the first error returned by the predicate.
+func CountByErr[T any](collection []T, predicate func(item T) (bool, error)) (int, error) {
+ var count int
+
+ for i := range collection {
+ ok, err := predicate(collection[i])
+ if err != nil {
+ return 0, err
+ }
+ if ok {
+ count++
+ }
+ }
+
+ return count, nil
+}
+
// CountValues counts the number of each element in the collection.
// Play: https://go.dev/play/p/-p-PyLT4dfy
func CountValues[T comparable](collection []T) map[T]int {
@@ -647,14 +1004,14 @@ func CountValues[T comparable](collection []T) map[T]int {
return result
}
-// CountValuesBy counts the number of each element returned from mapper function.
+// CountValuesBy counts the number of each element returned from transform function.
// Is equivalent to chaining lo.Map and lo.CountValues.
// Play: https://go.dev/play/p/2U0dG1SnOmS
-func CountValuesBy[T any, U comparable](collection []T, mapper func(item T) U) map[U]int {
+func CountValuesBy[T any, U comparable](collection []T, transform func(item T) U) map[U]int {
result := make(map[U]int)
for i := range collection {
- result[mapper(collection[i])]++
+ result[transform(collection[i])]++
}
return result
@@ -683,27 +1040,24 @@ func Subset[T any, Slice ~[]T](collection Slice, offset int, length uint) Slice
return collection[offset : offset+int(length)]
}
-// Slice returns a copy of a slice from `start` up to, but not including `end`. Like `slice[start:end]`, but does not panic on overflow.
+// Slice returns a slice from `start` up to, but not including `end`. Like `slice[start:end]`, but does not panic on overflow.
// Play: https://go.dev/play/p/8XWYhfMMA1h
func Slice[T any, Slice ~[]T](collection Slice, start, end int) Slice {
- size := len(collection)
-
if start >= end {
return Slice{}
}
- if start > size {
- start = size
- }
+ size := len(collection)
if start < 0 {
start = 0
+ } else if start > size {
+ start = size
}
- if end > size {
- end = size
- }
if end < 0 {
end = 0
+ } else if end > size {
+ end = size
}
return collection[start:end]
@@ -731,6 +1085,20 @@ func ReplaceAll[T comparable, Slice ~[]T](collection Slice, old, nEw T) Slice {
return Replace(collection, old, nEw, -1)
}
+// Clone returns a shallow copy of the collection.
+func Clone[T any, Slice ~[]T](collection Slice) Slice {
+ // backporting from slices.Clone in Go 1.21
+ // when we drop support for Go 1.20, this can be replaced with: return slices.Clone(collection)
+
+ // Preserve nilness in case it matters.
+ if collection == nil {
+ return nil
+ }
+ // Avoid s[:0:0] as it leads to unwanted liveness when cloning a
+ // zero-length slice of a large array; see https://go.dev/issue/68488.
+ return append(Slice{}, collection...)
+}
+
// Compact returns a slice of all non-zero elements.
// Play: https://go.dev/play/p/tXiy-iK6PAc
func Compact[T comparable, Slice ~[]T](collection Slice) Slice {
@@ -759,8 +1127,8 @@ func IsSorted[T constraints.Ordered](collection []T) bool {
return true
}
-// IsSortedByKey checks if a slice is sorted by iteratee.
-func IsSortedByKey[T any, K constraints.Ordered](collection []T, iteratee func(item T) K) bool {
+// IsSortedBy checks if a slice is sorted by iteratee.
+func IsSortedBy[T any, K constraints.Ordered](collection []T, iteratee func(item T) K) bool {
size := len(collection)
for i := 0; i < size-1; i++ {
@@ -772,6 +1140,13 @@ func IsSortedByKey[T any, K constraints.Ordered](collection []T, iteratee func(i
return true
}
+// IsSortedByKey checks if a slice is sorted by iteratee.
+//
+// Deprecated: Use lo.IsSortedBy instead.
+func IsSortedByKey[T any, K constraints.Ordered](collection []T, iteratee func(item T) K) bool {
+ return IsSortedBy(collection, iteratee)
+}
+
// Splice inserts multiple elements at index i. A negative index counts back
// from the end of the slice. The helper is protected against overflow errors.
// Play: https://go.dev/play/p/G5_GhkeSUBA
@@ -828,20 +1203,10 @@ func Cut[T comparable, Slice ~[]T](collection, separator Slice) (before, after S
// If prefix is the empty []T, CutPrefix returns collection, true.
// Play: https://go.dev/play/p/7Plak4a1ICl
func CutPrefix[T comparable, Slice ~[]T](collection, separator Slice) (after Slice, found bool) {
- if len(separator) == 0 {
- return collection, true
+ if HasPrefix(collection, separator) {
+ return collection[len(separator):], true
}
- if len(separator) > len(collection) {
- return collection, false
- }
-
- for i := range separator {
- if collection[i] != separator[i] {
- return collection, false
- }
- }
-
- return collection[len(separator):], true
+ return collection, false
}
// CutSuffix returns collection without the provided ending suffix []T and reports
@@ -849,21 +1214,10 @@ func CutPrefix[T comparable, Slice ~[]T](collection, separator Slice) (after Sli
// If suffix is the empty []T, CutSuffix returns collection, true.
// Play: https://go.dev/play/p/7FKfBFvPTaT
func CutSuffix[T comparable, Slice ~[]T](collection, separator Slice) (before Slice, found bool) {
- if len(separator) == 0 {
- return collection, true
+ if HasSuffix(collection, separator) {
+ return collection[:len(collection)-len(separator)], true
}
- if len(separator) > len(collection) {
- return collection, false
- }
-
- start := len(collection) - len(separator)
- for i := range separator {
- if collection[start+i] != separator[i] {
- return collection, false
- }
- }
-
- return collection[:start], true
+ return collection, false
}
// Trim removes all the leading and trailing cutset from the collection.
@@ -911,12 +1265,11 @@ func TrimPrefix[T comparable, Slice ~[]T](collection, prefix Slice) Slice {
return collection
}
- for {
- if !HasPrefix(collection, prefix) {
- return collection
- }
+ for HasPrefix(collection, prefix) {
collection = collection[len(prefix):]
}
+
+ return collection
}
// TrimRight removes all the trailing cutset from the collection.
@@ -937,10 +1290,9 @@ func TrimSuffix[T comparable, Slice ~[]T](collection, suffix Slice) Slice {
return collection
}
- for {
- if !HasSuffix(collection, suffix) {
- return collection
- }
+ for HasSuffix(collection, suffix) {
collection = collection[:len(collection)-len(suffix)]
}
+
+ return collection
}
diff --git a/vendor/github.com/samber/lo/string.go b/vendor/github.com/samber/lo/string.go
index 9590114b9..9b0fc6e95 100644
--- a/vendor/github.com/samber/lo/string.go
+++ b/vendor/github.com/samber/lo/string.go
@@ -7,10 +7,10 @@ import (
"unicode"
"unicode/utf8"
- "github.com/samber/lo/internal/xrand"
-
"golang.org/x/text/cases"
"golang.org/x/text/language"
+
+ "github.com/samber/lo/internal/xrand"
)
var (
@@ -41,7 +41,7 @@ func RandomString(size int, charset []rune) string {
}
// see https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go
- sb := strings.Builder{}
+ var sb strings.Builder
sb.Grow(size)
if len(charset) == 1 {
@@ -100,42 +100,107 @@ func nearestPowerOfTwo(capacity int) int {
return n + 1
}
-// Substring return part of a string.
+// Substring extracts a substring from a string with Unicode character (rune) awareness.
+// offset - starting position of the substring (can be positive, negative, or zero)
+// length - number of characters to extract
+// With positive offset, counting starts from the beginning of the string
+// With negative offset, counting starts from the end of the string
// Play: https://go.dev/play/p/TQlxQi82Lu1
func Substring[T ~string](str T, offset int, length uint) T {
- rs := []rune(str)
- size := len(rs)
+ str = substring(str, offset, length)
- if offset < 0 {
- offset = size + offset
- if offset < 0 {
- offset = 0
+ // Validate UTF-8 and fix invalid sequences
+ if !utf8.ValidString(string(str)) {
+ // Convert to []rune to replicate behavior with duplicated �
+ str = T([]rune(str))
+ }
+
+ // Remove null bytes from result
+ return T(strings.ReplaceAll(string(str), "\x00", ""))
+}
+
+func substring[T ~string](str T, offset int, length uint) T {
+ switch {
+ // Empty length or offset beyond string bounds - return empty string
+ case length == 0, offset >= len(str):
+ return ""
+
+ // Positive offset - count from the beginning
+ case offset > 0:
+ // Skip offset runes from the start
+ for i, r := range str {
+ if offset--; offset == 0 {
+ str = str[i+utf8.RuneLen(r):]
+ break
+ }
}
- }
- if offset >= size {
- return Empty[T]()
- }
+ // If couldn't skip enough runes - string is shorter than offset
+ if offset != 0 {
+ return ""
+ }
- if length > uint(size)-uint(offset) {
- length = uint(size - offset)
- }
+ // If remaining string is shorter than or equal to length - return it entirely
+ if uint(len(str)) <= length {
+ return str
+ }
- return T(strings.ReplaceAll(string(rs[offset:offset+int(length)]), "\x00", ""))
+ // Otherwise proceed to trimming by length
+ fallthrough
+
+ // Zero offset or offset less than minus string length - start from beginning
+ case offset < -len(str), offset == 0:
+ // Count length runes from the start
+ for i := range str {
+ if length == 0 {
+ return str[:i]
+ }
+ length--
+ }
+
+ return str
+
+ // Negative offset - count from the end of string
+ default: // -len(str) < offset < 0
+ // Helper function to move backward through runes
+ backwardPos := func(end int, count uint) (start int) {
+ for {
+ _, i := utf8.DecodeLastRuneInString(string(str[:end]))
+ end -= i
+
+ if count--; count == 0 || end == 0 {
+ return end
+ }
+ }
+ }
+
+ offset := uint(-offset)
+
+ // If offset is less than or equal to length - take from position to end
+ if offset <= length {
+ start := backwardPos(len(str), offset)
+ return str[start:]
+ }
+
+ // Otherwise calculate start and end positions
+ end := backwardPos(len(str), offset-length)
+ start := backwardPos(end, length)
+
+ return str[start:end]
+ }
}
// ChunkString returns a slice of strings split into groups of length size. If the string can't be split evenly,
// the final chunk will be the remaining characters.
// Play: https://go.dev/play/p/__FLTuJVz54
+//
+// Note: lo.ChunkString and lo.Chunk functions behave inconsistently for empty input: lo.ChunkString("", n) returns [""] instead of [].
+// See https://github.com/samber/lo/issues/788
func ChunkString[T ~string](str T, size int) []T {
if size <= 0 {
panic("lo.ChunkString: size must be greater than 0")
}
- if len(str) == 0 {
- return []T{""}
- }
-
if size >= len(str) {
return []T{str}
}
@@ -212,6 +277,7 @@ func Words(str string) []string {
// example: Int8Value => Int 8Value => Int 8 Value
str = splitNumberLetterReg.ReplaceAllString(str, "$1 $2")
var result strings.Builder
+ result.Grow(len(str))
for _, r := range str {
if unicode.IsLetter(r) || unicode.IsDigit(r) {
result.WriteRune(r)
@@ -228,26 +294,25 @@ func Capitalize(str string) string {
return cases.Title(language.English).String(str)
}
-// Ellipsis trims and truncates a string to a specified length **in bytes** and appends an ellipsis
-// if truncated. If the string contains non-ASCII characters (which may occupy multiple bytes in UTF-8),
-// truncating by byte length may split a character in the middle, potentially resulting in garbled output.
+// Ellipsis trims and truncates a string to a specified length in runes and appends an ellipsis
+// if truncated. The length parameter counts Unicode code points (runes), not bytes, so multi-byte
+// characters such as emoji or CJK ideographs are never split in the middle.
// Play: https://go.dev/play/p/qE93rgqe1TW
func Ellipsis(str string, length int) string {
str = strings.TrimSpace(str)
- if len(str) > length {
- if len(str) < 3 || length < 3 {
- return "..."
+ const ellipsis = "..."
+
+ cutPosition := 0
+ for i := range str {
+ if length == len(ellipsis) {
+ cutPosition = i
+ }
+
+ if length--; length < 0 {
+ return strings.TrimSpace(str[:cutPosition]) + ellipsis
}
- return strings.TrimSpace(str[0:length-3]) + "..."
}
return str
}
-
-// Elipse trims and truncates a string to a specified length and appends an ellipsis if truncated.
-//
-// Deprecated: Use Ellipsis instead.
-func Elipse(str string, length int) string {
- return Ellipsis(str, length)
-}
diff --git a/vendor/github.com/samber/lo/time.go b/vendor/github.com/samber/lo/time.go
index f295adbbd..a2b1c7e05 100644
--- a/vendor/github.com/samber/lo/time.go
+++ b/vendor/github.com/samber/lo/time.go
@@ -6,94 +6,94 @@ import (
// Duration returns the time taken to execute a function.
// Play: https://go.dev/play/p/HQfbBbAXaFP
-func Duration(cb func()) time.Duration {
- return Duration0(cb)
+func Duration(callback func()) time.Duration {
+ return Duration0(callback)
}
// Duration0 returns the time taken to execute a function.
// Play: https://go.dev/play/p/HQfbBbAXaFP
-func Duration0(cb func()) time.Duration {
+func Duration0(callback func()) time.Duration {
start := time.Now()
- cb()
+ callback()
return time.Since(start)
}
// Duration1 returns the time taken to execute a function.
// Play: https://go.dev/play/p/HQfbBbAXaFP
-func Duration1[A any](cb func() A) (A, time.Duration) {
+func Duration1[A any](callback func() A) (A, time.Duration) {
start := time.Now()
- a := cb()
+ a := callback()
return a, time.Since(start)
}
// Duration2 returns the time taken to execute a function.
// Play: https://go.dev/play/p/HQfbBbAXaFP
-func Duration2[A, B any](cb func() (A, B)) (A, B, time.Duration) {
+func Duration2[A, B any](callback func() (A, B)) (A, B, time.Duration) {
start := time.Now()
- a, b := cb()
+ a, b := callback()
return a, b, time.Since(start)
}
// Duration3 returns the time taken to execute a function.
// Play: https://go.dev/play/p/xr863iwkAxQ
-func Duration3[A, B, C any](cb func() (A, B, C)) (A, B, C, time.Duration) {
+func Duration3[A, B, C any](callback func() (A, B, C)) (A, B, C, time.Duration) {
start := time.Now()
- a, b, c := cb()
+ a, b, c := callback()
return a, b, c, time.Since(start)
}
// Duration4 returns the time taken to execute a function.
// Play: https://go.dev/play/p/xr863iwkAxQ
-func Duration4[A, B, C, D any](cb func() (A, B, C, D)) (A, B, C, D, time.Duration) {
+func Duration4[A, B, C, D any](callback func() (A, B, C, D)) (A, B, C, D, time.Duration) {
start := time.Now()
- a, b, c, d := cb()
+ a, b, c, d := callback()
return a, b, c, d, time.Since(start)
}
// Duration5 returns the time taken to execute a function.
// Play: https://go.dev/play/p/xr863iwkAxQ
-func Duration5[A, B, C, D, E any](cb func() (A, B, C, D, E)) (A, B, C, D, E, time.Duration) {
+func Duration5[A, B, C, D, E any](callback func() (A, B, C, D, E)) (A, B, C, D, E, time.Duration) {
start := time.Now()
- a, b, c, d, e := cb()
+ a, b, c, d, e := callback()
return a, b, c, d, e, time.Since(start)
}
// Duration6 returns the time taken to execute a function.
// Play: https://go.dev/play/p/mR4bTQKO-Tf
-func Duration6[A, B, C, D, E, F any](cb func() (A, B, C, D, E, F)) (A, B, C, D, E, F, time.Duration) {
+func Duration6[A, B, C, D, E, F any](callback func() (A, B, C, D, E, F)) (A, B, C, D, E, F, time.Duration) {
start := time.Now()
- a, b, c, d, e, f := cb()
+ a, b, c, d, e, f := callback()
return a, b, c, d, e, f, time.Since(start)
}
// Duration7 returns the time taken to execute a function.
// Play: https://go.dev/play/p/jgIAcBWWInS
-func Duration7[A, B, C, D, E, F, G any](cb func() (A, B, C, D, E, F, G)) (A, B, C, D, E, F, G, time.Duration) {
+func Duration7[A, B, C, D, E, F, G any](callback func() (A, B, C, D, E, F, G)) (A, B, C, D, E, F, G, time.Duration) {
start := time.Now()
- a, b, c, d, e, f, g := cb()
+ a, b, c, d, e, f, g := callback()
return a, b, c, d, e, f, g, time.Since(start)
}
// Duration8 returns the time taken to execute a function.
// Play: https://go.dev/play/p/T8kxpG1c5Na
-func Duration8[A, B, C, D, E, F, G, H any](cb func() (A, B, C, D, E, F, G, H)) (A, B, C, D, E, F, G, H, time.Duration) {
+func Duration8[A, B, C, D, E, F, G, H any](callback func() (A, B, C, D, E, F, G, H)) (A, B, C, D, E, F, G, H, time.Duration) {
start := time.Now()
- a, b, c, d, e, f, g, h := cb()
+ a, b, c, d, e, f, g, h := callback()
return a, b, c, d, e, f, g, h, time.Since(start)
}
// Duration9 returns the time taken to execute a function.
// Play: https://go.dev/play/p/bg9ix2VrZ0j
-func Duration9[A, B, C, D, E, F, G, H, I any](cb func() (A, B, C, D, E, F, G, H, I)) (A, B, C, D, E, F, G, H, I, time.Duration) {
+func Duration9[A, B, C, D, E, F, G, H, I any](callback func() (A, B, C, D, E, F, G, H, I)) (A, B, C, D, E, F, G, H, I, time.Duration) {
start := time.Now()
- a, b, c, d, e, f, g, h, i := cb()
+ a, b, c, d, e, f, g, h, i := callback()
return a, b, c, d, e, f, g, h, i, time.Since(start)
}
// Duration10 returns the time taken to execute a function.
// Play: https://go.dev/play/p/Y3n7oJXqJbk
-func Duration10[A, B, C, D, E, F, G, H, I, J any](cb func() (A, B, C, D, E, F, G, H, I, J)) (A, B, C, D, E, F, G, H, I, J, time.Duration) {
+func Duration10[A, B, C, D, E, F, G, H, I, J any](callback func() (A, B, C, D, E, F, G, H, I, J)) (A, B, C, D, E, F, G, H, I, J, time.Duration) {
start := time.Now()
- a, b, c, d, e, f, g, h, i, j := cb()
+ a, b, c, d, e, f, g, h, i, j := callback()
return a, b, c, d, e, f, g, h, i, j, time.Since(start)
}
diff --git a/vendor/github.com/samber/lo/tuples.go b/vendor/github.com/samber/lo/tuples.go
index d9805b2d4..1911f8db5 100644
--- a/vendor/github.com/samber/lo/tuples.go
+++ b/vendor/github.com/samber/lo/tuples.go
@@ -101,18 +101,13 @@ func Unpack9[A, B, C, D, E, F, G, H, I any](tuple Tuple9[A, B, C, D, E, F, G, H,
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/jujaA6GaJTp
func Zip2[A, B any](a []A, b []B) []Tuple2[A, B] {
- size := Max([]int{len(a), len(b)})
+ size := uint(Max([]int{len(a), len(b)}))
- result := make([]Tuple2[A, B], 0, size)
+ result := make([]Tuple2[A, B], size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
-
- result = append(result, Tuple2[A, B]{
- A: _a,
- B: _b,
- })
+ for index := uint(0); index < size; index++ {
+ result[index].A = NthOrEmpty(a, index)
+ result[index].B = NthOrEmpty(b, index)
}
return result
@@ -123,20 +118,14 @@ func Zip2[A, B any](a []A, b []B) []Tuple2[A, B] {
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/jujaA6GaJTp
func Zip3[A, B, C any](a []A, b []B, c []C) []Tuple3[A, B, C] {
- size := Max([]int{len(a), len(b), len(c)})
+ size := uint(Max([]int{len(a), len(b), len(c)}))
- result := make([]Tuple3[A, B, C], 0, size)
+ result := make([]Tuple3[A, B, C], size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
-
- result = append(result, Tuple3[A, B, C]{
- A: _a,
- B: _b,
- C: _c,
- })
+ for index := uint(0); index < size; index++ {
+ result[index].A = NthOrEmpty(a, index)
+ result[index].B = NthOrEmpty(b, index)
+ result[index].C = NthOrEmpty(c, index)
}
return result
@@ -147,22 +136,15 @@ func Zip3[A, B, C any](a []A, b []B, c []C) []Tuple3[A, B, C] {
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/jujaA6GaJTp
func Zip4[A, B, C, D any](a []A, b []B, c []C, d []D) []Tuple4[A, B, C, D] {
- size := Max([]int{len(a), len(b), len(c), len(d)})
+ size := uint(Max([]int{len(a), len(b), len(c), len(d)}))
- result := make([]Tuple4[A, B, C, D], 0, size)
+ result := make([]Tuple4[A, B, C, D], size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
- _d, _ := Nth(d, index)
-
- result = append(result, Tuple4[A, B, C, D]{
- A: _a,
- B: _b,
- C: _c,
- D: _d,
- })
+ for index := uint(0); index < size; index++ {
+ result[index].A = NthOrEmpty(a, index)
+ result[index].B = NthOrEmpty(b, index)
+ result[index].C = NthOrEmpty(c, index)
+ result[index].D = NthOrEmpty(d, index)
}
return result
@@ -173,24 +155,16 @@ func Zip4[A, B, C, D any](a []A, b []B, c []C, d []D) []Tuple4[A, B, C, D] {
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/jujaA6GaJTp
func Zip5[A, B, C, D, E any](a []A, b []B, c []C, d []D, e []E) []Tuple5[A, B, C, D, E] {
- size := Max([]int{len(a), len(b), len(c), len(d), len(e)})
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e)}))
- result := make([]Tuple5[A, B, C, D, E], 0, size)
+ result := make([]Tuple5[A, B, C, D, E], size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
- _d, _ := Nth(d, index)
- _e, _ := Nth(e, index)
-
- result = append(result, Tuple5[A, B, C, D, E]{
- A: _a,
- B: _b,
- C: _c,
- D: _d,
- E: _e,
- })
+ for index := uint(0); index < size; index++ {
+ result[index].A = NthOrEmpty(a, index)
+ result[index].B = NthOrEmpty(b, index)
+ result[index].C = NthOrEmpty(c, index)
+ result[index].D = NthOrEmpty(d, index)
+ result[index].E = NthOrEmpty(e, index)
}
return result
@@ -201,26 +175,17 @@ func Zip5[A, B, C, D, E any](a []A, b []B, c []C, d []D, e []E) []Tuple5[A, B, C
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/jujaA6GaJTp
func Zip6[A, B, C, D, E, F any](a []A, b []B, c []C, d []D, e []E, f []F) []Tuple6[A, B, C, D, E, F] {
- size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f)})
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e), len(f)}))
- result := make([]Tuple6[A, B, C, D, E, F], 0, size)
+ result := make([]Tuple6[A, B, C, D, E, F], size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
- _d, _ := Nth(d, index)
- _e, _ := Nth(e, index)
- _f, _ := Nth(f, index)
-
- result = append(result, Tuple6[A, B, C, D, E, F]{
- A: _a,
- B: _b,
- C: _c,
- D: _d,
- E: _e,
- F: _f,
- })
+ for index := uint(0); index < size; index++ {
+ result[index].A = NthOrEmpty(a, index)
+ result[index].B = NthOrEmpty(b, index)
+ result[index].C = NthOrEmpty(c, index)
+ result[index].D = NthOrEmpty(d, index)
+ result[index].E = NthOrEmpty(e, index)
+ result[index].F = NthOrEmpty(f, index)
}
return result
@@ -231,28 +196,18 @@ func Zip6[A, B, C, D, E, F any](a []A, b []B, c []C, d []D, e []E, f []F) []Tupl
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/jujaA6GaJTp
func Zip7[A, B, C, D, E, F, G any](a []A, b []B, c []C, d []D, e []E, f []F, g []G) []Tuple7[A, B, C, D, E, F, G] {
- size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g)})
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g)}))
- result := make([]Tuple7[A, B, C, D, E, F, G], 0, size)
+ result := make([]Tuple7[A, B, C, D, E, F, G], size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
- _d, _ := Nth(d, index)
- _e, _ := Nth(e, index)
- _f, _ := Nth(f, index)
- _g, _ := Nth(g, index)
-
- result = append(result, Tuple7[A, B, C, D, E, F, G]{
- A: _a,
- B: _b,
- C: _c,
- D: _d,
- E: _e,
- F: _f,
- G: _g,
- })
+ for index := uint(0); index < size; index++ {
+ result[index].A = NthOrEmpty(a, index)
+ result[index].B = NthOrEmpty(b, index)
+ result[index].C = NthOrEmpty(c, index)
+ result[index].D = NthOrEmpty(d, index)
+ result[index].E = NthOrEmpty(e, index)
+ result[index].F = NthOrEmpty(f, index)
+ result[index].G = NthOrEmpty(g, index)
}
return result
@@ -263,30 +218,19 @@ func Zip7[A, B, C, D, E, F, G any](a []A, b []B, c []C, d []D, e []E, f []F, g [
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/jujaA6GaJTp
func Zip8[A, B, C, D, E, F, G, H any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H) []Tuple8[A, B, C, D, E, F, G, H] {
- size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h)})
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h)}))
- result := make([]Tuple8[A, B, C, D, E, F, G, H], 0, size)
+ result := make([]Tuple8[A, B, C, D, E, F, G, H], size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
- _d, _ := Nth(d, index)
- _e, _ := Nth(e, index)
- _f, _ := Nth(f, index)
- _g, _ := Nth(g, index)
- _h, _ := Nth(h, index)
-
- result = append(result, Tuple8[A, B, C, D, E, F, G, H]{
- A: _a,
- B: _b,
- C: _c,
- D: _d,
- E: _e,
- F: _f,
- G: _g,
- H: _h,
- })
+ for index := uint(0); index < size; index++ {
+ result[index].A = NthOrEmpty(a, index)
+ result[index].B = NthOrEmpty(b, index)
+ result[index].C = NthOrEmpty(c, index)
+ result[index].D = NthOrEmpty(d, index)
+ result[index].E = NthOrEmpty(e, index)
+ result[index].F = NthOrEmpty(f, index)
+ result[index].G = NthOrEmpty(g, index)
+ result[index].H = NthOrEmpty(h, index)
}
return result
@@ -297,32 +241,20 @@ func Zip8[A, B, C, D, E, F, G, H any](a []A, b []B, c []C, d []D, e []E, f []F,
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/jujaA6GaJTp
func Zip9[A, B, C, D, E, F, G, H, I any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I) []Tuple9[A, B, C, D, E, F, G, H, I] {
- size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h), len(i)})
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h), len(i)}))
- result := make([]Tuple9[A, B, C, D, E, F, G, H, I], 0, size)
+ result := make([]Tuple9[A, B, C, D, E, F, G, H, I], size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
- _d, _ := Nth(d, index)
- _e, _ := Nth(e, index)
- _f, _ := Nth(f, index)
- _g, _ := Nth(g, index)
- _h, _ := Nth(h, index)
- _i, _ := Nth(i, index)
-
- result = append(result, Tuple9[A, B, C, D, E, F, G, H, I]{
- A: _a,
- B: _b,
- C: _c,
- D: _d,
- E: _e,
- F: _f,
- G: _g,
- H: _h,
- I: _i,
- })
+ for index := uint(0); index < size; index++ {
+ result[index].A = NthOrEmpty(a, index)
+ result[index].B = NthOrEmpty(b, index)
+ result[index].C = NthOrEmpty(c, index)
+ result[index].D = NthOrEmpty(d, index)
+ result[index].E = NthOrEmpty(e, index)
+ result[index].F = NthOrEmpty(f, index)
+ result[index].G = NthOrEmpty(g, index)
+ result[index].H = NthOrEmpty(h, index)
+ result[index].I = NthOrEmpty(i, index)
}
return result
@@ -333,15 +265,15 @@ func Zip9[A, B, C, D, E, F, G, H, I any](a []A, b []B, c []C, d []D, e []E, f []
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/wlHur6yO8rR
func ZipBy2[A, B, Out any](a []A, b []B, iteratee func(a A, b B) Out) []Out {
- size := Max([]int{len(a), len(b)})
+ size := uint(Max([]int{len(a), len(b)}))
- result := make([]Out, 0, size)
+ result := make([]Out, size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
-
- result = append(result, iteratee(_a, _b))
+ for index := uint(0); index < size; index++ {
+ result[index] = iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ )
}
return result
@@ -352,16 +284,16 @@ func ZipBy2[A, B, Out any](a []A, b []B, iteratee func(a A, b B) Out) []Out {
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/j9maveOnSQX
func ZipBy3[A, B, C, Out any](a []A, b []B, c []C, iteratee func(a A, b B, c C) Out) []Out {
- size := Max([]int{len(a), len(b), len(c)})
+ size := uint(Max([]int{len(a), len(b), len(c)}))
- result := make([]Out, 0, size)
+ result := make([]Out, size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
-
- result = append(result, iteratee(_a, _b, _c))
+ for index := uint(0); index < size; index++ {
+ result[index] = iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ )
}
return result
@@ -372,17 +304,17 @@ func ZipBy3[A, B, C, Out any](a []A, b []B, c []C, iteratee func(a A, b B, c C)
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/Y1eF2Ke0Ayz
func ZipBy4[A, B, C, D, Out any](a []A, b []B, c []C, d []D, iteratee func(a A, b B, c C, d D) Out) []Out {
- size := Max([]int{len(a), len(b), len(c), len(d)})
+ size := uint(Max([]int{len(a), len(b), len(c), len(d)}))
- result := make([]Out, 0, size)
+ result := make([]Out, size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
- _d, _ := Nth(d, index)
-
- result = append(result, iteratee(_a, _b, _c, _d))
+ for index := uint(0); index < size; index++ {
+ result[index] = iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ NthOrEmpty(d, index),
+ )
}
return result
@@ -393,18 +325,18 @@ func ZipBy4[A, B, C, D, Out any](a []A, b []B, c []C, d []D, iteratee func(a A,
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/SLynyalh5Oa
func ZipBy5[A, B, C, D, E, Out any](a []A, b []B, c []C, d []D, e []E, iteratee func(a A, b B, c C, d D, e E) Out) []Out {
- size := Max([]int{len(a), len(b), len(c), len(d), len(e)})
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e)}))
- result := make([]Out, 0, size)
+ result := make([]Out, size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
- _d, _ := Nth(d, index)
- _e, _ := Nth(e, index)
-
- result = append(result, iteratee(_a, _b, _c, _d, _e))
+ for index := uint(0); index < size; index++ {
+ result[index] = iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ NthOrEmpty(d, index),
+ NthOrEmpty(e, index),
+ )
}
return result
@@ -415,19 +347,19 @@ func ZipBy5[A, B, C, D, E, Out any](a []A, b []B, c []C, d []D, e []E, iteratee
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/IK6KVgw9e-S
func ZipBy6[A, B, C, D, E, F, Out any](a []A, b []B, c []C, d []D, e []E, f []F, iteratee func(a A, b B, c C, d D, e E, f F) Out) []Out {
- size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f)})
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e), len(f)}))
- result := make([]Out, 0, size)
+ result := make([]Out, size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
- _d, _ := Nth(d, index)
- _e, _ := Nth(e, index)
- _f, _ := Nth(f, index)
-
- result = append(result, iteratee(_a, _b, _c, _d, _e, _f))
+ for index := uint(0); index < size; index++ {
+ result[index] = iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ NthOrEmpty(d, index),
+ NthOrEmpty(e, index),
+ NthOrEmpty(f, index),
+ )
}
return result
@@ -438,20 +370,20 @@ func ZipBy6[A, B, C, D, E, F, Out any](a []A, b []B, c []C, d []D, e []E, f []F,
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/4uW6a2vXh8w
func ZipBy7[A, B, C, D, E, F, G, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, iteratee func(a A, b B, c C, d D, e E, f F, g G) Out) []Out {
- size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g)})
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g)}))
- result := make([]Out, 0, size)
+ result := make([]Out, size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
- _d, _ := Nth(d, index)
- _e, _ := Nth(e, index)
- _f, _ := Nth(f, index)
- _g, _ := Nth(g, index)
-
- result = append(result, iteratee(_a, _b, _c, _d, _e, _f, _g))
+ for index := uint(0); index < size; index++ {
+ result[index] = iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ NthOrEmpty(d, index),
+ NthOrEmpty(e, index),
+ NthOrEmpty(f, index),
+ NthOrEmpty(g, index),
+ )
}
return result
@@ -462,21 +394,21 @@ func ZipBy7[A, B, C, D, E, F, G, Out any](a []A, b []B, c []C, d []D, e []E, f [
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/tk8xW7XzY4v
func ZipBy8[A, B, C, D, E, F, G, H, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, iteratee func(a A, b B, c C, d D, e E, f F, g G, h H) Out) []Out {
- size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h)})
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h)}))
- result := make([]Out, 0, size)
+ result := make([]Out, size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
- _d, _ := Nth(d, index)
- _e, _ := Nth(e, index)
- _f, _ := Nth(f, index)
- _g, _ := Nth(g, index)
- _h, _ := Nth(h, index)
-
- result = append(result, iteratee(_a, _b, _c, _d, _e, _f, _g, _h))
+ for index := uint(0); index < size; index++ {
+ result[index] = iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ NthOrEmpty(d, index),
+ NthOrEmpty(e, index),
+ NthOrEmpty(f, index),
+ NthOrEmpty(g, index),
+ NthOrEmpty(h, index),
+ )
}
return result
@@ -487,27 +419,231 @@ func ZipBy8[A, B, C, D, E, F, G, H, Out any](a []A, b []B, c []C, d []D, e []E,
// When collections are different sizes, the Tuple attributes are filled with zero value.
// Play: https://go.dev/play/p/VGqjDmQ9YqX
func ZipBy9[A, B, C, D, E, F, G, H, I, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I, iteratee func(a A, b B, c C, d D, e E, f F, g G, h H, i I) Out) []Out {
- size := Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h), len(i)})
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h), len(i)}))
- result := make([]Out, 0, size)
+ result := make([]Out, size)
- for index := 0; index < size; index++ {
- _a, _ := Nth(a, index)
- _b, _ := Nth(b, index)
- _c, _ := Nth(c, index)
- _d, _ := Nth(d, index)
- _e, _ := Nth(e, index)
- _f, _ := Nth(f, index)
- _g, _ := Nth(g, index)
- _h, _ := Nth(h, index)
- _i, _ := Nth(i, index)
-
- result = append(result, iteratee(_a, _b, _c, _d, _e, _f, _g, _h, _i))
+ for index := uint(0); index < size; index++ {
+ result[index] = iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ NthOrEmpty(d, index),
+ NthOrEmpty(e, index),
+ NthOrEmpty(f, index),
+ NthOrEmpty(g, index),
+ NthOrEmpty(h, index),
+ NthOrEmpty(i, index),
+ )
}
return result
}
+// ZipByErr2 creates a slice of transformed elements, the first of which contains the first elements
+// of the given slices, the second of which contains the second elements of the given slices, and so on.
+// When collections are different sizes, the Tuple attributes are filled with zero value.
+// It returns the first error returned by the iteratee.
+func ZipByErr2[A, B, Out any](a []A, b []B, iteratee func(a A, b B) (Out, error)) ([]Out, error) {
+ size := uint(Max([]int{len(a), len(b)}))
+ result := make([]Out, size)
+
+ for index := uint(0); index < size; index++ {
+ r, err := iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ )
+ if err != nil {
+ return nil, err
+ }
+ result[index] = r
+ }
+
+ return result, nil
+}
+
+// ZipByErr3 creates a slice of transformed elements, the first of which contains the first elements
+// of the given slices, the second of which contains the second elements of the given slices, and so on.
+// When collections are different sizes, the Tuple attributes are filled with zero value.
+// It returns the first error returned by the iteratee.
+func ZipByErr3[A, B, C, Out any](a []A, b []B, c []C, iteratee func(a A, b B, c C) (Out, error)) ([]Out, error) {
+ size := uint(Max([]int{len(a), len(b), len(c)}))
+ result := make([]Out, size)
+
+ for index := uint(0); index < size; index++ {
+ r, err := iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ )
+ if err != nil {
+ return nil, err
+ }
+ result[index] = r
+ }
+
+ return result, nil
+}
+
+// ZipByErr4 creates a slice of transformed elements, the first of which contains the first elements
+// of the given slices, the second of which contains the second elements of the given slices, and so on.
+// When collections are different sizes, the Tuple attributes are filled with zero value.
+// It returns the first error returned by the iteratee.
+func ZipByErr4[A, B, C, D, Out any](a []A, b []B, c []C, d []D, iteratee func(a A, b B, c C, d D) (Out, error)) ([]Out, error) {
+ size := uint(Max([]int{len(a), len(b), len(c), len(d)}))
+ result := make([]Out, size)
+
+ for index := uint(0); index < size; index++ {
+ r, err := iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ NthOrEmpty(d, index),
+ )
+ if err != nil {
+ return nil, err
+ }
+ result[index] = r
+ }
+
+ return result, nil
+}
+
+// ZipByErr5 creates a slice of transformed elements, the first of which contains the first elements
+// of the given slices, the second of which contains the second elements of the given slices, and so on.
+// When collections are different sizes, the Tuple attributes are filled with zero value.
+// It returns the first error returned by the iteratee.
+func ZipByErr5[A, B, C, D, E, Out any](a []A, b []B, c []C, d []D, e []E, iteratee func(a A, b B, c C, d D, e E) (Out, error)) ([]Out, error) {
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e)}))
+ result := make([]Out, size)
+
+ for index := uint(0); index < size; index++ {
+ r, err := iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ NthOrEmpty(d, index),
+ NthOrEmpty(e, index),
+ )
+ if err != nil {
+ return nil, err
+ }
+ result[index] = r
+ }
+
+ return result, nil
+}
+
+// ZipByErr6 creates a slice of transformed elements, the first of which contains the first elements
+// of the given slices, the second of which contains the second elements of the given slices, and so on.
+// When collections are different sizes, the Tuple attributes are filled with zero value.
+// It returns the first error returned by the iteratee.
+func ZipByErr6[A, B, C, D, E, F, Out any](a []A, b []B, c []C, d []D, e []E, f []F, iteratee func(a A, b B, c C, d D, e E, f F) (Out, error)) ([]Out, error) {
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e), len(f)}))
+ result := make([]Out, size)
+
+ for index := uint(0); index < size; index++ {
+ r, err := iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ NthOrEmpty(d, index),
+ NthOrEmpty(e, index),
+ NthOrEmpty(f, index),
+ )
+ if err != nil {
+ return nil, err
+ }
+ result[index] = r
+ }
+
+ return result, nil
+}
+
+// ZipByErr7 creates a slice of transformed elements, the first of which contains the first elements
+// of the given slices, the second of which contains the second elements of the given slices, and so on.
+// When collections are different sizes, the Tuple attributes are filled with zero value.
+// It returns the first error returned by the iteratee.
+func ZipByErr7[A, B, C, D, E, F, G, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, iteratee func(a A, b B, c C, d D, e E, f F, g G) (Out, error)) ([]Out, error) {
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g)}))
+ result := make([]Out, size)
+
+ for index := uint(0); index < size; index++ {
+ r, err := iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ NthOrEmpty(d, index),
+ NthOrEmpty(e, index),
+ NthOrEmpty(f, index),
+ NthOrEmpty(g, index),
+ )
+ if err != nil {
+ return nil, err
+ }
+ result[index] = r
+ }
+
+ return result, nil
+}
+
+// ZipByErr8 creates a slice of transformed elements, the first of which contains the first elements
+// of the given slices, the second of which contains the second elements of the given slices, and so on.
+// When collections are different sizes, the Tuple attributes are filled with zero value.
+// It returns the first error returned by the iteratee.
+func ZipByErr8[A, B, C, D, E, F, G, H, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, iteratee func(a A, b B, c C, d D, e E, f F, g G, h H) (Out, error)) ([]Out, error) {
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h)}))
+ result := make([]Out, size)
+
+ for index := uint(0); index < size; index++ {
+ r, err := iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ NthOrEmpty(d, index),
+ NthOrEmpty(e, index),
+ NthOrEmpty(f, index),
+ NthOrEmpty(g, index),
+ NthOrEmpty(h, index),
+ )
+ if err != nil {
+ return nil, err
+ }
+ result[index] = r
+ }
+
+ return result, nil
+}
+
+// ZipByErr9 creates a slice of transformed elements, the first of which contains the first elements
+// of the given slices, the second of which contains the second elements of the given slices, and so on.
+// When collections are different sizes, the Tuple attributes are filled with zero value.
+// It returns the first error returned by the iteratee.
+func ZipByErr9[A, B, C, D, E, F, G, H, I, Out any](a []A, b []B, c []C, d []D, e []E, f []F, g []G, h []H, i []I, iteratee func(a A, b B, c C, d D, e E, f F, g G, h H, i I) (Out, error)) ([]Out, error) {
+ size := uint(Max([]int{len(a), len(b), len(c), len(d), len(e), len(f), len(g), len(h), len(i)}))
+ result := make([]Out, size)
+
+ for index := uint(0); index < size; index++ {
+ r, err := iteratee(
+ NthOrEmpty(a, index),
+ NthOrEmpty(b, index),
+ NthOrEmpty(c, index),
+ NthOrEmpty(d, index),
+ NthOrEmpty(e, index),
+ NthOrEmpty(f, index),
+ NthOrEmpty(g, index),
+ NthOrEmpty(h, index),
+ NthOrEmpty(i, index),
+ )
+ if err != nil {
+ return nil, err
+ }
+ result[index] = r
+ }
+
+ return result, nil
+}
+
// Unzip2 accepts a slice of grouped elements and creates a slice regrouping the elements
// to their pre-zip configuration.
// Play: https://go.dev/play/p/ciHugugvaAW
@@ -884,6 +1020,222 @@ func UnzipBy9[In, A, B, C, D, E, F, G, H, I any](items []In, iteratee func(In) (
return r1, r2, r3, r4, r5, r6, r7, r8, r9
}
+// UnzipByErr2 iterates over a collection and creates a slice regrouping the elements
+// to their pre-zip configuration.
+// It returns the first error returned by the iteratee.
+func UnzipByErr2[In, A, B any](items []In, iteratee func(In) (a A, b B, err error)) ([]A, []B, error) {
+ size := len(items)
+ r1 := make([]A, 0, size)
+ r2 := make([]B, 0, size)
+
+ for i := range items {
+ a, b, err := iteratee(items[i])
+ if err != nil {
+ return nil, nil, err
+ }
+ r1 = append(r1, a)
+ r2 = append(r2, b)
+ }
+
+ return r1, r2, nil
+}
+
+// UnzipByErr3 iterates over a collection and creates a slice regrouping the elements
+// to their pre-zip configuration.
+// It returns the first error returned by the iteratee.
+func UnzipByErr3[In, A, B, C any](items []In, iteratee func(In) (a A, b B, c C, err error)) ([]A, []B, []C, error) {
+ size := len(items)
+ r1 := make([]A, 0, size)
+ r2 := make([]B, 0, size)
+ r3 := make([]C, 0, size)
+
+ for i := range items {
+ a, b, c, err := iteratee(items[i])
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ r1 = append(r1, a)
+ r2 = append(r2, b)
+ r3 = append(r3, c)
+ }
+
+ return r1, r2, r3, nil
+}
+
+// UnzipByErr4 iterates over a collection and creates a slice regrouping the elements
+// to their pre-zip configuration.
+// It returns the first error returned by the iteratee.
+func UnzipByErr4[In, A, B, C, D any](items []In, iteratee func(In) (a A, b B, c C, d D, err error)) ([]A, []B, []C, []D, error) {
+ size := len(items)
+ r1 := make([]A, 0, size)
+ r2 := make([]B, 0, size)
+ r3 := make([]C, 0, size)
+ r4 := make([]D, 0, size)
+
+ for i := range items {
+ a, b, c, d, err := iteratee(items[i])
+ if err != nil {
+ return nil, nil, nil, nil, err
+ }
+ r1 = append(r1, a)
+ r2 = append(r2, b)
+ r3 = append(r3, c)
+ r4 = append(r4, d)
+ }
+
+ return r1, r2, r3, r4, nil
+}
+
+// UnzipByErr5 iterates over a collection and creates a slice regrouping the elements
+// to their pre-zip configuration.
+// It returns the first error returned by the iteratee.
+func UnzipByErr5[In, A, B, C, D, E any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, err error)) ([]A, []B, []C, []D, []E, error) {
+ size := len(items)
+ r1 := make([]A, 0, size)
+ r2 := make([]B, 0, size)
+ r3 := make([]C, 0, size)
+ r4 := make([]D, 0, size)
+ r5 := make([]E, 0, size)
+
+ for i := range items {
+ a, b, c, d, e, err := iteratee(items[i])
+ if err != nil {
+ return nil, nil, nil, nil, nil, err
+ }
+ r1 = append(r1, a)
+ r2 = append(r2, b)
+ r3 = append(r3, c)
+ r4 = append(r4, d)
+ r5 = append(r5, e)
+ }
+
+ return r1, r2, r3, r4, r5, nil
+}
+
+// UnzipByErr6 iterates over a collection and creates a slice regrouping the elements
+// to their pre-zip configuration.
+// It returns the first error returned by the iteratee.
+func UnzipByErr6[In, A, B, C, D, E, F any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, err error)) ([]A, []B, []C, []D, []E, []F, error) {
+ size := len(items)
+ r1 := make([]A, 0, size)
+ r2 := make([]B, 0, size)
+ r3 := make([]C, 0, size)
+ r4 := make([]D, 0, size)
+ r5 := make([]E, 0, size)
+ r6 := make([]F, 0, size)
+
+ for i := range items {
+ a, b, c, d, e, f, err := iteratee(items[i])
+ if err != nil {
+ return nil, nil, nil, nil, nil, nil, err
+ }
+ r1 = append(r1, a)
+ r2 = append(r2, b)
+ r3 = append(r3, c)
+ r4 = append(r4, d)
+ r5 = append(r5, e)
+ r6 = append(r6, f)
+ }
+
+ return r1, r2, r3, r4, r5, r6, nil
+}
+
+// UnzipByErr7 iterates over a collection and creates a slice regrouping the elements
+// to their pre-zip configuration.
+// It returns the first error returned by the iteratee.
+func UnzipByErr7[In, A, B, C, D, E, F, G any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, g G, err error)) ([]A, []B, []C, []D, []E, []F, []G, error) {
+ size := len(items)
+ r1 := make([]A, 0, size)
+ r2 := make([]B, 0, size)
+ r3 := make([]C, 0, size)
+ r4 := make([]D, 0, size)
+ r5 := make([]E, 0, size)
+ r6 := make([]F, 0, size)
+ r7 := make([]G, 0, size)
+
+ for i := range items {
+ a, b, c, d, e, f, g, err := iteratee(items[i])
+ if err != nil {
+ return nil, nil, nil, nil, nil, nil, nil, err
+ }
+ r1 = append(r1, a)
+ r2 = append(r2, b)
+ r3 = append(r3, c)
+ r4 = append(r4, d)
+ r5 = append(r5, e)
+ r6 = append(r6, f)
+ r7 = append(r7, g)
+ }
+
+ return r1, r2, r3, r4, r5, r6, r7, nil
+}
+
+// UnzipByErr8 iterates over a collection and creates a slice regrouping the elements
+// to their pre-zip configuration.
+// It returns the first error returned by the iteratee.
+func UnzipByErr8[In, A, B, C, D, E, F, G, H any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, g G, h H, err error)) ([]A, []B, []C, []D, []E, []F, []G, []H, error) {
+ size := len(items)
+ r1 := make([]A, 0, size)
+ r2 := make([]B, 0, size)
+ r3 := make([]C, 0, size)
+ r4 := make([]D, 0, size)
+ r5 := make([]E, 0, size)
+ r6 := make([]F, 0, size)
+ r7 := make([]G, 0, size)
+ r8 := make([]H, 0, size)
+
+ for i := range items {
+ a, b, c, d, e, f, g, h, err := iteratee(items[i])
+ if err != nil {
+ return nil, nil, nil, nil, nil, nil, nil, nil, err
+ }
+ r1 = append(r1, a)
+ r2 = append(r2, b)
+ r3 = append(r3, c)
+ r4 = append(r4, d)
+ r5 = append(r5, e)
+ r6 = append(r6, f)
+ r7 = append(r7, g)
+ r8 = append(r8, h)
+ }
+
+ return r1, r2, r3, r4, r5, r6, r7, r8, nil
+}
+
+// UnzipByErr9 iterates over a collection and creates a slice regrouping the elements
+// to their pre-zip configuration.
+// It returns the first error returned by the iteratee.
+func UnzipByErr9[In, A, B, C, D, E, F, G, H, I any](items []In, iteratee func(In) (a A, b B, c C, d D, e E, f F, g G, h H, i I, err error)) ([]A, []B, []C, []D, []E, []F, []G, []H, []I, error) {
+ size := len(items)
+ r1 := make([]A, 0, size)
+ r2 := make([]B, 0, size)
+ r3 := make([]C, 0, size)
+ r4 := make([]D, 0, size)
+ r5 := make([]E, 0, size)
+ r6 := make([]F, 0, size)
+ r7 := make([]G, 0, size)
+ r8 := make([]H, 0, size)
+ r9 := make([]I, 0, size)
+
+ for i := range items {
+ a, b, c, d, e, f, g, h, i, err := iteratee(items[i])
+ if err != nil {
+ return nil, nil, nil, nil, nil, nil, nil, nil, nil, err
+ }
+ r1 = append(r1, a)
+ r2 = append(r2, b)
+ r3 = append(r3, c)
+ r4 = append(r4, d)
+ r5 = append(r5, e)
+ r6 = append(r6, f)
+ r7 = append(r7, g)
+ r8 = append(r8, h)
+ r9 = append(r9, i)
+ }
+
+ return r1, r2, r3, r4, r5, r6, r7, r8, r9, nil
+}
+
// CrossJoin2 combines every item from one list with every item from others.
// It is the cartesian product of lists received as arguments.
// Returns an empty list if a list is empty.
@@ -949,11 +1301,11 @@ func CrossJoin9[A, B, C, D, E, F, G, H, I any](listA []A, listB []B, listC []C,
}
// CrossJoinBy2 combines every item from one list with every item from others.
-// It is the cartesian product of lists received as arguments. The project function
+// It is the cartesian product of lists received as arguments. The transform function
// is used to create the output values.
// Returns an empty list if a list is empty.
// Play: https://go.dev/play/p/8Y7btpvuA-C
-func CrossJoinBy2[A, B, Out any](listA []A, listB []B, project func(a A, b B) Out) []Out {
+func CrossJoinBy2[A, B, Out any](listA []A, listB []B, transform func(a A, b B) Out) []Out {
size := len(listA) * len(listB)
if size == 0 {
return []Out{}
@@ -963,7 +1315,7 @@ func CrossJoinBy2[A, B, Out any](listA []A, listB []B, project func(a A, b B) Ou
for _, a := range listA {
for _, b := range listB {
- result = append(result, project(a, b))
+ result = append(result, transform(a, b))
}
}
@@ -971,11 +1323,11 @@ func CrossJoinBy2[A, B, Out any](listA []A, listB []B, project func(a A, b B) Ou
}
// CrossJoinBy3 combines every item from one list with every item from others.
-// It is the cartesian product of lists received as arguments. The project function
+// It is the cartesian product of lists received as arguments. The transform function
// is used to create the output values.
// Returns an empty list if a list is empty.
// Play: https://go.dev/play/p/3z4y5x6w7v8
-func CrossJoinBy3[A, B, C, Out any](listA []A, listB []B, listC []C, project func(a A, b B, c C) Out) []Out {
+func CrossJoinBy3[A, B, C, Out any](listA []A, listB []B, listC []C, transform func(a A, b B, c C) Out) []Out {
size := len(listA) * len(listB) * len(listC)
if size == 0 {
return []Out{}
@@ -986,7 +1338,7 @@ func CrossJoinBy3[A, B, C, Out any](listA []A, listB []B, listC []C, project fun
for _, a := range listA {
for _, b := range listB {
for _, c := range listC {
- result = append(result, project(a, b, c))
+ result = append(result, transform(a, b, c))
}
}
}
@@ -995,11 +1347,11 @@ func CrossJoinBy3[A, B, C, Out any](listA []A, listB []B, listC []C, project fun
}
// CrossJoinBy4 combines every item from one list with every item from others.
-// It is the cartesian product of lists received as arguments. The project function
+// It is the cartesian product of lists received as arguments. The transform function
// is used to create the output values.
// Returns an empty list if a list is empty.
// Play: https://go.dev/play/p/8b9c0d1e2f3
-func CrossJoinBy4[A, B, C, D, Out any](listA []A, listB []B, listC []C, listD []D, project func(a A, b B, c C, d D) Out) []Out {
+func CrossJoinBy4[A, B, C, D, Out any](listA []A, listB []B, listC []C, listD []D, transform func(a A, b B, c C, d D) Out) []Out {
size := len(listA) * len(listB) * len(listC) * len(listD)
if size == 0 {
return []Out{}
@@ -1011,7 +1363,7 @@ func CrossJoinBy4[A, B, C, D, Out any](listA []A, listB []B, listC []C, listD []
for _, b := range listB {
for _, c := range listC {
for _, d := range listD {
- result = append(result, project(a, b, c, d))
+ result = append(result, transform(a, b, c, d))
}
}
}
@@ -1021,11 +1373,11 @@ func CrossJoinBy4[A, B, C, D, Out any](listA []A, listB []B, listC []C, listD []
}
// CrossJoinBy5 combines every item from one list with every item from others.
-// It is the cartesian product of lists received as arguments. The project function
+// It is the cartesian product of lists received as arguments. The transform function
// is used to create the output values.
// Returns an empty list if a list is empty.
// Play: https://go.dev/play/p/4g5h6i7j8k9
-func CrossJoinBy5[A, B, C, D, E, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, project func(a A, b B, c C, d D, e E) Out) []Out {
+func CrossJoinBy5[A, B, C, D, E, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, transform func(a A, b B, c C, d D, e E) Out) []Out {
size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE)
if size == 0 {
return []Out{}
@@ -1038,7 +1390,7 @@ func CrossJoinBy5[A, B, C, D, E, Out any](listA []A, listB []B, listC []C, listD
for _, c := range listC {
for _, d := range listD {
for _, e := range listE {
- result = append(result, project(a, b, c, d, e))
+ result = append(result, transform(a, b, c, d, e))
}
}
}
@@ -1049,11 +1401,11 @@ func CrossJoinBy5[A, B, C, D, E, Out any](listA []A, listB []B, listC []C, listD
}
// CrossJoinBy6 combines every item from one list with every item from others.
-// It is the cartesian product of lists received as arguments. The project function
+// It is the cartesian product of lists received as arguments. The transform function
// is used to create the output values.
// Returns an empty list if a list is empty.
// Play: https://go.dev/play/p/1l2m3n4o5p6
-func CrossJoinBy6[A, B, C, D, E, F, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, project func(a A, b B, c C, d D, e E, f F) Out) []Out {
+func CrossJoinBy6[A, B, C, D, E, F, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, transform func(a A, b B, c C, d D, e E, f F) Out) []Out {
size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF)
if size == 0 {
return []Out{}
@@ -1067,7 +1419,7 @@ func CrossJoinBy6[A, B, C, D, E, F, Out any](listA []A, listB []B, listC []C, li
for _, d := range listD {
for _, e := range listE {
for _, f := range listF {
- result = append(result, project(a, b, c, d, e, f))
+ result = append(result, transform(a, b, c, d, e, f))
}
}
}
@@ -1079,11 +1431,11 @@ func CrossJoinBy6[A, B, C, D, E, F, Out any](listA []A, listB []B, listC []C, li
}
// CrossJoinBy7 combines every item from one list with every item from others.
-// It is the cartesian product of lists received as arguments. The project function
+// It is the cartesian product of lists received as arguments. The transform function
// is used to create the output values.
// Returns an empty list if a list is empty.
// Play: https://go.dev/play/p/7q8r9s0t1u2
-func CrossJoinBy7[A, B, C, D, E, F, G, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, project func(a A, b B, c C, d D, e E, f F, g G) Out) []Out {
+func CrossJoinBy7[A, B, C, D, E, F, G, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, transform func(a A, b B, c C, d D, e E, f F, g G) Out) []Out {
size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG)
if size == 0 {
return []Out{}
@@ -1098,7 +1450,7 @@ func CrossJoinBy7[A, B, C, D, E, F, G, Out any](listA []A, listB []B, listC []C,
for _, e := range listE {
for _, f := range listF {
for _, g := range listG {
- result = append(result, project(a, b, c, d, e, f, g))
+ result = append(result, transform(a, b, c, d, e, f, g))
}
}
}
@@ -1111,11 +1463,11 @@ func CrossJoinBy7[A, B, C, D, E, F, G, Out any](listA []A, listB []B, listC []C,
}
// CrossJoinBy8 combines every item from one list with every item from others.
-// It is the cartesian product of lists received as arguments. The project function
+// It is the cartesian product of lists received as arguments. The transform function
// is used to create the output values.
// Returns an empty list if a list is empty.
// Play: https://go.dev/play/p/3v4w5x6y7z8
-func CrossJoinBy8[A, B, C, D, E, F, G, H, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, project func(a A, b B, c C, d D, e E, f F, g G, h H) Out) []Out {
+func CrossJoinBy8[A, B, C, D, E, F, G, H, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, transform func(a A, b B, c C, d D, e E, f F, g G, h H) Out) []Out {
size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG) * len(listH)
if size == 0 {
return []Out{}
@@ -1131,7 +1483,7 @@ func CrossJoinBy8[A, B, C, D, E, F, G, H, Out any](listA []A, listB []B, listC [
for _, f := range listF {
for _, g := range listG {
for _, h := range listH {
- result = append(result, project(a, b, c, d, e, f, g, h))
+ result = append(result, transform(a, b, c, d, e, f, g, h))
}
}
}
@@ -1145,11 +1497,11 @@ func CrossJoinBy8[A, B, C, D, E, F, G, H, Out any](listA []A, listB []B, listC [
}
// CrossJoinBy9 combines every item from one list with every item from others.
-// It is the cartesian product of lists received as arguments. The project function
+// It is the cartesian product of lists received as arguments. The transform function
// is used to create the output values.
// Returns an empty list if a list is empty.
// Play: https://go.dev/play/p/9a0b1c2d3e4
-func CrossJoinBy9[A, B, C, D, E, F, G, H, I, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, listI []I, project func(a A, b B, c C, d D, e E, f F, g G, h H, i I) Out) []Out {
+func CrossJoinBy9[A, B, C, D, E, F, G, H, I, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, listI []I, transform func(a A, b B, c C, d D, e E, f F, g G, h H, i I) Out) []Out {
size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG) * len(listH) * len(listI)
if size == 0 {
return []Out{}
@@ -1166,7 +1518,7 @@ func CrossJoinBy9[A, B, C, D, E, F, G, H, I, Out any](listA []A, listB []B, list
for _, g := range listG {
for _, h := range listH {
for _, i := range listI {
- result = append(result, project(a, b, c, d, e, f, g, h, i))
+ result = append(result, transform(a, b, c, d, e, f, g, h, i))
}
}
}
@@ -1179,3 +1531,267 @@ func CrossJoinBy9[A, B, C, D, E, F, G, H, I, Out any](listA []A, listB []B, list
return result
}
+
+// CrossJoinByErr2 combines every item from one list with every item from others.
+// It is the cartesian product of lists received as arguments. The transform function
+// is used to create the output values.
+// Returns an empty list if a list is empty.
+// It returns the first error returned by the transform function.
+func CrossJoinByErr2[A, B, Out any](listA []A, listB []B, transform func(a A, b B) (Out, error)) ([]Out, error) {
+ size := len(listA) * len(listB)
+ if size == 0 {
+ return []Out{}, nil
+ }
+
+ result := make([]Out, 0, size)
+
+ for _, a := range listA {
+ for _, b := range listB {
+ r, err := transform(a, b)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, r)
+ }
+ }
+
+ return result, nil
+}
+
+// CrossJoinByErr3 combines every item from one list with every item from others.
+// It is the cartesian product of lists received as arguments. The transform function
+// is used to create the output values.
+// Returns an empty list if a list is empty.
+// It returns the first error returned by the transform function.
+func CrossJoinByErr3[A, B, C, Out any](listA []A, listB []B, listC []C, transform func(a A, b B, c C) (Out, error)) ([]Out, error) {
+ size := len(listA) * len(listB) * len(listC)
+ if size == 0 {
+ return []Out{}, nil
+ }
+
+ result := make([]Out, 0, size)
+
+ for _, a := range listA {
+ for _, b := range listB {
+ for _, c := range listC {
+ r, err := transform(a, b, c)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, r)
+ }
+ }
+ }
+
+ return result, nil
+}
+
+// CrossJoinByErr4 combines every item from one list with every item from others.
+// It is the cartesian product of lists received as arguments. The transform function
+// is used to create the output values.
+// Returns an empty list if a list is empty.
+// It returns the first error returned by the transform function.
+func CrossJoinByErr4[A, B, C, D, Out any](listA []A, listB []B, listC []C, listD []D, transform func(a A, b B, c C, d D) (Out, error)) ([]Out, error) {
+ size := len(listA) * len(listB) * len(listC) * len(listD)
+ if size == 0 {
+ return []Out{}, nil
+ }
+
+ result := make([]Out, 0, size)
+
+ for _, a := range listA {
+ for _, b := range listB {
+ for _, c := range listC {
+ for _, d := range listD {
+ r, err := transform(a, b, c, d)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, r)
+ }
+ }
+ }
+ }
+
+ return result, nil
+}
+
+// CrossJoinByErr5 combines every item from one list with every item from others.
+// It is the cartesian product of lists received as arguments. The transform function
+// is used to create the output values.
+// Returns an empty list if a list is empty.
+// It returns the first error returned by the transform function.
+func CrossJoinByErr5[A, B, C, D, E, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, transform func(a A, b B, c C, d D, e E) (Out, error)) ([]Out, error) {
+ size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE)
+ if size == 0 {
+ return []Out{}, nil
+ }
+
+ result := make([]Out, 0, size)
+
+ for _, a := range listA {
+ for _, b := range listB {
+ for _, c := range listC {
+ for _, d := range listD {
+ for _, e := range listE {
+ r, err := transform(a, b, c, d, e)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, r)
+ }
+ }
+ }
+ }
+ }
+
+ return result, nil
+}
+
+// CrossJoinByErr6 combines every item from one list with every item from others.
+// It is the cartesian product of lists received as arguments. The transform function
+// is used to create the output values.
+// Returns an empty list if a list is empty.
+// It returns the first error returned by the transform function.
+func CrossJoinByErr6[A, B, C, D, E, F, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, transform func(a A, b B, c C, d D, e E, f F) (Out, error)) ([]Out, error) {
+ size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF)
+ if size == 0 {
+ return []Out{}, nil
+ }
+
+ result := make([]Out, 0, size)
+
+ for _, a := range listA {
+ for _, b := range listB {
+ for _, c := range listC {
+ for _, d := range listD {
+ for _, e := range listE {
+ for _, f := range listF {
+ r, err := transform(a, b, c, d, e, f)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, r)
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return result, nil
+}
+
+// CrossJoinByErr7 combines every item from one list with every item from others.
+// It is the cartesian product of lists received as arguments. The transform function
+// is used to create the output values.
+// Returns an empty list if a list is empty.
+// It returns the first error returned by the transform function.
+func CrossJoinByErr7[A, B, C, D, E, F, G, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, transform func(a A, b B, c C, d D, e E, f F, g G) (Out, error)) ([]Out, error) {
+ size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG)
+ if size == 0 {
+ return []Out{}, nil
+ }
+
+ result := make([]Out, 0, size)
+
+ for _, a := range listA {
+ for _, b := range listB {
+ for _, c := range listC {
+ for _, d := range listD {
+ for _, e := range listE {
+ for _, f := range listF {
+ for _, g := range listG {
+ r, err := transform(a, b, c, d, e, f, g)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, r)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return result, nil
+}
+
+// CrossJoinByErr8 combines every item from one list with every item from others.
+// It is the cartesian product of lists received as arguments. The transform function
+// is used to create the output values.
+// Returns an empty list if a list is empty.
+// It returns the first error returned by the transform function.
+func CrossJoinByErr8[A, B, C, D, E, F, G, H, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, transform func(a A, b B, c C, d D, e E, f F, g G, h H) (Out, error)) ([]Out, error) {
+ size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG) * len(listH)
+ if size == 0 {
+ return []Out{}, nil
+ }
+
+ result := make([]Out, 0, size)
+
+ for _, a := range listA {
+ for _, b := range listB {
+ for _, c := range listC {
+ for _, d := range listD {
+ for _, e := range listE {
+ for _, f := range listF {
+ for _, g := range listG {
+ for _, h := range listH {
+ r, err := transform(a, b, c, d, e, f, g, h)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, r)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return result, nil
+}
+
+// CrossJoinByErr9 combines every item from one list with every item from others.
+// It is the cartesian product of lists received as arguments. The transform function
+// is used to create the output values.
+// Returns an empty list if a list is empty.
+// It returns the first error returned by the transform function.
+func CrossJoinByErr9[A, B, C, D, E, F, G, H, I, Out any](listA []A, listB []B, listC []C, listD []D, listE []E, listF []F, listG []G, listH []H, listI []I, transform func(a A, b B, c C, d D, e E, f F, g G, h H, i I) (Out, error)) ([]Out, error) {
+ size := len(listA) * len(listB) * len(listC) * len(listD) * len(listE) * len(listF) * len(listG) * len(listH) * len(listI)
+ if size == 0 {
+ return []Out{}, nil
+ }
+
+ result := make([]Out, 0, size)
+
+ for _, a := range listA {
+ for _, b := range listB {
+ for _, c := range listC {
+ for _, d := range listD {
+ for _, e := range listE {
+ for _, f := range listF {
+ for _, g := range listG {
+ for _, h := range listH {
+ for _, i := range listI {
+ r, err := transform(a, b, c, d, e, f, g, h, i)
+ if err != nil {
+ return nil, err
+ }
+ result = append(result, r)
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return result, nil
+}
diff --git a/vendor/github.com/samber/lo/type_manipulation.go b/vendor/github.com/samber/lo/type_manipulation.go
index 281f316dc..c2f93d735 100644
--- a/vendor/github.com/samber/lo/type_manipulation.go
+++ b/vendor/github.com/samber/lo/type_manipulation.go
@@ -173,7 +173,7 @@ func CoalesceOrEmpty[T comparable](v ...T) T {
// Play: https://go.dev/play/p/Gyo9otyvFHH
func CoalesceSlice[T any](v ...[]T) ([]T, bool) {
for i := range v {
- if v[i] != nil && len(v[i]) > 0 {
+ if len(v[i]) > 0 {
return v[i], true
}
}
@@ -184,7 +184,7 @@ func CoalesceSlice[T any](v ...[]T) ([]T, bool) {
// Play: https://go.dev/play/p/Gyo9otyvFHH
func CoalesceSliceOrEmpty[T any](v ...[]T) []T {
for i := range v {
- if v[i] != nil && len(v[i]) > 0 {
+ if len(v[i]) > 0 {
return v[i]
}
}
@@ -195,7 +195,7 @@ func CoalesceSliceOrEmpty[T any](v ...[]T) []T {
// Play: https://go.dev/play/p/Gyo9otyvFHH
func CoalesceMap[K comparable, V any](v ...map[K]V) (map[K]V, bool) {
for i := range v {
- if v[i] != nil && len(v[i]) > 0 {
+ if len(v[i]) > 0 {
return v[i], true
}
}
@@ -206,7 +206,7 @@ func CoalesceMap[K comparable, V any](v ...map[K]V) (map[K]V, bool) {
// Play: https://go.dev/play/p/Gyo9otyvFHH
func CoalesceMapOrEmpty[K comparable, V any](v ...map[K]V) map[K]V {
for i := range v {
- if v[i] != nil && len(v[i]) > 0 {
+ if len(v[i]) > 0 {
return v[i]
}
}
diff --git a/vendor/github.com/samber/slog-common/Makefile b/vendor/github.com/samber/slog-common/Makefile
index accd04f70..5cd537ab5 100644
--- a/vendor/github.com/samber/slog-common/Makefile
+++ b/vendor/github.com/samber/slog-common/Makefile
@@ -12,6 +12,15 @@ bench:
watch-bench:
reflex -t 50ms -s -- sh -c 'go test -benchmem -count 3 -bench ./...'
+fuzz:
+ go test -fuzz=FuzzAttrsToMap -fuzztime=10s ./...
+ go test -fuzz=FuzzValueToString -fuzztime=10s ./...
+ go test -fuzz=FuzzAnyValueToString -fuzztime=10s ./...
+ go test -fuzz=FuzzFindAttribute -fuzztime=10s ./...
+ go test -fuzz=FuzzRemoveEmptyAttrs -fuzztime=10s ./...
+ go test -fuzz=FuzzUniqAttrs -fuzztime=10s ./...
+ go test -fuzz=FuzzAttrsToString -fuzztime=10s ./...
+
coverage:
go test -v -coverprofile=cover.out -covermode=atomic ./...
go tool cover -html=cover.out -o cover.html
diff --git a/vendor/github.com/samber/slog-common/attributes.go b/vendor/github.com/samber/slog-common/attributes.go
index 548de0daa..a029bbe92 100644
--- a/vendor/github.com/samber/slog-common/attributes.go
+++ b/vendor/github.com/samber/slog-common/attributes.go
@@ -63,7 +63,7 @@ func AttrsToMap(attrs ...slog.Attr) map[string]any {
}
func RecordToAttrsMap(r slog.Record) map[string]any {
- attrs := make([]slog.Attr, r.NumAttrs())
+ attrs := make([]slog.Attr, 0, r.NumAttrs())
r.Attrs(func(attr slog.Attr) bool {
attrs = append(attrs, attr)
return true
diff --git a/vendor/github.com/samber/slog-common/finder.go b/vendor/github.com/samber/slog-common/finder.go
index 1dce87ee0..d20e980fd 100644
--- a/vendor/github.com/samber/slog-common/finder.go
+++ b/vendor/github.com/samber/slog-common/finder.go
@@ -18,10 +18,12 @@ func FindAttrByGroupAndKey(attrs []slog.Attr, groups []string, key string) (slog
}
for i := range attrs {
- if attrs[i].Key == key && attrs[i].Value.Kind() == slog.KindGroup {
- return FindAttrByGroupAndKey(attrs[i].Value.Group(), groups[1:], key)
+ if attrs[i].Key == groups[0] && attrs[i].Value.Kind() == slog.KindGroup {
+ attr, found := FindAttrByGroupAndKey(attrs[i].Value.Group(), groups[1:], key)
+ if found {
+ return attr, true
+ }
}
}
-
return slog.Attr{}, false
}
diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/config.go b/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/config.go
index 2a64fd330..7f3a93522 100644
--- a/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/config.go
+++ b/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/config.go
@@ -5,6 +5,8 @@ package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.g
import (
"context"
+ "os"
+ "strings"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
@@ -29,6 +31,14 @@ type InterceptorFilter func(*InterceptorInfo) bool
// A Filter must return true if the request should be instrumented.
type Filter func(*stats.RPCTagInfo) bool
+type semconvMode int
+
+const (
+ semconvModeNew semconvMode = iota // Default
+ semconvModeOld
+ semconvModeDup
+)
+
// config is a group of options for this instrumentation.
type config struct {
Filter Filter
@@ -47,6 +57,8 @@ type config struct {
ReceivedEvent bool
SentEvent bool
+
+ semconvMode semconvMode
}
// Option applies an option value for a config.
@@ -66,13 +78,33 @@ func newConfig(opts []Option) *config {
Propagators: otel.GetTextMapPropagator(),
TracerProvider: otel.GetTracerProvider(),
MeterProvider: otel.GetMeterProvider(),
+ semconvMode: parseSemconvMode(),
}
for _, o := range opts {
o.apply(c)
}
+
return c
}
+func parseSemconvMode() semconvMode {
+ val := os.Getenv("OTEL_SEMCONV_STABILITY_OPT_IN")
+ if val == "" {
+ return semconvModeNew
+ }
+ parts := strings.SplitSeq(val, ",")
+ for p := range parts {
+ p = strings.TrimSpace(p)
+ if p == "rpc/dup" {
+ return semconvModeDup
+ }
+ if p == "rpc/old" {
+ return semconvModeOld
+ }
+ }
+ return semconvModeNew
+}
+
// WithPublicEndpoint configures the Handler to link the span with an incoming
// span context. If this option is not provided, then the association is a child
// association instead of a link.
diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal/parse.go b/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal/parse.go
index 7d7f172e0..5174f4dd1 100644
--- a/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal/parse.go
+++ b/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal/parse.go
@@ -8,6 +8,7 @@ import (
"strings"
"go.opentelemetry.io/otel/attribute"
+ oldsemconv "go.opentelemetry.io/otel/semconv/v1.37.0" //nolint:depguard // Use of v1.37.0 is required for backward compatibility stability opt-in.
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
)
@@ -25,3 +26,28 @@ func ParseFullMethod(fullMethod string) (string, []attribute.KeyValue) {
name := fullMethod[1:]
return name, []attribute.KeyValue{semconv.RPCMethod(name)}
}
+
+// ParseFullMethodOld returns a span name following the old OpenTelemetry semantic
+// conventions as well as all applicable span attribute.KeyValue attributes based
+// on a gRPC's FullMethod.
+// Based on the implementation in:
+// https://github.com/open-telemetry/opentelemetry-go-contrib/blob/072dcf8ad7e5e48b506e05720b29d8b078759606/instrumentation/google.golang.org/grpc/otelgrpc/internal/parse.go#L20
+func ParseFullMethodOld(fullMethod string) (string, []attribute.KeyValue) {
+ if !strings.HasPrefix(fullMethod, "/") {
+ return fullMethod, nil
+ }
+ name := fullMethod[1:]
+ parts := strings.Split(name, "/")
+ if len(parts) < 2 {
+ return name, []attribute.KeyValue{
+ attribute.String("rpc.system", "grpc"),
+ }
+ }
+ service := parts[0]
+ method := parts[1]
+ return name, []attribute.KeyValue{
+ oldsemconv.RPCSystemKey.String("grpc"),
+ oldsemconv.RPCServiceKey.String(service),
+ oldsemconv.RPCMethodKey.String(method),
+ }
+}
diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go b/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go
index be9282f29..c9c44c159 100644
--- a/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go
+++ b/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/stats_handler.go
@@ -12,11 +12,12 @@ import (
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/metric"
+ oldrpcconv "go.opentelemetry.io/otel/semconv/v1.37.0/rpcconv" //nolint:depguard // Use of v1.37.0 is required for backward compatibility stability opt-in.
semconv "go.opentelemetry.io/otel/semconv/v1.40.0"
"go.opentelemetry.io/otel/semconv/v1.40.0/rpcconv"
"go.opentelemetry.io/otel/trace"
+
grpc_codes "google.golang.org/grpc/codes"
- "google.golang.org/grpc/peer"
"google.golang.org/grpc/stats"
"google.golang.org/grpc/status"
@@ -35,7 +36,8 @@ type serverHandler struct {
tracer trace.Tracer
- duration rpcconv.ServerCallDuration
+ duration rpcconv.ServerCallDuration
+ oldDuration oldrpcconv.ServerDuration
}
// NewServerHandler creates a stats.Handler for a gRPC server.
@@ -59,15 +61,26 @@ func NewServerHandler(opts ...Option) stats.Handler {
)
var err error
- h.duration, err = rpcconv.NewServerCallDuration(
- meter,
- metric.WithExplicitBucketBoundaries(
- 0.005, 0.01, 0.025, 0.05, 0.075, 0.1,
- 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,
- ),
- )
- if err != nil {
- otel.Handle(err)
+ if c.semconvMode == semconvModeOld || c.semconvMode == semconvModeDup {
+ oldDur, err := oldrpcconv.NewServerDuration(meter)
+ if err != nil {
+ otel.Handle(err)
+ } else {
+ h.oldDuration = oldDur
+ }
+ }
+
+ if c.semconvMode == semconvModeNew || c.semconvMode == semconvModeDup {
+ h.duration, err = rpcconv.NewServerCallDuration(
+ meter,
+ metric.WithExplicitBucketBoundaries(
+ 0.005, 0.01, 0.025, 0.05, 0.075, 0.1,
+ 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,
+ ),
+ )
+ if err != nil {
+ otel.Handle(err)
+ }
}
return h
@@ -86,8 +99,23 @@ func (*serverHandler) HandleConn(context.Context, stats.ConnStats) {
func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
ctx = extract(ctx, h.Propagators)
- name, attrs := internal.ParseFullMethod(info.FullMethodName)
- attrs = append(attrs, semconv.RPCSystemNameGRPC)
+ var name string
+ var attrs []attribute.KeyValue
+
+ switch h.semconvMode {
+ case semconvModeOld:
+ name, attrs = internal.ParseFullMethodOld(info.FullMethodName)
+ case semconvModeDup:
+ var attrsNew, attrsOld []attribute.KeyValue
+ name, attrsNew = internal.ParseFullMethod(info.FullMethodName)
+ _, attrsOld = internal.ParseFullMethodOld(info.FullMethodName)
+ // Combine both. We append New last so its rpc.method (fully qualified) wins when deduplicated.
+ attrs = append(append([]attribute.KeyValue{}, attrsOld...), attrsNew...)
+ attrs = append(attrs, semconv.RPCSystemNameGRPC) // New convention
+ default: // semconvModeNew
+ name, attrs = internal.ParseFullMethod(info.FullMethodName)
+ attrs = append(attrs, semconv.RPCSystemNameGRPC)
+ }
record := true
if h.Filter != nil {
@@ -131,10 +159,19 @@ func (h *serverHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont
// HandleRPC processes the RPC stats.
func (h *serverHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
+ var dur metric.Float64Histogram
+ if h.semconvMode == semconvModeNew || h.semconvMode == semconvModeDup {
+ dur = h.duration.Inst()
+ }
+ var oldDur metric.Float64Histogram
+ if h.semconvMode == semconvModeOld || h.semconvMode == semconvModeDup {
+ oldDur = h.oldDuration.Inst()
+ }
h.handleRPC(
ctx,
rs,
- h.duration.Inst(),
+ dur,
+ oldDur,
serverStatus,
)
}
@@ -144,7 +181,8 @@ type clientHandler struct {
tracer trace.Tracer
- duration rpcconv.ClientCallDuration
+ duration rpcconv.ClientCallDuration
+ oldDuration oldrpcconv.ClientDuration
}
// NewClientHandler creates a stats.Handler for a gRPC client.
@@ -168,15 +206,26 @@ func NewClientHandler(opts ...Option) stats.Handler {
)
var err error
- h.duration, err = rpcconv.NewClientCallDuration(
- meter,
- metric.WithExplicitBucketBoundaries(
- 0.005, 0.01, 0.025, 0.05, 0.075, 0.1,
- 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,
- ),
- )
- if err != nil {
- otel.Handle(err)
+ if c.semconvMode == semconvModeOld || c.semconvMode == semconvModeDup {
+ oldDur, err := oldrpcconv.NewClientDuration(meter)
+ if err != nil {
+ otel.Handle(err)
+ } else {
+ h.oldDuration = oldDur
+ }
+ }
+
+ if c.semconvMode == semconvModeNew || c.semconvMode == semconvModeDup {
+ h.duration, err = rpcconv.NewClientCallDuration(
+ meter,
+ metric.WithExplicitBucketBoundaries(
+ 0.005, 0.01, 0.025, 0.05, 0.075, 0.1,
+ 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,
+ ),
+ )
+ if err != nil {
+ otel.Handle(err)
+ }
}
return h
@@ -184,8 +233,23 @@ func NewClientHandler(opts ...Option) stats.Handler {
// TagRPC can attach some information to the given context.
func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) context.Context {
- name, attrs := internal.ParseFullMethod(info.FullMethodName)
- attrs = append(attrs, semconv.RPCSystemNameGRPC)
+ var name string
+ var attrs []attribute.KeyValue
+
+ switch h.semconvMode {
+ case semconvModeOld:
+ name, attrs = internal.ParseFullMethodOld(info.FullMethodName)
+ case semconvModeDup:
+ var attrsNew, attrsOld []attribute.KeyValue
+ name, attrsNew = internal.ParseFullMethod(info.FullMethodName)
+ _, attrsOld = internal.ParseFullMethodOld(info.FullMethodName)
+ // Combine both. We append New last so its rpc.method (fully qualified) wins when deduplicated.
+ attrs = append(append([]attribute.KeyValue{}, attrsOld...), attrsNew...)
+ attrs = append(attrs, semconv.RPCSystemNameGRPC) // New convention
+ default: // semconvModeNew
+ name, attrs = internal.ParseFullMethod(info.FullMethodName)
+ attrs = append(attrs, semconv.RPCSystemNameGRPC)
+ }
record := true
if h.Filter != nil {
@@ -219,10 +283,19 @@ func (h *clientHandler) TagRPC(ctx context.Context, info *stats.RPCTagInfo) cont
// HandleRPC processes the RPC stats.
func (h *clientHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) {
+ var dur metric.Float64Histogram
+ if h.semconvMode == semconvModeNew || h.semconvMode == semconvModeDup {
+ dur = h.duration.Inst()
+ }
+ var oldDur metric.Float64Histogram
+ if h.semconvMode == semconvModeOld || h.semconvMode == semconvModeDup {
+ oldDur = h.oldDuration.Inst()
+ }
h.handleRPC(
ctx,
rs,
- h.duration.Inst(),
+ dur,
+ oldDur,
func(s *status.Status) (codes.Code, string) {
return codes.Error, s.Message()
},
@@ -243,6 +316,7 @@ func (*config) handleRPC(
ctx context.Context,
rs stats.RPCStats,
duration metric.Float64Histogram,
+ oldDuration metric.Float64Histogram,
recordStatus func(*status.Status) (codes.Code, string),
) {
gctx, _ := ctx.Value(gRPCContextKey{}).(*gRPCContext)
@@ -255,12 +329,23 @@ func (*config) handleRPC(
switch rs := rs.(type) {
case *stats.Begin:
case *stats.InPayload:
+ case *stats.InHeader:
+ if !rs.Client && rs.LocalAddr != nil {
+ if span.IsRecording() {
+ span.SetAttributes(serverAddrAttrs(rs.LocalAddr.String())...)
+ }
+ // TODO: add server.address and server.port to metrics once the API supports opt-in attributes.
+ }
case *stats.OutPayload:
case *stats.OutTrailer:
case *stats.OutHeader:
- if span.IsRecording() {
- if p, ok := peer.FromContext(ctx); ok {
- span.SetAttributes(serverAddrAttrs(p.Addr.String())...)
+ if rs.Client && rs.RemoteAddr != nil && (span.IsRecording() || gctx != nil) {
+ attrs := serverAddrAttrs(rs.RemoteAddr.String())
+ if span.IsRecording() {
+ span.SetAttributes(attrs...)
+ }
+ if gctx != nil {
+ gctx.metricAttrs = append(gctx.metricAttrs, attrs...)
}
}
case *stats.End:
@@ -298,7 +383,13 @@ func (*config) handleRPC(
// Measure right before calling Record() to capture as much elapsed time as possible.
elapsedTime := float64(rs.EndTime.Sub(rs.BeginTime)) / float64(time.Second)
- duration.Record(ctx, elapsedTime, recordOpts...)
+ if duration != nil {
+ duration.Record(ctx, elapsedTime, recordOpts...)
+ }
+ if oldDuration != nil {
+ oldDuration.Record(ctx, elapsedTime*1000.0, recordOpts...)
+ }
+
default:
return
}
diff --git a/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/version.go b/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/version.go
index 535e63a07..1e81d31fe 100644
--- a/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/version.go
+++ b/vendor/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/version.go
@@ -4,4 +4,4 @@
package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc"
// Version is the current release version of the gRPC instrumentation.
-const Version = "0.67.0"
+const Version = "0.68.0"
diff --git a/vendor/go.opentelemetry.io/otel/.golangci.yml b/vendor/go.opentelemetry.io/otel/.golangci.yml
index d12c8920a..db1f55101 100644
--- a/vendor/go.opentelemetry.io/otel/.golangci.yml
+++ b/vendor/go.opentelemetry.io/otel/.golangci.yml
@@ -17,6 +17,7 @@ linters:
- ineffassign
- misspell
- modernize
+ - noctx
- perfsprint
- revive
- staticcheck
@@ -88,6 +89,16 @@ linters:
deny:
- pkg: go.opentelemetry.io/otel/exporters/otlp/otlptrace/internal
desc: Do not use cross-module internal packages.
+ semconv:
+ list-mode: lax
+ files:
+ - "!**/semconv/**"
+ - "!**/exporters/zipkin/**"
+ deny:
+ - pkg: go.opentelemetry.io/otel/semconv
+ desc: "Use go.opentelemetry.io/otel/semconv/v1.40.0 instead. If a newer semconv version has been released, update the depguard rule."
+ allow:
+ - go.opentelemetry.io/otel/semconv/v1.40.0
gocritic:
disabled-checks:
- appendAssign
diff --git a/vendor/go.opentelemetry.io/otel/CHANGELOG.md b/vendor/go.opentelemetry.io/otel/CHANGELOG.md
index ab7263014..20edda441 100644
--- a/vendor/go.opentelemetry.io/otel/CHANGELOG.md
+++ b/vendor/go.opentelemetry.io/otel/CHANGELOG.md
@@ -11,6 +11,49 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
+## [1.43.0/0.65.0/0.19.0] 2026-04-02
+
+### Added
+
+- Add `IsRandom` and `WithRandom` on `TraceFlags`, and `IsRandom` on `SpanContext` in `go.opentelemetry.io/otel/trace` for [W3C Trace Context Level 2 Random Trace ID Flag](https://www.w3.org/TR/trace-context-2/#random-trace-id-flag) support. (#8012)
+- Add service detection with `WithService` in `go.opentelemetry.io/otel/sdk/resource`. (#7642)
+- Add `DefaultWithContext` and `EnvironmentWithContext` in `go.opentelemetry.io/otel/sdk/resource` to support plumbing `context.Context` through default and environment detectors. (#8051)
+- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc`. (#8038)
+- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc`. (#8038)
+- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc`. (#8038)
+- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp`. (#8038)
+- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp`. (#8038)
+- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp`. (#8038)
+- Support attributes with empty value (`attribute.EMPTY`) in `go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest`. (#8038)
+- Add support for per-series start time tracking for cumulative metrics in `go.opentelemetry.io/otel/sdk/metric`.
+ Set `OTEL_GO_X_PER_SERIES_START_TIMESTAMPS=true` to enable. (#8060)
+- Add `WithCardinalityLimitSelector` for metric reader for configuring cardinality limits specific to the instrument kind. (#7855)
+
+### Changed
+
+- Introduce the `EMPTY` Type in `go.opentelemetry.io/otel/attribute` to reflect that an empty value is now a valid value, with `INVALID` remaining as a deprecated alias of `EMPTY`. (#8038)
+- Improve slice handling in `go.opentelemetry.io/otel/attribute` to optimize short slice values with fixed-size fast paths. (#8039)
+- Improve performance of span metric recording in `go.opentelemetry.io/otel/sdk/trace` by returning early if self-observability is not enabled. (#8067)
+- Improve formatting of metric data diffs in `go.opentelemetry.io/otel/sdk/metric/metricdata/metricdatatest`. (#8073)
+
+### Deprecated
+
+- Deprecate `INVALID` in `go.opentelemetry.io/otel/attribute`. Use `EMPTY` instead. (#8038)
+
+### Fixed
+
+- Return spec-compliant `TraceIdRatioBased` description. This is a breaking behavioral change, but it is necessary to
+ make the implementation [spec-compliant](https://opentelemetry.io/docs/specs/otel/trace/sdk/#traceidratiobased). (#8027)
+- Fix a race condition in `go.opentelemetry.io/otel/sdk/metric` where the lastvalue aggregation could collect the value 0 even when no zero-value measurements were recorded. (#8056)
+- Limit HTTP response body to 4 MiB in `go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp` to mitigate excessive memory usage caused by a misconfigured or malicious server.
+ Responses exceeding the limit are treated as non-retryable errors. (#8108)
+- Limit HTTP response body to 4 MiB in `go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp` to mitigate excessive memory usage caused by a misconfigured or malicious server.
+ Responses exceeding the limit are treated as non-retryable errors. (#8108)
+- Limit HTTP response body to 4 MiB in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` to mitigate excessive memory usage caused by a misconfigured or malicious server.
+ Responses exceeding the limit are treated as non-retryable errors. (#8108)
+- `WithHostID` detector in `go.opentelemetry.io/otel/sdk/resource` to use full path for `kenv` command on BSD. (#8113)
+- Fix missing `request.GetBody` in `go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp` to correctly handle HTTP2 GOAWAY frame. (#8096)
+
## [1.42.0/0.64.0/0.18.0/0.0.16] 2026-03-06
### Added
@@ -3576,7 +3619,8 @@ It contains api and sdk for trace and meter.
- CircleCI build CI manifest files.
- CODEOWNERS file to track owners of this project.
-[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.42.0...HEAD
+[Unreleased]: https://github.com/open-telemetry/opentelemetry-go/compare/v1.43.0...HEAD
+[1.43.0/0.65.0/0.19.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.43.0
[1.42.0/0.64.0/0.18.0/0.0.16]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.42.0
[1.41.0/0.63.0/0.17.0/0.0.15]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.41.0
[1.40.0/0.62.0/0.16.0]: https://github.com/open-telemetry/opentelemetry-go/releases/tag/v1.40.0
diff --git a/vendor/go.opentelemetry.io/otel/Makefile b/vendor/go.opentelemetry.io/otel/Makefile
index 9f6e6b511..42466f2d6 100644
--- a/vendor/go.opentelemetry.io/otel/Makefile
+++ b/vendor/go.opentelemetry.io/otel/Makefile
@@ -38,10 +38,14 @@ CROSSLINK = $(TOOLS)/crosslink
$(TOOLS)/crosslink: PACKAGE=go.opentelemetry.io/build-tools/crosslink
SEMCONVKIT = $(TOOLS)/semconvkit
+SEMCONVKIT_FILES := $(sort $(shell find $(TOOLS_MOD_DIR)/semconvkit -type f))
$(TOOLS)/semconvkit: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/semconvkit
+$(TOOLS)/semconvkit: $(SEMCONVKIT_FILES)
VERIFYREADMES = $(TOOLS)/verifyreadmes
+VERIFYREADMES_FILES := $(sort $(shell find $(TOOLS_MOD_DIR)/verifyreadmes -type f))
$(TOOLS)/verifyreadmes: PACKAGE=go.opentelemetry.io/otel/$(TOOLS_MOD_DIR)/verifyreadmes
+$(TOOLS)/verifyreadmes: $(VERIFYREADMES_FILES)
GOLANGCI_LINT = $(TOOLS)/golangci-lint
$(TOOLS)/golangci-lint: PACKAGE=github.com/golangci/golangci-lint/v2/cmd/golangci-lint
diff --git a/vendor/go.opentelemetry.io/otel/RELEASING.md b/vendor/go.opentelemetry.io/otel/RELEASING.md
index 861756fd7..6aff7548c 100644
--- a/vendor/go.opentelemetry.io/otel/RELEASING.md
+++ b/vendor/go.opentelemetry.io/otel/RELEASING.md
@@ -4,7 +4,9 @@
Create a `Version Release` issue to track the release process.
-## Semantic Convention Generation
+## Semantic Convention Upgrade
+
+### Semantic Convention Generation
New versions of the [OpenTelemetry Semantic Conventions] mean new versions of the `semconv` package need to be generated.
The `semconv-generate` make target is used for this.
@@ -22,6 +24,43 @@ make semconv-generate # Uses the exported TAG.
This should create a new sub-package of [`semconv`](./semconv).
Ensure things look correct before submitting a pull request to include the addition.
+The `CHANGELOG.md` should also be updated to reflect the new changes:
+
+```md
+- The `go.opentelemetry.io/otel/semconv/` package. The package contains semantic conventions from the `` version of the OpenTelemetry Semantic Conventions. See the [migration documentation](./semconv//MIGRATION.md) for information on how to upgrade from `go.opentelemetry.io/otel/semconv/`. (#PR_NUMBER)
+```
+
+> **Tip:** Change to the release and prior version to match the changes
+
+### Update semconv imports
+
+Once the new semconv module has been generated, update all semconv imports throughout the codebase to reference the new version:
+
+```go
+// Before
+semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
+"go.opentelemetry.io/otel/semconv/v1.37.0/otelconv"
+
+
+// After
+semconv "go.opentelemetry.io/otel/semconv/v1.39.0"
+"go.opentelemetry.io/otel/semconv/v1.39.0/otelconv"
+```
+
+Once complete, run `make` to check for any compilation or test failures.
+
+#### Handling attribute changes
+
+Some semconv releases might add new attributes or impact attributes that are currently being used. Changes could stem from a simple renaming, to more complex changes like merging attributes and property values being changed.
+
+One should update the code to the new attributes that supersede the impacted ones, hence sticking to the semantic conventions. However, legacy attributes might still be emitted in accordance to the `OTEL_SEMCONV_STABILITY_OPT_IN` environment variable.
+
+For an example on how such migration might have to be tracked and performed, see issue [#7806](https://github.com/open-telemetry/opentelemetry-go/issues/7806).
+
+### Go contrib linter update
+
+Update [.golangci.yml](https://github.com/open-telemetry/opentelemetry-go-contrib/blob/main/.golangci.yml) in [opentelemetry-go-contrib](https://github.com/open-telemetry/opentelemetry-go-contrib/) to mandate the new semconv version.
+
## Breaking changes validation
You can run `make gorelease` which runs [gorelease](https://pkg.go.dev/golang.org/x/exp/cmd/gorelease) to ensure that there are no unwanted changes made in the public API.
diff --git a/vendor/go.opentelemetry.io/otel/attribute/encoder.go b/vendor/go.opentelemetry.io/otel/attribute/encoder.go
index 6cc1a1655..771dd69c5 100644
--- a/vendor/go.opentelemetry.io/otel/attribute/encoder.go
+++ b/vendor/go.opentelemetry.io/otel/attribute/encoder.go
@@ -53,7 +53,7 @@ var (
_ Encoder = &defaultAttrEncoder{}
// encoderIDCounter is for generating IDs for other attribute encoders.
- encoderIDCounter uint64
+ encoderIDCounter atomic.Uint64
defaultEncoderOnce sync.Once
defaultEncoderID = NewEncoderID()
@@ -64,7 +64,7 @@ var (
// once per each type of attribute encoder. Preferably in init() or in var
// definition.
func NewEncoderID() EncoderID {
- return EncoderID{value: atomic.AddUint64(&encoderIDCounter, 1)}
+ return EncoderID{value: encoderIDCounter.Add(1)}
}
// DefaultEncoder returns an attribute encoder that encodes attributes in such
diff --git a/vendor/go.opentelemetry.io/otel/attribute/hash.go b/vendor/go.opentelemetry.io/otel/attribute/hash.go
index 6aa69aeae..b09caaa6d 100644
--- a/vendor/go.opentelemetry.io/otel/attribute/hash.go
+++ b/vendor/go.opentelemetry.io/otel/attribute/hash.go
@@ -27,6 +27,7 @@ const (
int64SliceID uint64 = 3762322556277578591 // "_[]int64" (little endian)
float64SliceID uint64 = 7308324551835016539 // "[]double" (little endian)
stringSliceID uint64 = 7453010373645655387 // "[]string" (little endian)
+ emptyID uint64 = 7305809155345288421 // "__empty_" (little endian)
)
// hashKVs returns a new xxHash64 hash of kvs.
@@ -80,7 +81,8 @@ func hashKV(h xxhash.Hash, kv KeyValue) xxhash.Hash {
for i := 0; i < rv.Len(); i++ {
h = h.String(rv.Index(i).String())
}
- case INVALID:
+ case EMPTY:
+ h = h.Uint64(emptyID)
default:
// Logging is an alternative, but using the internal logger here
// causes an import cycle so it is not done.
diff --git a/vendor/go.opentelemetry.io/otel/attribute/internal/attribute.go b/vendor/go.opentelemetry.io/otel/attribute/internal/attribute.go
index 7f5eae877..d9f51fa2d 100644
--- a/vendor/go.opentelemetry.io/otel/attribute/internal/attribute.go
+++ b/vendor/go.opentelemetry.io/otel/attribute/internal/attribute.go
@@ -11,80 +11,63 @@ import (
"reflect"
)
-// BoolSliceValue converts a bool slice into an array with same elements as slice.
-func BoolSliceValue(v []bool) any {
- cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[bool]())).Elem()
+// sliceElem is the exact set of element types stored in attribute slice values.
+// Using a closed set prevents accidental instantiations for unsupported types.
+type sliceElem interface {
+ bool | int64 | float64 | string
+}
+
+// SliceValue converts a slice into an array with the same elements.
+func SliceValue[T sliceElem](v []T) any {
+ // Keep only the common tiny-slice cases out of reflection. Extending this
+ // much further increases code size for diminishing benefit while larger
+ // slices still need the generic reflective path to preserve comparability.
+ // This matches the short lengths that show up most often in local
+ // benchmarks and semantic convention examples while leaving larger, less
+ // predictable slices on the generic reflective path.
+ switch len(v) {
+ case 0:
+ return [0]T{}
+ case 1:
+ return [1]T{v[0]}
+ case 2:
+ return [2]T{v[0], v[1]}
+ case 3:
+ return [3]T{v[0], v[1], v[2]}
+ }
+
+ return sliceValueReflect(v)
+}
+
+// AsSlice converts an array into a slice with the same elements.
+func AsSlice[T sliceElem](v any) []T {
+ // Mirror the small fixed-array fast path used by SliceValue.
+ switch a := v.(type) {
+ case [0]T:
+ return []T{}
+ case [1]T:
+ return []T{a[0]}
+ case [2]T:
+ return []T{a[0], a[1]}
+ case [3]T:
+ return []T{a[0], a[1], a[2]}
+ }
+
+ return asSliceReflect[T](v)
+}
+
+func sliceValueReflect[T sliceElem](v []T) any {
+ cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[T]())).Elem()
reflect.Copy(cp, reflect.ValueOf(v))
return cp.Interface()
}
-// Int64SliceValue converts an int64 slice into an array with same elements as slice.
-func Int64SliceValue(v []int64) any {
- cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[int64]())).Elem()
- reflect.Copy(cp, reflect.ValueOf(v))
- return cp.Interface()
-}
-
-// Float64SliceValue converts a float64 slice into an array with same elements as slice.
-func Float64SliceValue(v []float64) any {
- cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[float64]())).Elem()
- reflect.Copy(cp, reflect.ValueOf(v))
- return cp.Interface()
-}
-
-// StringSliceValue converts a string slice into an array with same elements as slice.
-func StringSliceValue(v []string) any {
- cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[string]())).Elem()
- reflect.Copy(cp, reflect.ValueOf(v))
- return cp.Interface()
-}
-
-// AsBoolSlice converts a bool array into a slice into with same elements as array.
-func AsBoolSlice(v any) []bool {
+func asSliceReflect[T sliceElem](v any) []T {
rv := reflect.ValueOf(v)
- if rv.Type().Kind() != reflect.Array {
+ if !rv.IsValid() || rv.Kind() != reflect.Array || rv.Type().Elem() != reflect.TypeFor[T]() {
return nil
}
- cpy := make([]bool, rv.Len())
- if len(cpy) > 0 {
- _ = reflect.Copy(reflect.ValueOf(cpy), rv)
- }
- return cpy
-}
-
-// AsInt64Slice converts an int64 array into a slice into with same elements as array.
-func AsInt64Slice(v any) []int64 {
- rv := reflect.ValueOf(v)
- if rv.Type().Kind() != reflect.Array {
- return nil
- }
- cpy := make([]int64, rv.Len())
- if len(cpy) > 0 {
- _ = reflect.Copy(reflect.ValueOf(cpy), rv)
- }
- return cpy
-}
-
-// AsFloat64Slice converts a float64 array into a slice into with same elements as array.
-func AsFloat64Slice(v any) []float64 {
- rv := reflect.ValueOf(v)
- if rv.Type().Kind() != reflect.Array {
- return nil
- }
- cpy := make([]float64, rv.Len())
- if len(cpy) > 0 {
- _ = reflect.Copy(reflect.ValueOf(cpy), rv)
- }
- return cpy
-}
-
-// AsStringSlice converts a string array into a slice into with same elements as array.
-func AsStringSlice(v any) []string {
- rv := reflect.ValueOf(v)
- if rv.Type().Kind() != reflect.Array {
- return nil
- }
- cpy := make([]string, rv.Len())
+ cpy := make([]T, rv.Len())
if len(cpy) > 0 {
_ = reflect.Copy(reflect.ValueOf(cpy), rv)
}
diff --git a/vendor/go.opentelemetry.io/otel/attribute/kv.go b/vendor/go.opentelemetry.io/otel/attribute/kv.go
index 8c6928ca7..0cc368018 100644
--- a/vendor/go.opentelemetry.io/otel/attribute/kv.go
+++ b/vendor/go.opentelemetry.io/otel/attribute/kv.go
@@ -15,7 +15,7 @@ type KeyValue struct {
// Valid reports whether kv is a valid OpenTelemetry attribute.
func (kv KeyValue) Valid() bool {
- return kv.Key.Defined() && kv.Value.Type() != INVALID
+ return kv.Key.Defined()
}
// Bool creates a KeyValue with a BOOL Value type.
diff --git a/vendor/go.opentelemetry.io/otel/attribute/type_string.go b/vendor/go.opentelemetry.io/otel/attribute/type_string.go
index 24f1fa37d..6c04448d6 100644
--- a/vendor/go.opentelemetry.io/otel/attribute/type_string.go
+++ b/vendor/go.opentelemetry.io/otel/attribute/type_string.go
@@ -8,7 +8,7 @@ func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
- _ = x[INVALID-0]
+ _ = x[EMPTY-0]
_ = x[BOOL-1]
_ = x[INT64-2]
_ = x[FLOAT64-3]
@@ -19,9 +19,9 @@ func _() {
_ = x[STRINGSLICE-8]
}
-const _Type_name = "INVALIDBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICE"
+const _Type_name = "EMPTYBOOLINT64FLOAT64STRINGBOOLSLICEINT64SLICEFLOAT64SLICESTRINGSLICE"
-var _Type_index = [...]uint8{0, 7, 11, 16, 23, 29, 38, 48, 60, 71}
+var _Type_index = [...]uint8{0, 5, 9, 14, 21, 27, 36, 46, 58, 69}
func (i Type) String() string {
idx := int(i) - 0
diff --git a/vendor/go.opentelemetry.io/otel/attribute/value.go b/vendor/go.opentelemetry.io/otel/attribute/value.go
index 5931e7129..db04b1326 100644
--- a/vendor/go.opentelemetry.io/otel/attribute/value.go
+++ b/vendor/go.opentelemetry.io/otel/attribute/value.go
@@ -6,7 +6,6 @@ package attribute // import "go.opentelemetry.io/otel/attribute"
import (
"encoding/json"
"fmt"
- "reflect"
"strconv"
attribute "go.opentelemetry.io/otel/attribute/internal"
@@ -18,6 +17,8 @@ import (
type Type int // nolint: revive // redefines builtin Type.
// Value represents the value part in key-value pairs.
+//
+// Note that the zero value is a valid empty value.
type Value struct {
vtype Type
numeric uint64
@@ -26,8 +27,8 @@ type Value struct {
}
const (
- // INVALID is used for a Value with no value set.
- INVALID Type = iota
+ // EMPTY is used for a Value with no value set.
+ EMPTY Type = iota
// BOOL is a boolean Type Value.
BOOL
// INT64 is a 64-bit signed integral Type Value.
@@ -44,6 +45,10 @@ const (
FLOAT64SLICE
// STRINGSLICE is a slice of strings Type Value.
STRINGSLICE
+ // INVALID is used for a Value with no value set.
+ //
+ // Deprecated: Use EMPTY instead as an empty value is a valid value.
+ INVALID = EMPTY
)
// BoolValue creates a BOOL Value.
@@ -56,7 +61,7 @@ func BoolValue(v bool) Value {
// BoolSliceValue creates a BOOLSLICE Value.
func BoolSliceValue(v []bool) Value {
- return Value{vtype: BOOLSLICE, slice: attribute.BoolSliceValue(v)}
+ return Value{vtype: BOOLSLICE, slice: attribute.SliceValue(v)}
}
// IntValue creates an INT64 Value.
@@ -64,16 +69,30 @@ func IntValue(v int) Value {
return Int64Value(int64(v))
}
-// IntSliceValue creates an INTSLICE Value.
+// IntSliceValue creates an INT64SLICE Value.
func IntSliceValue(v []int) Value {
- cp := reflect.New(reflect.ArrayOf(len(v), reflect.TypeFor[int64]()))
- for i, val := range v {
- cp.Elem().Index(i).SetInt(int64(val))
- }
- return Value{
- vtype: INT64SLICE,
- slice: cp.Elem().Interface(),
+ val := Value{vtype: INT64SLICE}
+
+ // Avoid the common tiny-slice cases from allocating a new slice.
+ switch len(v) {
+ case 0:
+ val.slice = [0]int64{}
+ case 1:
+ val.slice = [1]int64{int64(v[0])}
+ case 2:
+ val.slice = [2]int64{int64(v[0]), int64(v[1])}
+ case 3:
+ val.slice = [3]int64{int64(v[0]), int64(v[1]), int64(v[2])}
+ default:
+ // Fallback to a new slice for larger slices.
+ cp := make([]int64, len(v))
+ for i, val := range v {
+ cp[i] = int64(val)
+ }
+ val.slice = attribute.SliceValue(cp)
}
+
+ return val
}
// Int64Value creates an INT64 Value.
@@ -86,7 +105,7 @@ func Int64Value(v int64) Value {
// Int64SliceValue creates an INT64SLICE Value.
func Int64SliceValue(v []int64) Value {
- return Value{vtype: INT64SLICE, slice: attribute.Int64SliceValue(v)}
+ return Value{vtype: INT64SLICE, slice: attribute.SliceValue(v)}
}
// Float64Value creates a FLOAT64 Value.
@@ -99,7 +118,7 @@ func Float64Value(v float64) Value {
// Float64SliceValue creates a FLOAT64SLICE Value.
func Float64SliceValue(v []float64) Value {
- return Value{vtype: FLOAT64SLICE, slice: attribute.Float64SliceValue(v)}
+ return Value{vtype: FLOAT64SLICE, slice: attribute.SliceValue(v)}
}
// StringValue creates a STRING Value.
@@ -112,7 +131,7 @@ func StringValue(v string) Value {
// StringSliceValue creates a STRINGSLICE Value.
func StringSliceValue(v []string) Value {
- return Value{vtype: STRINGSLICE, slice: attribute.StringSliceValue(v)}
+ return Value{vtype: STRINGSLICE, slice: attribute.SliceValue(v)}
}
// Type returns a type of the Value.
@@ -136,7 +155,7 @@ func (v Value) AsBoolSlice() []bool {
}
func (v Value) asBoolSlice() []bool {
- return attribute.AsBoolSlice(v.slice)
+ return attribute.AsSlice[bool](v.slice)
}
// AsInt64 returns the int64 value. Make sure that the Value's type is
@@ -155,7 +174,7 @@ func (v Value) AsInt64Slice() []int64 {
}
func (v Value) asInt64Slice() []int64 {
- return attribute.AsInt64Slice(v.slice)
+ return attribute.AsSlice[int64](v.slice)
}
// AsFloat64 returns the float64 value. Make sure that the Value's
@@ -174,7 +193,7 @@ func (v Value) AsFloat64Slice() []float64 {
}
func (v Value) asFloat64Slice() []float64 {
- return attribute.AsFloat64Slice(v.slice)
+ return attribute.AsSlice[float64](v.slice)
}
// AsString returns the string value. Make sure that the Value's type
@@ -193,7 +212,7 @@ func (v Value) AsStringSlice() []string {
}
func (v Value) asStringSlice() []string {
- return attribute.AsStringSlice(v.slice)
+ return attribute.AsSlice[string](v.slice)
}
type unknownValueType struct{}
@@ -217,6 +236,8 @@ func (v Value) AsInterface() any {
return v.stringly
case STRINGSLICE:
return v.asStringSlice()
+ case EMPTY:
+ return nil
}
return unknownValueType{}
}
@@ -252,6 +273,8 @@ func (v Value) Emit() string {
return string(j)
case STRING:
return v.stringly
+ case EMPTY:
+ return ""
default:
return "unknown"
}
diff --git a/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile b/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile
index f0cc942ba..7a9b3c055 100644
--- a/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile
+++ b/vendor/go.opentelemetry.io/otel/dependencies.Dockerfile
@@ -1,4 +1,4 @@
# This is a renovate-friendly source of Docker images.
FROM python:3.13.6-slim-bullseye@sha256:e98b521460ee75bca92175c16247bdf7275637a8faaeb2bcfa19d879ae5c4b9a AS python
-FROM otel/weaver:v0.21.2@sha256:2401de985c38bdb98b43918e2f43aa36b2afed4aa5669ac1c1de0a17301cd36d AS weaver
+FROM otel/weaver:v0.22.1@sha256:33ae522ae4b71c1c562563c1d81f46aa0f79f088a0873199143a1f11ac30e5c9 AS weaver
FROM avtodev/markdown-lint:v1@sha256:6aeedc2f49138ce7a1cd0adffc1b1c0321b841dc2102408967d9301c031949ee AS markdown
diff --git a/vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go b/vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go
index bfeb73e81..694b64a31 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/internal/x/features.go
@@ -37,3 +37,18 @@ var Observability = newFeature(
return "", false
},
)
+
+// PerSeriesStartTimestamps is an experimental feature flag that determines if the SDK
+// uses the new Start Timestamps specification.
+//
+// To enable this feature set the OTEL_GO_X_PER_SERIES_START_TIMESTAMPS environment variable
+// to the case-insensitive string value of "true".
+var PerSeriesStartTimestamps = newFeature(
+ []string{"PER_SERIES_START_TIMESTAMPS"},
+ func(v string) (bool, bool) {
+ if strings.EqualFold(v, "true") {
+ return true, true
+ }
+ return false, false
+ },
+)
diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/config.go b/vendor/go.opentelemetry.io/otel/sdk/resource/config.go
index 0d6e213d9..a3d647d92 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/resource/config.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/resource/config.go
@@ -193,3 +193,11 @@ func WithContainer() Option {
func WithContainerID() Option {
return WithDetectors(cgroupContainerIDDetector{})
}
+
+// WithService adds all the Service attributes to the configured Resource.
+func WithService() Option {
+ return WithDetectors(
+ defaultServiceInstanceIDDetector{},
+ defaultServiceNameDetector{},
+ )
+}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go
index 1a72b756a..755c08242 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/resource/host_id.go
@@ -31,19 +31,19 @@ type hostIDReaderBSD struct {
readFile fileReader
}
-// read attempts to read the machine-id from /etc/hostid. If not found it will
-// execute `kenv -q smbios.system.uuid`. If neither location yields an id an
-// error will be returned.
+// read attempts to read the machine-id from /etc/hostid.
+// If not found it will execute: /bin/kenv -q smbios.system.uuid.
+// If neither location yields an id an error will be returned.
func (r *hostIDReaderBSD) read() (string, error) {
if result, err := r.readFile("/etc/hostid"); err == nil {
return strings.TrimSpace(result), nil
}
- if result, err := r.execCommand("kenv", "-q", "smbios.system.uuid"); err == nil {
+ if result, err := r.execCommand("/bin/kenv", "-q", "smbios.system.uuid"); err == nil {
return strings.TrimSpace(result), nil
}
- return "", errors.New("host id not found in: /etc/hostid or kenv")
+ return "", errors.New("host id not found in: /etc/hostid or /bin/kenv")
}
// hostIDReaderDarwin implements hostIDReader.
diff --git a/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go b/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go
index 28e1e4f7e..f715be53e 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/resource/resource.go
@@ -232,6 +232,15 @@ func Empty() *Resource {
// Default returns an instance of Resource with a default
// "service.name" and OpenTelemetrySDK attributes.
func Default() *Resource {
+ return DefaultWithContext(context.Background())
+}
+
+// DefaultWithContext returns an instance of Resource with a default
+// "service.name" and OpenTelemetrySDK attributes.
+//
+// If the default resource has already been initialized, the provided ctx
+// is ignored and the cached resource is returned.
+func DefaultWithContext(ctx context.Context) *Resource {
defaultResourceOnce.Do(func() {
var err error
defaultDetectors := []Detector{
@@ -243,7 +252,7 @@ func Default() *Resource {
defaultDetectors = append([]Detector{defaultServiceInstanceIDDetector{}}, defaultDetectors...)
}
defaultResource, err = Detect(
- context.Background(),
+ ctx,
defaultDetectors...,
)
if err != nil {
@@ -260,8 +269,14 @@ func Default() *Resource {
// Environment returns an instance of Resource with attributes
// extracted from the OTEL_RESOURCE_ATTRIBUTES environment variable.
func Environment() *Resource {
+ return EnvironmentWithContext(context.Background())
+}
+
+// EnvironmentWithContext returns an instance of Resource with attributes
+// extracted from the OTEL_RESOURCE_ATTRIBUTES environment variable.
+func EnvironmentWithContext(ctx context.Context) *Resource {
detector := &fromEnv{}
- resource, err := detector.Detect(context.Background())
+ resource, err := detector.Detect(ctx)
if err != nil {
otel.Handle(err)
}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go b/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go
index c7c317fbc..32854b14a 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/trace/batch_span_processor.go
@@ -68,7 +68,7 @@ type batchSpanProcessor struct {
o BatchSpanProcessorOptions
queue chan ReadOnlySpan
- dropped uint32
+ dropped atomic.Uint32
inst *observ.BSP
@@ -293,7 +293,7 @@ func (bsp *batchSpanProcessor) exportSpans(ctx context.Context) error {
}
if l := len(bsp.batch); l > 0 {
- global.Debug("exporting spans", "count", len(bsp.batch), "total_dropped", atomic.LoadUint32(&bsp.dropped))
+ global.Debug("exporting spans", "count", len(bsp.batch), "total_dropped", bsp.dropped.Load())
if bsp.inst != nil {
bsp.inst.Processed(ctx, int64(l))
}
@@ -421,7 +421,7 @@ func (bsp *batchSpanProcessor) enqueueDrop(ctx context.Context, sd ReadOnlySpan)
case bsp.queue <- sd:
return true
default:
- atomic.AddUint32(&bsp.dropped, 1)
+ bsp.dropped.Add(1)
if bsp.inst != nil {
bsp.inst.ProcessedQueueFull(ctx, 1)
}
diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go
index a2334811e..560d316f2 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/trace/internal/observ/tracer.go
@@ -55,6 +55,10 @@ func NewTracer() (Tracer, error) {
func (t Tracer) Enabled() bool { return t.enabled }
func (t Tracer) SpanStarted(ctx context.Context, psc trace.SpanContext, span trace.Span) {
+ if !t.started.Enabled(ctx) {
+ return
+ }
+
key := spanStartedKey{
parent: parentStateNoParent,
sampling: samplingStateDrop,
@@ -89,6 +93,10 @@ func (t Tracer) SpanEnded(ctx context.Context, span trace.Span) {
}
func (t Tracer) spanLive(ctx context.Context, value int64, span trace.Span) {
+ if !t.live.Enabled(ctx) {
+ return
+ }
+
key := spanLiveKey{sampled: span.SpanContext().IsSampled()}
opts := spanLiveOpts[key]
t.live.Add(ctx, value, opts...)
diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go b/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go
index 4ba4acf13..cd40d299d 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/trace/provider.go
@@ -303,14 +303,7 @@ func (p *TracerProvider) Shutdown(ctx context.Context) error {
sps.state.Do(func() {
err = sps.sp.Shutdown(ctx)
})
- if err != nil {
- if retErr == nil {
- retErr = err
- } else {
- // Poor man's list of errors
- retErr = fmt.Errorf("%w; %w", retErr, err)
- }
- }
+ retErr = errors.Join(retErr, err)
}
p.spanProcessors.Store(&spanProcessorStates{})
return retErr
diff --git a/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go b/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go
index 81c5060ad..845e292c2 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/trace/sampling.go
@@ -69,17 +69,17 @@ type traceIDRatioSampler struct {
}
func (ts traceIDRatioSampler) ShouldSample(p SamplingParameters) SamplingResult {
- psc := trace.SpanContextFromContext(p.ParentContext)
+ state := trace.SpanContextFromContext(p.ParentContext).TraceState()
x := binary.BigEndian.Uint64(p.TraceID[8:16]) >> 1
if x < ts.traceIDUpperBound {
return SamplingResult{
Decision: RecordAndSample,
- Tracestate: psc.TraceState(),
+ Tracestate: state,
}
}
return SamplingResult{
Decision: Drop,
- Tracestate: psc.TraceState(),
+ Tracestate: state,
}
}
@@ -94,12 +94,20 @@ func (ts traceIDRatioSampler) Description() string {
//
//nolint:revive // revive complains about stutter of `trace.TraceIDRatioBased`
func TraceIDRatioBased(fraction float64) Sampler {
+ // Cannot use AlwaysSample() and NeverSample(), must return spec-compliant descriptions.
+ // See https://opentelemetry.io/docs/specs/otel/trace/sdk/#traceidratiobased.
if fraction >= 1 {
- return AlwaysSample()
+ return predeterminedSampler{
+ description: "TraceIDRatioBased{1}",
+ decision: RecordAndSample,
+ }
}
if fraction <= 0 {
- fraction = 0
+ return predeterminedSampler{
+ description: "TraceIDRatioBased{0}",
+ decision: Drop,
+ }
}
return &traceIDRatioSampler{
@@ -118,6 +126,7 @@ func (alwaysOnSampler) ShouldSample(p SamplingParameters) SamplingResult {
}
func (alwaysOnSampler) Description() string {
+ // https://opentelemetry.io/docs/specs/otel/trace/sdk/#alwayson
return "AlwaysOnSampler"
}
@@ -139,6 +148,7 @@ func (alwaysOffSampler) ShouldSample(p SamplingParameters) SamplingResult {
}
func (alwaysOffSampler) Description() string {
+ // https://opentelemetry.io/docs/specs/otel/trace/sdk/#alwaysoff
return "AlwaysOffSampler"
}
@@ -147,6 +157,22 @@ func NeverSample() Sampler {
return alwaysOffSampler{}
}
+type predeterminedSampler struct {
+ description string
+ decision SamplingDecision
+}
+
+func (s predeterminedSampler) ShouldSample(p SamplingParameters) SamplingResult {
+ return SamplingResult{
+ Decision: s.decision,
+ Tracestate: trace.SpanContextFromContext(p.ParentContext).TraceState(),
+ }
+}
+
+func (s predeterminedSampler) Description() string {
+ return s.description
+}
+
// ParentBased returns a sampler decorator which behaves differently,
// based on the parent of the span. If the span has no parent,
// the decorated sampler is used to make sampling decision. If the span has
diff --git a/vendor/go.opentelemetry.io/otel/sdk/version.go b/vendor/go.opentelemetry.io/otel/sdk/version.go
index 96e0fc60f..766731dd2 100644
--- a/vendor/go.opentelemetry.io/otel/sdk/version.go
+++ b/vendor/go.opentelemetry.io/otel/sdk/version.go
@@ -6,5 +6,5 @@ package sdk // import "go.opentelemetry.io/otel/sdk"
// Version is the current release version of the OpenTelemetry SDK in use.
func Version() string {
- return "1.42.0"
+ return "1.43.0"
}
diff --git a/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/rpcconv/metric.go b/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/rpcconv/metric.go
new file mode 100644
index 000000000..089b0c457
--- /dev/null
+++ b/vendor/go.opentelemetry.io/otel/semconv/v1.37.0/rpcconv/metric.go
@@ -0,0 +1,1010 @@
+// Code generated from semantic convention specification. DO NOT EDIT.
+
+// Copyright The OpenTelemetry Authors
+// SPDX-License-Identifier: Apache-2.0
+
+// Package rpcconv provides types and functionality for OpenTelemetry semantic
+// conventions in the "rpc" namespace.
+package rpcconv
+
+import (
+ "context"
+ "sync"
+
+ "go.opentelemetry.io/otel/attribute"
+ "go.opentelemetry.io/otel/metric"
+ "go.opentelemetry.io/otel/metric/noop"
+)
+
+var (
+ addOptPool = &sync.Pool{New: func() any { return &[]metric.AddOption{} }}
+ recOptPool = &sync.Pool{New: func() any { return &[]metric.RecordOption{} }}
+)
+
+// ClientDuration is an instrument used to record metric values conforming to the
+// "rpc.client.duration" semantic conventions. It represents the measures the
+// duration of outbound RPC.
+type ClientDuration struct {
+ metric.Float64Histogram
+}
+
+var newClientDurationOpts = []metric.Float64HistogramOption{
+ metric.WithDescription("Measures the duration of outbound RPC."),
+ metric.WithUnit("ms"),
+}
+
+// NewClientDuration returns a new ClientDuration instrument.
+func NewClientDuration(
+ m metric.Meter,
+ opt ...metric.Float64HistogramOption,
+) (ClientDuration, error) {
+ // Check if the meter is nil.
+ if m == nil {
+ return ClientDuration{noop.Float64Histogram{}}, nil
+ }
+
+ if len(opt) == 0 {
+ opt = newClientDurationOpts
+ } else {
+ opt = append(opt, newClientDurationOpts...)
+ }
+
+ i, err := m.Float64Histogram(
+ "rpc.client.duration",
+ opt...,
+ )
+ if err != nil {
+ return ClientDuration{noop.Float64Histogram{}}, err
+ }
+ return ClientDuration{i}, nil
+}
+
+// Inst returns the underlying metric instrument.
+func (m ClientDuration) Inst() metric.Float64Histogram {
+ return m.Float64Histogram
+}
+
+// Name returns the semantic convention name of the instrument.
+func (ClientDuration) Name() string {
+ return "rpc.client.duration"
+}
+
+// Unit returns the semantic convention unit of the instrument
+func (ClientDuration) Unit() string {
+ return "ms"
+}
+
+// Description returns the semantic convention description of the instrument
+func (ClientDuration) Description() string {
+ return "Measures the duration of outbound RPC."
+}
+
+// Record records val to the current distribution for attrs.
+//
+// While streaming RPCs may record this metric as start-of-batch
+// to end-of-batch, it's hard to interpret in practice.
+//
+// **Streaming**: N/A.
+func (m ClientDuration) Record(ctx context.Context, val float64, attrs ...attribute.KeyValue) {
+ if len(attrs) == 0 {
+ m.Float64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributes(attrs...))
+ m.Float64Histogram.Record(ctx, val, *o...)
+}
+
+// RecordSet records val to the current distribution for set.
+//
+// While streaming RPCs may record this metric as start-of-batch
+// to end-of-batch, it's hard to interpret in practice.
+//
+// **Streaming**: N/A.
+func (m ClientDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) {
+ if set.Len() == 0 {
+ m.Float64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributeSet(set))
+ m.Float64Histogram.Record(ctx, val, *o...)
+}
+
+// ClientRequestSize is an instrument used to record metric values conforming to
+// the "rpc.client.request.size" semantic conventions. It represents the measures
+// the size of RPC request messages (uncompressed).
+type ClientRequestSize struct {
+ metric.Int64Histogram
+}
+
+var newClientRequestSizeOpts = []metric.Int64HistogramOption{
+ metric.WithDescription("Measures the size of RPC request messages (uncompressed)."),
+ metric.WithUnit("By"),
+}
+
+// NewClientRequestSize returns a new ClientRequestSize instrument.
+func NewClientRequestSize(
+ m metric.Meter,
+ opt ...metric.Int64HistogramOption,
+) (ClientRequestSize, error) {
+ // Check if the meter is nil.
+ if m == nil {
+ return ClientRequestSize{noop.Int64Histogram{}}, nil
+ }
+
+ if len(opt) == 0 {
+ opt = newClientRequestSizeOpts
+ } else {
+ opt = append(opt, newClientRequestSizeOpts...)
+ }
+
+ i, err := m.Int64Histogram(
+ "rpc.client.request.size",
+ opt...,
+ )
+ if err != nil {
+ return ClientRequestSize{noop.Int64Histogram{}}, err
+ }
+ return ClientRequestSize{i}, nil
+}
+
+// Inst returns the underlying metric instrument.
+func (m ClientRequestSize) Inst() metric.Int64Histogram {
+ return m.Int64Histogram
+}
+
+// Name returns the semantic convention name of the instrument.
+func (ClientRequestSize) Name() string {
+ return "rpc.client.request.size"
+}
+
+// Unit returns the semantic convention unit of the instrument
+func (ClientRequestSize) Unit() string {
+ return "By"
+}
+
+// Description returns the semantic convention description of the instrument
+func (ClientRequestSize) Description() string {
+ return "Measures the size of RPC request messages (uncompressed)."
+}
+
+// Record records val to the current distribution for attrs.
+//
+// **Streaming**: Recorded per message in a streaming batch
+func (m ClientRequestSize) Record(ctx context.Context, val int64, attrs ...attribute.KeyValue) {
+ if len(attrs) == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributes(attrs...))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// RecordSet records val to the current distribution for set.
+//
+// **Streaming**: Recorded per message in a streaming batch
+func (m ClientRequestSize) RecordSet(ctx context.Context, val int64, set attribute.Set) {
+ if set.Len() == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributeSet(set))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// ClientRequestsPerRPC is an instrument used to record metric values conforming
+// to the "rpc.client.requests_per_rpc" semantic conventions. It represents the
+// measures the number of messages received per RPC.
+type ClientRequestsPerRPC struct {
+ metric.Int64Histogram
+}
+
+var newClientRequestsPerRPCOpts = []metric.Int64HistogramOption{
+ metric.WithDescription("Measures the number of messages received per RPC."),
+ metric.WithUnit("{count}"),
+}
+
+// NewClientRequestsPerRPC returns a new ClientRequestsPerRPC instrument.
+func NewClientRequestsPerRPC(
+ m metric.Meter,
+ opt ...metric.Int64HistogramOption,
+) (ClientRequestsPerRPC, error) {
+ // Check if the meter is nil.
+ if m == nil {
+ return ClientRequestsPerRPC{noop.Int64Histogram{}}, nil
+ }
+
+ if len(opt) == 0 {
+ opt = newClientRequestsPerRPCOpts
+ } else {
+ opt = append(opt, newClientRequestsPerRPCOpts...)
+ }
+
+ i, err := m.Int64Histogram(
+ "rpc.client.requests_per_rpc",
+ opt...,
+ )
+ if err != nil {
+ return ClientRequestsPerRPC{noop.Int64Histogram{}}, err
+ }
+ return ClientRequestsPerRPC{i}, nil
+}
+
+// Inst returns the underlying metric instrument.
+func (m ClientRequestsPerRPC) Inst() metric.Int64Histogram {
+ return m.Int64Histogram
+}
+
+// Name returns the semantic convention name of the instrument.
+func (ClientRequestsPerRPC) Name() string {
+ return "rpc.client.requests_per_rpc"
+}
+
+// Unit returns the semantic convention unit of the instrument
+func (ClientRequestsPerRPC) Unit() string {
+ return "{count}"
+}
+
+// Description returns the semantic convention description of the instrument
+func (ClientRequestsPerRPC) Description() string {
+ return "Measures the number of messages received per RPC."
+}
+
+// Record records val to the current distribution for attrs.
+//
+// Should be 1 for all non-streaming RPCs.
+//
+// **Streaming**: This metric is required for server and client streaming RPCs
+func (m ClientRequestsPerRPC) Record(ctx context.Context, val int64, attrs ...attribute.KeyValue) {
+ if len(attrs) == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributes(attrs...))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// RecordSet records val to the current distribution for set.
+//
+// Should be 1 for all non-streaming RPCs.
+//
+// **Streaming**: This metric is required for server and client streaming RPCs
+func (m ClientRequestsPerRPC) RecordSet(ctx context.Context, val int64, set attribute.Set) {
+ if set.Len() == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributeSet(set))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// ClientResponseSize is an instrument used to record metric values conforming to
+// the "rpc.client.response.size" semantic conventions. It represents the
+// measures the size of RPC response messages (uncompressed).
+type ClientResponseSize struct {
+ metric.Int64Histogram
+}
+
+var newClientResponseSizeOpts = []metric.Int64HistogramOption{
+ metric.WithDescription("Measures the size of RPC response messages (uncompressed)."),
+ metric.WithUnit("By"),
+}
+
+// NewClientResponseSize returns a new ClientResponseSize instrument.
+func NewClientResponseSize(
+ m metric.Meter,
+ opt ...metric.Int64HistogramOption,
+) (ClientResponseSize, error) {
+ // Check if the meter is nil.
+ if m == nil {
+ return ClientResponseSize{noop.Int64Histogram{}}, nil
+ }
+
+ if len(opt) == 0 {
+ opt = newClientResponseSizeOpts
+ } else {
+ opt = append(opt, newClientResponseSizeOpts...)
+ }
+
+ i, err := m.Int64Histogram(
+ "rpc.client.response.size",
+ opt...,
+ )
+ if err != nil {
+ return ClientResponseSize{noop.Int64Histogram{}}, err
+ }
+ return ClientResponseSize{i}, nil
+}
+
+// Inst returns the underlying metric instrument.
+func (m ClientResponseSize) Inst() metric.Int64Histogram {
+ return m.Int64Histogram
+}
+
+// Name returns the semantic convention name of the instrument.
+func (ClientResponseSize) Name() string {
+ return "rpc.client.response.size"
+}
+
+// Unit returns the semantic convention unit of the instrument
+func (ClientResponseSize) Unit() string {
+ return "By"
+}
+
+// Description returns the semantic convention description of the instrument
+func (ClientResponseSize) Description() string {
+ return "Measures the size of RPC response messages (uncompressed)."
+}
+
+// Record records val to the current distribution for attrs.
+//
+// **Streaming**: Recorded per response in a streaming batch
+func (m ClientResponseSize) Record(ctx context.Context, val int64, attrs ...attribute.KeyValue) {
+ if len(attrs) == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributes(attrs...))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// RecordSet records val to the current distribution for set.
+//
+// **Streaming**: Recorded per response in a streaming batch
+func (m ClientResponseSize) RecordSet(ctx context.Context, val int64, set attribute.Set) {
+ if set.Len() == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributeSet(set))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// ClientResponsesPerRPC is an instrument used to record metric values conforming
+// to the "rpc.client.responses_per_rpc" semantic conventions. It represents the
+// measures the number of messages sent per RPC.
+type ClientResponsesPerRPC struct {
+ metric.Int64Histogram
+}
+
+var newClientResponsesPerRPCOpts = []metric.Int64HistogramOption{
+ metric.WithDescription("Measures the number of messages sent per RPC."),
+ metric.WithUnit("{count}"),
+}
+
+// NewClientResponsesPerRPC returns a new ClientResponsesPerRPC instrument.
+func NewClientResponsesPerRPC(
+ m metric.Meter,
+ opt ...metric.Int64HistogramOption,
+) (ClientResponsesPerRPC, error) {
+ // Check if the meter is nil.
+ if m == nil {
+ return ClientResponsesPerRPC{noop.Int64Histogram{}}, nil
+ }
+
+ if len(opt) == 0 {
+ opt = newClientResponsesPerRPCOpts
+ } else {
+ opt = append(opt, newClientResponsesPerRPCOpts...)
+ }
+
+ i, err := m.Int64Histogram(
+ "rpc.client.responses_per_rpc",
+ opt...,
+ )
+ if err != nil {
+ return ClientResponsesPerRPC{noop.Int64Histogram{}}, err
+ }
+ return ClientResponsesPerRPC{i}, nil
+}
+
+// Inst returns the underlying metric instrument.
+func (m ClientResponsesPerRPC) Inst() metric.Int64Histogram {
+ return m.Int64Histogram
+}
+
+// Name returns the semantic convention name of the instrument.
+func (ClientResponsesPerRPC) Name() string {
+ return "rpc.client.responses_per_rpc"
+}
+
+// Unit returns the semantic convention unit of the instrument
+func (ClientResponsesPerRPC) Unit() string {
+ return "{count}"
+}
+
+// Description returns the semantic convention description of the instrument
+func (ClientResponsesPerRPC) Description() string {
+ return "Measures the number of messages sent per RPC."
+}
+
+// Record records val to the current distribution for attrs.
+//
+// Should be 1 for all non-streaming RPCs.
+//
+// **Streaming**: This metric is required for server and client streaming RPCs
+func (m ClientResponsesPerRPC) Record(ctx context.Context, val int64, attrs ...attribute.KeyValue) {
+ if len(attrs) == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributes(attrs...))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// RecordSet records val to the current distribution for set.
+//
+// Should be 1 for all non-streaming RPCs.
+//
+// **Streaming**: This metric is required for server and client streaming RPCs
+func (m ClientResponsesPerRPC) RecordSet(ctx context.Context, val int64, set attribute.Set) {
+ if set.Len() == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributeSet(set))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// ServerDuration is an instrument used to record metric values conforming to the
+// "rpc.server.duration" semantic conventions. It represents the measures the
+// duration of inbound RPC.
+type ServerDuration struct {
+ metric.Float64Histogram
+}
+
+var newServerDurationOpts = []metric.Float64HistogramOption{
+ metric.WithDescription("Measures the duration of inbound RPC."),
+ metric.WithUnit("ms"),
+}
+
+// NewServerDuration returns a new ServerDuration instrument.
+func NewServerDuration(
+ m metric.Meter,
+ opt ...metric.Float64HistogramOption,
+) (ServerDuration, error) {
+ // Check if the meter is nil.
+ if m == nil {
+ return ServerDuration{noop.Float64Histogram{}}, nil
+ }
+
+ if len(opt) == 0 {
+ opt = newServerDurationOpts
+ } else {
+ opt = append(opt, newServerDurationOpts...)
+ }
+
+ i, err := m.Float64Histogram(
+ "rpc.server.duration",
+ opt...,
+ )
+ if err != nil {
+ return ServerDuration{noop.Float64Histogram{}}, err
+ }
+ return ServerDuration{i}, nil
+}
+
+// Inst returns the underlying metric instrument.
+func (m ServerDuration) Inst() metric.Float64Histogram {
+ return m.Float64Histogram
+}
+
+// Name returns the semantic convention name of the instrument.
+func (ServerDuration) Name() string {
+ return "rpc.server.duration"
+}
+
+// Unit returns the semantic convention unit of the instrument
+func (ServerDuration) Unit() string {
+ return "ms"
+}
+
+// Description returns the semantic convention description of the instrument
+func (ServerDuration) Description() string {
+ return "Measures the duration of inbound RPC."
+}
+
+// Record records val to the current distribution for attrs.
+//
+// While streaming RPCs may record this metric as start-of-batch
+// to end-of-batch, it's hard to interpret in practice.
+//
+// **Streaming**: N/A.
+func (m ServerDuration) Record(ctx context.Context, val float64, attrs ...attribute.KeyValue) {
+ if len(attrs) == 0 {
+ m.Float64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributes(attrs...))
+ m.Float64Histogram.Record(ctx, val, *o...)
+}
+
+// RecordSet records val to the current distribution for set.
+//
+// While streaming RPCs may record this metric as start-of-batch
+// to end-of-batch, it's hard to interpret in practice.
+//
+// **Streaming**: N/A.
+func (m ServerDuration) RecordSet(ctx context.Context, val float64, set attribute.Set) {
+ if set.Len() == 0 {
+ m.Float64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributeSet(set))
+ m.Float64Histogram.Record(ctx, val, *o...)
+}
+
+// ServerRequestSize is an instrument used to record metric values conforming to
+// the "rpc.server.request.size" semantic conventions. It represents the measures
+// the size of RPC request messages (uncompressed).
+type ServerRequestSize struct {
+ metric.Int64Histogram
+}
+
+var newServerRequestSizeOpts = []metric.Int64HistogramOption{
+ metric.WithDescription("Measures the size of RPC request messages (uncompressed)."),
+ metric.WithUnit("By"),
+}
+
+// NewServerRequestSize returns a new ServerRequestSize instrument.
+func NewServerRequestSize(
+ m metric.Meter,
+ opt ...metric.Int64HistogramOption,
+) (ServerRequestSize, error) {
+ // Check if the meter is nil.
+ if m == nil {
+ return ServerRequestSize{noop.Int64Histogram{}}, nil
+ }
+
+ if len(opt) == 0 {
+ opt = newServerRequestSizeOpts
+ } else {
+ opt = append(opt, newServerRequestSizeOpts...)
+ }
+
+ i, err := m.Int64Histogram(
+ "rpc.server.request.size",
+ opt...,
+ )
+ if err != nil {
+ return ServerRequestSize{noop.Int64Histogram{}}, err
+ }
+ return ServerRequestSize{i}, nil
+}
+
+// Inst returns the underlying metric instrument.
+func (m ServerRequestSize) Inst() metric.Int64Histogram {
+ return m.Int64Histogram
+}
+
+// Name returns the semantic convention name of the instrument.
+func (ServerRequestSize) Name() string {
+ return "rpc.server.request.size"
+}
+
+// Unit returns the semantic convention unit of the instrument
+func (ServerRequestSize) Unit() string {
+ return "By"
+}
+
+// Description returns the semantic convention description of the instrument
+func (ServerRequestSize) Description() string {
+ return "Measures the size of RPC request messages (uncompressed)."
+}
+
+// Record records val to the current distribution for attrs.
+//
+// **Streaming**: Recorded per message in a streaming batch
+func (m ServerRequestSize) Record(ctx context.Context, val int64, attrs ...attribute.KeyValue) {
+ if len(attrs) == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributes(attrs...))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// RecordSet records val to the current distribution for set.
+//
+// **Streaming**: Recorded per message in a streaming batch
+func (m ServerRequestSize) RecordSet(ctx context.Context, val int64, set attribute.Set) {
+ if set.Len() == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributeSet(set))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// ServerRequestsPerRPC is an instrument used to record metric values conforming
+// to the "rpc.server.requests_per_rpc" semantic conventions. It represents the
+// measures the number of messages received per RPC.
+type ServerRequestsPerRPC struct {
+ metric.Int64Histogram
+}
+
+var newServerRequestsPerRPCOpts = []metric.Int64HistogramOption{
+ metric.WithDescription("Measures the number of messages received per RPC."),
+ metric.WithUnit("{count}"),
+}
+
+// NewServerRequestsPerRPC returns a new ServerRequestsPerRPC instrument.
+func NewServerRequestsPerRPC(
+ m metric.Meter,
+ opt ...metric.Int64HistogramOption,
+) (ServerRequestsPerRPC, error) {
+ // Check if the meter is nil.
+ if m == nil {
+ return ServerRequestsPerRPC{noop.Int64Histogram{}}, nil
+ }
+
+ if len(opt) == 0 {
+ opt = newServerRequestsPerRPCOpts
+ } else {
+ opt = append(opt, newServerRequestsPerRPCOpts...)
+ }
+
+ i, err := m.Int64Histogram(
+ "rpc.server.requests_per_rpc",
+ opt...,
+ )
+ if err != nil {
+ return ServerRequestsPerRPC{noop.Int64Histogram{}}, err
+ }
+ return ServerRequestsPerRPC{i}, nil
+}
+
+// Inst returns the underlying metric instrument.
+func (m ServerRequestsPerRPC) Inst() metric.Int64Histogram {
+ return m.Int64Histogram
+}
+
+// Name returns the semantic convention name of the instrument.
+func (ServerRequestsPerRPC) Name() string {
+ return "rpc.server.requests_per_rpc"
+}
+
+// Unit returns the semantic convention unit of the instrument
+func (ServerRequestsPerRPC) Unit() string {
+ return "{count}"
+}
+
+// Description returns the semantic convention description of the instrument
+func (ServerRequestsPerRPC) Description() string {
+ return "Measures the number of messages received per RPC."
+}
+
+// Record records val to the current distribution for attrs.
+//
+// Should be 1 for all non-streaming RPCs.
+//
+// **Streaming** : This metric is required for server and client streaming RPCs
+func (m ServerRequestsPerRPC) Record(ctx context.Context, val int64, attrs ...attribute.KeyValue) {
+ if len(attrs) == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributes(attrs...))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// RecordSet records val to the current distribution for set.
+//
+// Should be 1 for all non-streaming RPCs.
+//
+// **Streaming** : This metric is required for server and client streaming RPCs
+func (m ServerRequestsPerRPC) RecordSet(ctx context.Context, val int64, set attribute.Set) {
+ if set.Len() == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributeSet(set))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// ServerResponseSize is an instrument used to record metric values conforming to
+// the "rpc.server.response.size" semantic conventions. It represents the
+// measures the size of RPC response messages (uncompressed).
+type ServerResponseSize struct {
+ metric.Int64Histogram
+}
+
+var newServerResponseSizeOpts = []metric.Int64HistogramOption{
+ metric.WithDescription("Measures the size of RPC response messages (uncompressed)."),
+ metric.WithUnit("By"),
+}
+
+// NewServerResponseSize returns a new ServerResponseSize instrument.
+func NewServerResponseSize(
+ m metric.Meter,
+ opt ...metric.Int64HistogramOption,
+) (ServerResponseSize, error) {
+ // Check if the meter is nil.
+ if m == nil {
+ return ServerResponseSize{noop.Int64Histogram{}}, nil
+ }
+
+ if len(opt) == 0 {
+ opt = newServerResponseSizeOpts
+ } else {
+ opt = append(opt, newServerResponseSizeOpts...)
+ }
+
+ i, err := m.Int64Histogram(
+ "rpc.server.response.size",
+ opt...,
+ )
+ if err != nil {
+ return ServerResponseSize{noop.Int64Histogram{}}, err
+ }
+ return ServerResponseSize{i}, nil
+}
+
+// Inst returns the underlying metric instrument.
+func (m ServerResponseSize) Inst() metric.Int64Histogram {
+ return m.Int64Histogram
+}
+
+// Name returns the semantic convention name of the instrument.
+func (ServerResponseSize) Name() string {
+ return "rpc.server.response.size"
+}
+
+// Unit returns the semantic convention unit of the instrument
+func (ServerResponseSize) Unit() string {
+ return "By"
+}
+
+// Description returns the semantic convention description of the instrument
+func (ServerResponseSize) Description() string {
+ return "Measures the size of RPC response messages (uncompressed)."
+}
+
+// Record records val to the current distribution for attrs.
+//
+// **Streaming**: Recorded per response in a streaming batch
+func (m ServerResponseSize) Record(ctx context.Context, val int64, attrs ...attribute.KeyValue) {
+ if len(attrs) == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributes(attrs...))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// RecordSet records val to the current distribution for set.
+//
+// **Streaming**: Recorded per response in a streaming batch
+func (m ServerResponseSize) RecordSet(ctx context.Context, val int64, set attribute.Set) {
+ if set.Len() == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributeSet(set))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// ServerResponsesPerRPC is an instrument used to record metric values conforming
+// to the "rpc.server.responses_per_rpc" semantic conventions. It represents the
+// measures the number of messages sent per RPC.
+type ServerResponsesPerRPC struct {
+ metric.Int64Histogram
+}
+
+var newServerResponsesPerRPCOpts = []metric.Int64HistogramOption{
+ metric.WithDescription("Measures the number of messages sent per RPC."),
+ metric.WithUnit("{count}"),
+}
+
+// NewServerResponsesPerRPC returns a new ServerResponsesPerRPC instrument.
+func NewServerResponsesPerRPC(
+ m metric.Meter,
+ opt ...metric.Int64HistogramOption,
+) (ServerResponsesPerRPC, error) {
+ // Check if the meter is nil.
+ if m == nil {
+ return ServerResponsesPerRPC{noop.Int64Histogram{}}, nil
+ }
+
+ if len(opt) == 0 {
+ opt = newServerResponsesPerRPCOpts
+ } else {
+ opt = append(opt, newServerResponsesPerRPCOpts...)
+ }
+
+ i, err := m.Int64Histogram(
+ "rpc.server.responses_per_rpc",
+ opt...,
+ )
+ if err != nil {
+ return ServerResponsesPerRPC{noop.Int64Histogram{}}, err
+ }
+ return ServerResponsesPerRPC{i}, nil
+}
+
+// Inst returns the underlying metric instrument.
+func (m ServerResponsesPerRPC) Inst() metric.Int64Histogram {
+ return m.Int64Histogram
+}
+
+// Name returns the semantic convention name of the instrument.
+func (ServerResponsesPerRPC) Name() string {
+ return "rpc.server.responses_per_rpc"
+}
+
+// Unit returns the semantic convention unit of the instrument
+func (ServerResponsesPerRPC) Unit() string {
+ return "{count}"
+}
+
+// Description returns the semantic convention description of the instrument
+func (ServerResponsesPerRPC) Description() string {
+ return "Measures the number of messages sent per RPC."
+}
+
+// Record records val to the current distribution for attrs.
+//
+// Should be 1 for all non-streaming RPCs.
+//
+// **Streaming**: This metric is required for server and client streaming RPCs
+func (m ServerResponsesPerRPC) Record(ctx context.Context, val int64, attrs ...attribute.KeyValue) {
+ if len(attrs) == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributes(attrs...))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
+
+// RecordSet records val to the current distribution for set.
+//
+// Should be 1 for all non-streaming RPCs.
+//
+// **Streaming**: This metric is required for server and client streaming RPCs
+func (m ServerResponsesPerRPC) RecordSet(ctx context.Context, val int64, set attribute.Set) {
+ if set.Len() == 0 {
+ m.Int64Histogram.Record(ctx, val)
+ return
+ }
+
+ o := recOptPool.Get().(*[]metric.RecordOption)
+ defer func() {
+ *o = (*o)[:0]
+ recOptPool.Put(o)
+ }()
+
+ *o = append(*o, metric.WithAttributeSet(set))
+ m.Int64Histogram.Record(ctx, val, *o...)
+}
diff --git a/vendor/go.opentelemetry.io/otel/trace/trace.go b/vendor/go.opentelemetry.io/otel/trace/trace.go
index 96c06ec32..e3d103c4b 100644
--- a/vendor/go.opentelemetry.io/otel/trace/trace.go
+++ b/vendor/go.opentelemetry.io/otel/trace/trace.go
@@ -196,6 +196,20 @@ func (tf TraceFlags) WithSampled(sampled bool) TraceFlags { // nolint:revive //
return tf &^ FlagsSampled
}
+// IsRandom reports whether the random bit is set in the TraceFlags.
+func (tf TraceFlags) IsRandom() bool {
+ return tf&FlagsRandom == FlagsRandom
+}
+
+// WithRandom sets the random bit in a new copy of the TraceFlags.
+func (tf TraceFlags) WithRandom(random bool) TraceFlags { // nolint:revive // random is not a control flag.
+ if random {
+ return tf | FlagsRandom
+ }
+
+ return tf &^ FlagsRandom
+}
+
// MarshalJSON implements a custom marshal function to encode TraceFlags
// as a hex string.
func (tf TraceFlags) MarshalJSON() ([]byte, error) {
@@ -322,6 +336,11 @@ func (sc SpanContext) IsSampled() bool {
return sc.traceFlags.IsSampled()
}
+// IsRandom reports whether the random bit is set in the SpanContext's TraceFlags.
+func (sc SpanContext) IsRandom() bool {
+ return sc.traceFlags.IsRandom()
+}
+
// WithTraceFlags returns a new SpanContext with the TraceFlags replaced.
func (sc SpanContext) WithTraceFlags(flags TraceFlags) SpanContext {
return SpanContext{
diff --git a/vendor/go.opentelemetry.io/otel/version.go b/vendor/go.opentelemetry.io/otel/version.go
index 2dae06f25..1db4f47e4 100644
--- a/vendor/go.opentelemetry.io/otel/version.go
+++ b/vendor/go.opentelemetry.io/otel/version.go
@@ -5,5 +5,5 @@ package otel // import "go.opentelemetry.io/otel"
// Version is the current release version of OpenTelemetry in use.
func Version() string {
- return "1.42.0"
+ return "1.43.0"
}
diff --git a/vendor/go.opentelemetry.io/otel/versions.yaml b/vendor/go.opentelemetry.io/otel/versions.yaml
index 6c634ef31..bcc6ee78a 100644
--- a/vendor/go.opentelemetry.io/otel/versions.yaml
+++ b/vendor/go.opentelemetry.io/otel/versions.yaml
@@ -3,7 +3,7 @@
module-sets:
stable-v1:
- version: v1.42.0
+ version: v1.43.0
modules:
- go.opentelemetry.io/otel
- go.opentelemetry.io/otel/bridge/opencensus
@@ -22,11 +22,11 @@ module-sets:
- go.opentelemetry.io/otel/sdk/metric
- go.opentelemetry.io/otel/trace
experimental-metrics:
- version: v0.64.0
+ version: v0.65.0
modules:
- go.opentelemetry.io/otel/exporters/prometheus
experimental-logs:
- version: v0.18.0
+ version: v0.19.0
modules:
- go.opentelemetry.io/otel/log
- go.opentelemetry.io/otel/log/logtest
@@ -64,3 +64,6 @@ modules:
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp:
version-refs:
- ./internal/version.go
+ go.opentelemetry.io/otel/exporters/stdout/stdoutlog:
+ version-refs:
+ - ./internal/version.go
diff --git a/vendor/google.golang.org/genproto/googleapis/rpc/errdetails/error_details.pb.go b/vendor/google.golang.org/genproto/googleapis/rpc/errdetails/error_details.pb.go
index e017ef071..842a5d9b5 100644
--- a/vendor/google.golang.org/genproto/googleapis/rpc/errdetails/error_details.pb.go
+++ b/vendor/google.golang.org/genproto/googleapis/rpc/errdetails/error_details.pb.go
@@ -1,4 +1,4 @@
-// Copyright 2025 Google LLC
+// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -957,17 +957,17 @@ type BadRequest_FieldViolation struct {
// In this example, in proto `field` could take one of the following values:
//
// - `full_name` for a violation in the `full_name` value
- // - `email_addresses[1].email` for a violation in the `email` field of the
+ // - `email_addresses[0].email` for a violation in the `email` field of the
// first `email_addresses` message
- // - `email_addresses[3].type[2]` for a violation in the second `type`
+ // - `email_addresses[2].type[1]` for a violation in the second `type`
// value in the third `email_addresses` message.
//
// In JSON, the same values are represented as:
//
// - `fullName` for a violation in the `fullName` value
- // - `emailAddresses[1].email` for a violation in the `email` field of the
+ // - `emailAddresses[0].email` for a violation in the `email` field of the
// first `emailAddresses` message
- // - `emailAddresses[3].type[2]` for a violation in the second `type`
+ // - `emailAddresses[2].type[1]` for a violation in the second `type`
// value in the third `emailAddresses` message.
Field string `protobuf:"bytes,1,opt,name=field,proto3" json:"field,omitempty"`
// A description of why the request element is bad.
diff --git a/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go b/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go
index 06a3f7106..f25a7bcc7 100644
--- a/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go
+++ b/vendor/google.golang.org/genproto/googleapis/rpc/status/status.pb.go
@@ -1,4 +1,4 @@
-// Copyright 2025 Google LLC
+// Copyright 2026 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -127,14 +127,13 @@ var file_google_rpc_status_proto_rawDesc = []byte{
0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x07, 0x64, 0x65, 0x74, 0x61,
0x69, 0x6c, 0x73, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x14, 0x2e, 0x67, 0x6f, 0x6f, 0x67,
0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41, 0x6e, 0x79, 0x52,
- 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x61, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e,
+ 0x07, 0x64, 0x65, 0x74, 0x61, 0x69, 0x6c, 0x73, 0x42, 0x5e, 0x0a, 0x0e, 0x63, 0x6f, 0x6d, 0x2e,
0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x72, 0x70, 0x63, 0x42, 0x0b, 0x53, 0x74, 0x61, 0x74,
0x75, 0x73, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x37, 0x67, 0x6f, 0x6f, 0x67, 0x6c,
0x65, 0x2e, 0x67, 0x6f, 0x6c, 0x61, 0x6e, 0x67, 0x2e, 0x6f, 0x72, 0x67, 0x2f, 0x67, 0x65, 0x6e,
0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x70, 0x69, 0x73,
0x2f, 0x72, 0x70, 0x63, 0x2f, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x3b, 0x73, 0x74, 0x61, 0x74,
- 0x75, 0x73, 0xf8, 0x01, 0x01, 0xa2, 0x02, 0x03, 0x52, 0x50, 0x43, 0x62, 0x06, 0x70, 0x72, 0x6f,
- 0x74, 0x6f, 0x33,
+ 0x75, 0x73, 0xa2, 0x02, 0x03, 0x52, 0x50, 0x43, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (
diff --git a/vendor/google.golang.org/grpc/attributes/attributes.go b/vendor/google.golang.org/grpc/attributes/attributes.go
index 52d530d7a..4c60518c7 100644
--- a/vendor/google.golang.org/grpc/attributes/attributes.go
+++ b/vendor/google.golang.org/grpc/attributes/attributes.go
@@ -27,6 +27,8 @@ package attributes
import (
"fmt"
+ "iter"
+ "maps"
"strings"
)
@@ -37,37 +39,46 @@ import (
// any) bool', it will be called by (*Attributes).Equal to determine whether
// two values with the same key should be considered equal.
type Attributes struct {
- m map[any]any
+ parent *Attributes
+ key, value any
}
// New returns a new Attributes containing the key/value pair.
func New(key, value any) *Attributes {
- return &Attributes{m: map[any]any{key: value}}
+ return &Attributes{
+ key: key,
+ value: value,
+ }
}
// WithValue returns a new Attributes containing the previous keys and values
// and the new key/value pair. If the same key appears multiple times, the
-// last value overwrites all previous values for that key. To remove an
-// existing key, use a nil value. value should not be modified later.
+// last value overwrites all previous values for that key. value should not be
+// modified later.
+//
+// Note that Attributes do not support deletion. Avoid using untyped nil values.
+// Since the Value method returns an untyped nil when a key is absent, it is
+// impossible to distinguish between a missing key and a key explicitly set to
+// an untyped nil. If you need to represent a value being unset, consider
+// storing a specific sentinel type or a wrapper struct with a boolean field
+// indicating presence.
func (a *Attributes) WithValue(key, value any) *Attributes {
- if a == nil {
- return New(key, value)
+ return &Attributes{
+ parent: a,
+ key: key,
+ value: value,
}
- n := &Attributes{m: make(map[any]any, len(a.m)+1)}
- for k, v := range a.m {
- n.m[k] = v
- }
- n.m[key] = value
- return n
}
// Value returns the value associated with these attributes for key, or nil if
// no value is associated with key. The returned value should not be modified.
func (a *Attributes) Value(key any) any {
- if a == nil {
- return nil
+ for cur := a; cur != nil; cur = cur.parent {
+ if cur.key == key {
+ return cur.value
+ }
}
- return a.m[key]
+ return nil
}
// Equal returns whether a and o are equivalent. If 'Equal(o any) bool' is
@@ -83,11 +94,15 @@ func (a *Attributes) Equal(o *Attributes) bool {
if a == nil || o == nil {
return false
}
- if len(a.m) != len(o.m) {
- return false
+ if a == o {
+ return true
}
- for k, v := range a.m {
- ov, ok := o.m[k]
+ m := maps.Collect(o.all())
+ lenA := 0
+
+ for k, v := range a.all() {
+ lenA++
+ ov, ok := m[k]
if !ok {
// o missing element of a
return false
@@ -101,7 +116,7 @@ func (a *Attributes) Equal(o *Attributes) bool {
return false
}
}
- return true
+ return lenA == len(m)
}
// String prints the attribute map. If any key or values throughout the map
@@ -110,11 +125,11 @@ func (a *Attributes) String() string {
var sb strings.Builder
sb.WriteString("{")
first := true
- for k, v := range a.m {
+ for k, v := range a.all() {
if !first {
sb.WriteString(", ")
}
- sb.WriteString(fmt.Sprintf("%q: %q ", str(k), str(v)))
+ fmt.Fprintf(&sb, "%q: %q ", str(k), str(v))
first = false
}
sb.WriteString("}")
@@ -139,3 +154,21 @@ func str(x any) (s string) {
func (a *Attributes) MarshalJSON() ([]byte, error) {
return []byte(a.String()), nil
}
+
+// all returns an iterator that yields all key-value pairs in the Attributes
+// chain. If a key appears multiple times, only the most recently added value
+// is yielded.
+func (a *Attributes) all() iter.Seq2[any, any] {
+ return func(yield func(any, any) bool) {
+ seen := map[any]bool{}
+ for cur := a; cur != nil; cur = cur.parent {
+ if seen[cur.key] {
+ continue
+ }
+ if !yield(cur.key, cur.value) {
+ return
+ }
+ seen[cur.key] = true
+ }
+ }
+}
diff --git a/vendor/google.golang.org/grpc/balancer/balancer.go b/vendor/google.golang.org/grpc/balancer/balancer.go
index d08b7ad63..326888ae3 100644
--- a/vendor/google.golang.org/grpc/balancer/balancer.go
+++ b/vendor/google.golang.org/grpc/balancer/balancer.go
@@ -33,6 +33,7 @@ import (
estats "google.golang.org/grpc/experimental/stats"
"google.golang.org/grpc/grpclog"
"google.golang.org/grpc/internal"
+ "google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/resolver"
"google.golang.org/grpc/serviceconfig"
@@ -46,8 +47,8 @@ var (
)
// Register registers the balancer builder to the balancer map. b.Name
-// (lowercased) will be used as the name registered with this builder. If the
-// Builder implements ConfigParser, ParseConfig will be called when new service
+// will be used as the name registered with this builder. If the Builder
+// implements ConfigParser, ParseConfig will be called when new service
// configs are received by the resolver, and the result will be provided to the
// Balancer in UpdateClientConnState.
//
@@ -55,12 +56,12 @@ var (
// an init() function), and is not thread-safe. If multiple Balancers are
// registered with the same name, the one registered last will take effect.
func Register(b Builder) {
- name := strings.ToLower(b.Name())
- if name != b.Name() {
- // TODO: Skip the use of strings.ToLower() to index the map after v1.59
- // is released to switch to case sensitive balancer registry. Also,
- // remove this warning and update the docstrings for Register and Get.
- logger.Warningf("Balancer registered with name %q. grpc-go will be switching to case sensitive balancer registries soon", b.Name())
+ name := b.Name()
+ if !envconfig.CaseSensitiveBalancerRegistries {
+ name = strings.ToLower(name)
+ if name != b.Name() {
+ logger.Warningf("Balancer registered with name %q. grpc-go will be switching to case sensitive balancer registries soon. After 2 releases, we will enable the env var by default.", b.Name())
+ }
}
m[name] = b
}
@@ -78,16 +79,17 @@ func init() {
}
// Get returns the resolver builder registered with the given name.
-// Note that the compare is done in a case-insensitive fashion.
+// Note that the compare is done in a case-sensitive fashion.
// If no builder is register with the name, nil will be returned.
func Get(name string) Builder {
- if strings.ToLower(name) != name {
- // TODO: Skip the use of strings.ToLower() to index the map after v1.59
- // is released to switch to case sensitive balancer registry. Also,
- // remove this warning and update the docstrings for Register and Get.
- logger.Warningf("Balancer retrieved for name %q. grpc-go will be switching to case sensitive balancer registries soon", name)
+ if !envconfig.CaseSensitiveBalancerRegistries {
+ lowerName := strings.ToLower(name)
+ if lowerName != name {
+ logger.Warningf("Balancer retrieved for name %q. grpc-go will be switching to case sensitive balancer registries soon. After 2 releases, we will enable the env var by default.", name)
+ }
+ name = lowerName
}
- if b, ok := m[strings.ToLower(name)]; ok {
+ if b, ok := m[name]; ok {
return b
}
return nil
diff --git a/vendor/google.golang.org/grpc/balancer/base/balancer.go b/vendor/google.golang.org/grpc/balancer/base/balancer.go
index 4d576876d..4399ba014 100644
--- a/vendor/google.golang.org/grpc/balancer/base/balancer.go
+++ b/vendor/google.golang.org/grpc/balancer/base/balancer.go
@@ -121,8 +121,7 @@ func (b *baseBalancer) UpdateClientConnState(s balancer.ClientConnState) error {
sc.Connect()
}
}
- for _, a := range b.subConns.Keys() {
- sc, _ := b.subConns.Get(a)
+ for a, sc := range b.subConns.All() {
// a was removed by resolver.
if _, ok := addrsSet.Get(a); !ok {
sc.Shutdown()
@@ -171,8 +170,7 @@ func (b *baseBalancer) regeneratePicker() {
readySCs := make(map[balancer.SubConn]SubConnInfo)
// Filter out all ready SCs from full subConn map.
- for _, addr := range b.subConns.Keys() {
- sc, _ := b.subConns.Get(addr)
+ for addr, sc := range b.subConns.All() {
if st, ok := b.scStates[sc]; ok && st == connectivity.Ready {
readySCs[sc] = SubConnInfo{Address: addr}
}
diff --git a/vendor/google.golang.org/grpc/balancer/endpointsharding/endpointsharding.go b/vendor/google.golang.org/grpc/balancer/endpointsharding/endpointsharding.go
index 360db08eb..12479f698 100644
--- a/vendor/google.golang.org/grpc/balancer/endpointsharding/endpointsharding.go
+++ b/vendor/google.golang.org/grpc/balancer/endpointsharding/endpointsharding.go
@@ -187,8 +187,7 @@ func (es *endpointSharding) UpdateClientConnState(state balancer.ClientConnState
}
}
// Delete old children that are no longer present.
- for _, e := range children.Keys() {
- child, _ := children.Get(e)
+ for e, child := range children.All() {
if _, ok := newChildren.Get(e); !ok {
child.closeLocked()
}
@@ -212,7 +211,7 @@ func (es *endpointSharding) ResolverError(err error) {
es.updateState()
}()
children := es.children.Load()
- for _, child := range children.Values() {
+ for _, child := range children.All() {
child.resolverErrorLocked(err)
}
}
@@ -225,7 +224,7 @@ func (es *endpointSharding) Close() {
es.childMu.Lock()
defer es.childMu.Unlock()
children := es.children.Load()
- for _, child := range children.Values() {
+ for _, child := range children.All() {
child.closeLocked()
}
}
@@ -233,7 +232,7 @@ func (es *endpointSharding) Close() {
func (es *endpointSharding) ExitIdle() {
es.childMu.Lock()
defer es.childMu.Unlock()
- for _, bw := range es.children.Load().Values() {
+ for _, bw := range es.children.Load().All() {
if !bw.isClosed {
bw.child.ExitIdle()
}
@@ -255,7 +254,7 @@ func (es *endpointSharding) updateState() {
children := es.children.Load()
childStates := make([]ChildState, 0, children.Len())
- for _, child := range children.Values() {
+ for _, child := range children.All() {
childState := child.childState
childStates = append(childStates, childState)
childPicker := childState.State.Picker
diff --git a/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go b/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go
index dccd9f0bf..518a69d57 100644
--- a/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go
+++ b/vendor/google.golang.org/grpc/balancer/pickfirst/pickfirst.go
@@ -399,14 +399,14 @@ func (b *pickfirstBalancer) startFirstPassLocked() {
b.firstPass = true
b.numTF = 0
// Reset the connection attempt record for existing SubConns.
- for _, sd := range b.subConns.Values() {
+ for _, sd := range b.subConns.All() {
sd.connectionFailedInFirstPass = false
}
b.requestConnectionLocked()
}
func (b *pickfirstBalancer) closeSubConnsLocked() {
- for _, sd := range b.subConns.Values() {
+ for _, sd := range b.subConns.All() {
sd.subConn.Shutdown()
}
b.subConns = resolver.NewAddressMapV2[*scData]()
@@ -506,7 +506,7 @@ func (b *pickfirstBalancer) reconcileSubConnsLocked(newAddrs []resolver.Address)
newAddrsMap.Set(addr, true)
}
- for _, oldAddr := range b.subConns.Keys() {
+ for oldAddr := range b.subConns.All() {
if _, ok := newAddrsMap.Get(oldAddr); ok {
continue
}
@@ -520,7 +520,7 @@ func (b *pickfirstBalancer) reconcileSubConnsLocked(newAddrs []resolver.Address)
// becomes ready, which means that all other subConn must be shutdown.
func (b *pickfirstBalancer) shutdownRemainingLocked(selected *scData) {
b.cancelConnectionTimer()
- for _, sd := range b.subConns.Values() {
+ for _, sd := range b.subConns.All() {
if sd.subConn != selected.subConn {
sd.subConn.Shutdown()
}
@@ -771,7 +771,7 @@ func (b *pickfirstBalancer) endFirstPassIfPossibleLocked(lastErr error) {
}
// Connect() has been called on all the SubConns. The first pass can be
// ended if all the SubConns have reported a failure.
- for _, sd := range b.subConns.Values() {
+ for _, sd := range b.subConns.All() {
if !sd.connectionFailedInFirstPass {
return
}
@@ -782,7 +782,7 @@ func (b *pickfirstBalancer) endFirstPassIfPossibleLocked(lastErr error) {
Picker: &picker{err: lastErr},
})
// Start re-connecting all the SubConns that are already in IDLE.
- for _, sd := range b.subConns.Values() {
+ for _, sd := range b.subConns.All() {
if sd.rawConnectivityState == connectivity.Idle {
sd.subConn.Connect()
}
diff --git a/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go b/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go
index 42c61cf9f..296123e20 100644
--- a/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go
+++ b/vendor/google.golang.org/grpc/binarylog/grpc_binarylog_v1/binarylog.pb.go
@@ -18,7 +18,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.36.10
+// protoc-gen-go v1.36.11
// protoc v5.27.1
// source: grpc/binlog/v1/binarylog.proto
diff --git a/vendor/google.golang.org/grpc/credentials/tls.go b/vendor/google.golang.org/grpc/credentials/tls.go
index 0bcd16dbb..a6083c3b0 100644
--- a/vendor/google.golang.org/grpc/credentials/tls.go
+++ b/vendor/google.golang.org/grpc/credentials/tls.go
@@ -22,7 +22,6 @@ import (
"context"
"crypto/tls"
"crypto/x509"
- "errors"
"fmt"
"net"
"net/url"
@@ -52,22 +51,21 @@ func (t TLSInfo) AuthType() string {
}
// ValidateAuthority validates the provided authority being used to override the
-// :authority header by verifying it against the peer certificates. It returns a
+// :authority header by verifying it against the peer certificate. It returns a
// non-nil error if the validation fails.
func (t TLSInfo) ValidateAuthority(authority string) error {
- var errs []error
host, _, err := net.SplitHostPort(authority)
if err != nil {
host = authority
}
- for _, cert := range t.State.PeerCertificates {
- var err error
- if err = cert.VerifyHostname(host); err == nil {
- return nil
- }
- errs = append(errs, err)
+
+ // Verify authority against the leaf certificate.
+ if len(t.State.PeerCertificates) == 0 {
+ // This is not expected to happen as the TLS handshake has already
+ // completed and should have populated PeerCertificates.
+ return fmt.Errorf("credentials: no peer certificates found to verify authority %q", host)
}
- return fmt.Errorf("credentials: invalid authority %q: %v", authority, errors.Join(errs...))
+ return t.State.PeerCertificates[0].VerifyHostname(host)
}
// cipherSuiteLookup returns the string version of a TLS cipher suite ID.
diff --git a/vendor/google.golang.org/grpc/dialoptions.go b/vendor/google.golang.org/grpc/dialoptions.go
index 7a5ac2e7c..4ec5f9cd0 100644
--- a/vendor/google.golang.org/grpc/dialoptions.go
+++ b/vendor/google.golang.org/grpc/dialoptions.go
@@ -705,10 +705,11 @@ func WithDisableHealthCheck() DialOption {
func defaultDialOptions() dialOptions {
return dialOptions{
copts: transport.ConnectOptions{
- ReadBufferSize: defaultReadBufSize,
- WriteBufferSize: defaultWriteBufSize,
- UserAgent: grpcUA,
- BufferPool: mem.DefaultBufferPool(),
+ ReadBufferSize: defaultReadBufSize,
+ WriteBufferSize: defaultWriteBufSize,
+ SharedWriteBuffer: true,
+ UserAgent: grpcUA,
+ BufferPool: mem.DefaultBufferPool(),
},
bs: internalbackoff.DefaultExponential,
idleTimeout: 30 * time.Minute,
diff --git a/vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go b/vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go
index 8f7d9f6bb..dcb98cdbc 100644
--- a/vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go
+++ b/vendor/google.golang.org/grpc/health/grpc_health_v1/health.pb.go
@@ -17,7 +17,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.36.10
+// protoc-gen-go v1.36.11
// protoc v5.27.1
// source: grpc/health/v1/health.proto
diff --git a/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go b/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go
index e99cd5c83..9e10fdd2e 100644
--- a/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go
+++ b/vendor/google.golang.org/grpc/health/grpc_health_v1/health_grpc.pb.go
@@ -17,7 +17,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
-// - protoc-gen-go-grpc v1.6.0
+// - protoc-gen-go-grpc v1.6.1
// - protoc v5.27.1
// source: grpc/health/v1/health.proto
diff --git a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go
index 7ad6fb44c..3ae45faa4 100644
--- a/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go
+++ b/vendor/google.golang.org/grpc/internal/envconfig/envconfig.go
@@ -54,17 +54,16 @@ var (
// XDSEndpointHashKeyBackwardCompat controls the parsing of the endpoint hash
// key from EDS LbEndpoint metadata. Endpoint hash keys can be disabled by
- // setting "GRPC_XDS_ENDPOINT_HASH_KEY_BACKWARD_COMPAT" to "true". When the
- // implementation of A76 is stable, we will flip the default value to false
- // in a subsequent release. A final release will remove this environment
- // variable, enabling the new behavior unconditionally.
- XDSEndpointHashKeyBackwardCompat = boolFromEnv("GRPC_XDS_ENDPOINT_HASH_KEY_BACKWARD_COMPAT", true)
+ // setting "GRPC_XDS_ENDPOINT_HASH_KEY_BACKWARD_COMPAT" to "true". A future
+ // release will remove this environment variable, enabling the new behavior
+ // unconditionally.
+ XDSEndpointHashKeyBackwardCompat = boolFromEnv("GRPC_XDS_ENDPOINT_HASH_KEY_BACKWARD_COMPAT", false)
// RingHashSetRequestHashKey is set if the ring hash balancer can get the
// request hash header by setting the "requestHashHeader" field, according
- // to gRFC A76. It can be enabled by setting the environment variable
- // "GRPC_EXPERIMENTAL_RING_HASH_SET_REQUEST_HASH_KEY" to "true".
- RingHashSetRequestHashKey = boolFromEnv("GRPC_EXPERIMENTAL_RING_HASH_SET_REQUEST_HASH_KEY", false)
+ // to gRFC A76. It can be disabled by setting the environment variable
+ // "GRPC_EXPERIMENTAL_RING_HASH_SET_REQUEST_HASH_KEY" to "false".
+ RingHashSetRequestHashKey = boolFromEnv("GRPC_EXPERIMENTAL_RING_HASH_SET_REQUEST_HASH_KEY", true)
// ALTSHandshakerKeepaliveParams is set if we should add the
// KeepaliveParams when dial the ALTS handshaker service.
@@ -78,6 +77,14 @@ var (
// - The DNS resolver is being used.
EnableDefaultPortForProxyTarget = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_DEFAULT_PORT_FOR_PROXY_TARGET", true)
+ // CaseSensitiveBalancerRegistries is set if the balancer registry should be
+ // case-sensitive. This is disabled by default, but can be enabled by setting
+ // the env variable "GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES"
+ // to "true".
+ //
+ // TODO: After 2 releases, we will enable the env var by default.
+ CaseSensitiveBalancerRegistries = boolFromEnv("GRPC_GO_EXPERIMENTAL_CASE_SENSITIVE_BALANCER_REGISTRIES", false)
+
// XDSAuthorityRewrite indicates whether xDS authority rewriting is enabled.
// This feature is defined in gRFC A81 and is enabled by setting the
// environment variable GRPC_EXPERIMENTAL_XDS_AUTHORITY_REWRITE to "true".
@@ -89,6 +96,14 @@ var (
// GRPC_EXPERIMENTAL_PF_WEIGHTED_SHUFFLING to "false".
PickFirstWeightedShuffling = boolFromEnv("GRPC_EXPERIMENTAL_PF_WEIGHTED_SHUFFLING", true)
+ // XDSRecoverPanicInResourceParsing indicates whether the xdsclient should
+ // recover from panics while parsing xDS resources.
+ //
+ // This feature can be disabled (e.g. for fuzz testing) by setting the
+ // environment variable "GRPC_GO_EXPERIMENTAL_XDS_RESOURCE_PANIC_RECOVERY"
+ // to "false".
+ XDSRecoverPanicInResourceParsing = boolFromEnv("GRPC_GO_EXPERIMENTAL_XDS_RESOURCE_PANIC_RECOVERY", true)
+
// DisableStrictPathChecking indicates whether strict path checking is
// disabled. This feature can be disabled by setting the environment
// variable GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING to "true".
@@ -104,6 +119,13 @@ var (
// A future release will remove this environment variable, enabling strict
// path checking behavior unconditionally.
DisableStrictPathChecking = boolFromEnv("GRPC_GO_EXPERIMENTAL_DISABLE_STRICT_PATH_CHECKING", false)
+
+ // EnablePriorityLBChildPolicyCache controls whether the priority balancer
+ // should cache child balancers that are removed from the LB policy config,
+ // for a period of 15 minutes. This is disabled by default, but can be
+ // enabled by setting the env variable
+ // GRPC_EXPERIMENTAL_ENABLE_PRIORITY_LB_CHILD_POLICY_CACHE to true.
+ EnablePriorityLBChildPolicyCache = boolFromEnv("GRPC_EXPERIMENTAL_ENABLE_PRIORITY_LB_CHILD_POLICY_CACHE", false)
)
func boolFromEnv(envVar string, def bool) bool {
diff --git a/vendor/google.golang.org/grpc/internal/mem/buffer_pool.go b/vendor/google.golang.org/grpc/internal/mem/buffer_pool.go
new file mode 100644
index 000000000..c2348a82e
--- /dev/null
+++ b/vendor/google.golang.org/grpc/internal/mem/buffer_pool.go
@@ -0,0 +1,338 @@
+/*
+ *
+ * Copyright 2026 gRPC authors.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * 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.
+ *
+ */
+
+// Package mem provides utilities that facilitate memory reuse in byte slices
+// that are used as buffers.
+package mem
+
+import (
+ "fmt"
+ "math/bits"
+ "slices"
+ "sort"
+ "sync"
+)
+
+const (
+ goPageSize = 4 * 1024 // 4KiB. N.B. this must be a power of 2.
+)
+
+var uintSize = bits.UintSize // use a variable for mocking during tests.
+
+// bufferPool is a copy of the public bufferPool interface used to avoid
+// circular dependencies.
+type bufferPool interface {
+ // Get returns a buffer with specified length from the pool.
+ Get(length int) *[]byte
+
+ // Put returns a buffer to the pool.
+ //
+ // The provided pointer must hold a prefix of the buffer obtained via
+ // BufferPool.Get to ensure the buffer's entire capacity can be re-used.
+ Put(*[]byte)
+}
+
+// BinaryTieredBufferPool is a buffer pool that uses multiple sub-pools with
+// power-of-two sizes.
+type BinaryTieredBufferPool struct {
+ // exponentToNextLargestPoolMap maps a power-of-two exponent (e.g., 12 for
+ // 4KB) to the index of the next largest sizedBufferPool. This is used by
+ // Get() to find the smallest pool that can satisfy a request for a given
+ // size.
+ exponentToNextLargestPoolMap []int
+ // exponentToPreviousLargestPoolMap maps a power-of-two exponent to the
+ // index of the previous largest sizedBufferPool. This is used by Put()
+ // to return a buffer to the most appropriate pool based on its capacity.
+ exponentToPreviousLargestPoolMap []int
+ sizedPools []bufferPool
+ fallbackPool bufferPool
+ maxPoolCap int // Optimization: Cache max capacity
+}
+
+// NewBinaryTieredBufferPool returns a BufferPool backed by multiple sub-pools.
+// This structure enables O(1) lookup time for Get and Put operations.
+//
+// The arguments provided are the exponents for the buffer capacities (powers
+// of 2), not the raw byte sizes. For example, to create a pool of 16KB buffers
+// (2^14 bytes), pass 14 as the argument.
+func NewBinaryTieredBufferPool(powerOfTwoExponents ...uint8) (*BinaryTieredBufferPool, error) {
+ return newBinaryTiered(func(size int) bufferPool {
+ return newSizedBufferPool(size, true)
+ }, &simpleBufferPool{shouldZero: true}, powerOfTwoExponents...)
+}
+
+// NewDirtyBinaryTieredBufferPool returns a BufferPool backed by multiple
+// sub-pools. It is similar to NewBinaryTieredBufferPool but it does not
+// initialize the buffers before returning them.
+func NewDirtyBinaryTieredBufferPool(powerOfTwoExponents ...uint8) (*BinaryTieredBufferPool, error) {
+ return newBinaryTiered(func(size int) bufferPool {
+ return newSizedBufferPool(size, false)
+ }, &simpleBufferPool{shouldZero: false}, powerOfTwoExponents...)
+}
+
+func newBinaryTiered(sizedPoolFactory func(int) bufferPool, fallbackPool bufferPool, powerOfTwoExponents ...uint8) (*BinaryTieredBufferPool, error) {
+ slices.Sort(powerOfTwoExponents)
+ powerOfTwoExponents = slices.Compact(powerOfTwoExponents)
+
+ // Determine the maximum exponent we need to support. This depends on the
+ // word size (32-bit vs 64-bit).
+ maxExponent := uintSize - 2
+ indexOfNextLargestBit := slices.Repeat([]int{-1}, maxExponent+1)
+ indexOfPreviousLargestBit := slices.Repeat([]int{-1}, maxExponent+1)
+
+ maxTier := 0
+ pools := make([]bufferPool, 0, len(powerOfTwoExponents))
+
+ for i, exp := range powerOfTwoExponents {
+ // Allocating slices of size > 2^maxExponent isn't possible on
+ // maxExponent-bit machines.
+ if int(exp) > maxExponent {
+ return nil, fmt.Errorf("mem: allocating slice of size 2^%d is not possible", exp)
+ }
+ tierSize := 1 << exp
+ pools = append(pools, sizedPoolFactory(tierSize))
+ maxTier = max(maxTier, tierSize)
+
+ // Map the exact power of 2 to this pool index.
+ indexOfNextLargestBit[exp] = i
+ indexOfPreviousLargestBit[exp] = i
+ }
+
+ // Fill gaps for Get() (Next Largest)
+ // We iterate backwards. If current is empty, take the value from the right (larger).
+ for i := maxExponent - 1; i >= 0; i-- {
+ if indexOfNextLargestBit[i] == -1 {
+ indexOfNextLargestBit[i] = indexOfNextLargestBit[i+1]
+ }
+ }
+
+ // Fill gaps for Put() (Previous Largest)
+ // We iterate forwards. If current is empty, take the value from the left (smaller).
+ for i := 1; i <= maxExponent; i++ {
+ if indexOfPreviousLargestBit[i] == -1 {
+ indexOfPreviousLargestBit[i] = indexOfPreviousLargestBit[i-1]
+ }
+ }
+
+ return &BinaryTieredBufferPool{
+ exponentToNextLargestPoolMap: indexOfNextLargestBit,
+ exponentToPreviousLargestPoolMap: indexOfPreviousLargestBit,
+ sizedPools: pools,
+ maxPoolCap: maxTier,
+ fallbackPool: fallbackPool,
+ }, nil
+}
+
+// Get returns a buffer with specified length from the pool.
+func (b *BinaryTieredBufferPool) Get(size int) *[]byte {
+ return b.poolForGet(size).Get(size)
+}
+
+func (b *BinaryTieredBufferPool) poolForGet(size int) bufferPool {
+ if size == 0 || size > b.maxPoolCap {
+ return b.fallbackPool
+ }
+
+ // Calculate the exponent of the smallest power of 2 >= size.
+ // We subtract 1 from size to handle exact powers of 2 correctly.
+ //
+ // Examples:
+ // size=16 (0b10000) -> size-1=15 (0b01111) -> bits.Len=4 -> Pool for 2^4
+ // size=17 (0b10001) -> size-1=16 (0b10000) -> bits.Len=5 -> Pool for 2^5
+ querySize := uint(size - 1)
+ poolIdx := b.exponentToNextLargestPoolMap[bits.Len(querySize)]
+
+ return b.sizedPools[poolIdx]
+}
+
+// Put returns a buffer to the pool.
+func (b *BinaryTieredBufferPool) Put(buf *[]byte) {
+ // We pass the capacity of the buffer, and not the size of the buffer here.
+ // If we did the latter, all buffers would eventually move to the smallest
+ // pool.
+ b.poolForPut(cap(*buf)).Put(buf)
+}
+
+func (b *BinaryTieredBufferPool) poolForPut(bCap int) bufferPool {
+ if bCap == 0 {
+ return NopBufferPool{}
+ }
+ if bCap > b.maxPoolCap {
+ return b.fallbackPool
+ }
+ // Find the pool with the largest capacity <= bCap.
+ //
+ // We calculate the exponent of the largest power of 2 <= bCap.
+ // bits.Len(x) returns the minimum number of bits required to represent x;
+ // i.e. the number of bits up to and including the most significant bit.
+ // Subtracting 1 gives the 0-based index of the most significant bit,
+ // which is the exponent of the largest power of 2 <= bCap.
+ //
+ // Examples:
+ // cap=16 (0b10000) -> Len=5 -> 5-1=4 -> 2^4
+ // cap=15 (0b01111) -> Len=4 -> 4-1=3 -> 2^3
+ largestPowerOfTwo := bits.Len(uint(bCap)) - 1
+ poolIdx := b.exponentToPreviousLargestPoolMap[largestPowerOfTwo]
+ // The buffer is smaller than the smallest power of 2, discard it.
+ if poolIdx == -1 {
+ // Buffer is smaller than our smallest pool bucket.
+ return NopBufferPool{}
+ }
+ return b.sizedPools[poolIdx]
+}
+
+// NopBufferPool is a buffer pool that returns new buffers without pooling.
+type NopBufferPool struct{}
+
+// Get returns a buffer with specified length from the pool.
+func (NopBufferPool) Get(length int) *[]byte {
+ b := make([]byte, length)
+ return &b
+}
+
+// Put returns a buffer to the pool.
+func (NopBufferPool) Put(*[]byte) {
+}
+
+// sizedBufferPool is a BufferPool implementation that is optimized for specific
+// buffer sizes. For example, HTTP/2 frames within gRPC have a default max size
+// of 16kb and a sizedBufferPool can be configured to only return buffers with a
+// capacity of 16kb. Note that however it does not support returning larger
+// buffers and in fact panics if such a buffer is requested. Because of this,
+// this BufferPool implementation is not meant to be used on its own and rather
+// is intended to be embedded in a TieredBufferPool such that Get is only
+// invoked when the required size is smaller than or equal to defaultSize.
+type sizedBufferPool struct {
+ pool sync.Pool
+ defaultSize int
+ shouldZero bool
+}
+
+func (p *sizedBufferPool) Get(size int) *[]byte {
+ buf, ok := p.pool.Get().(*[]byte)
+ if !ok {
+ buf := make([]byte, size, p.defaultSize)
+ return &buf
+ }
+ b := *buf
+ if p.shouldZero {
+ clear(b[:cap(b)])
+ }
+ *buf = b[:size]
+ return buf
+}
+
+func (p *sizedBufferPool) Put(buf *[]byte) {
+ if cap(*buf) < p.defaultSize {
+ // Ignore buffers that are too small to fit in the pool. Otherwise, when
+ // Get is called it will panic as it tries to index outside the bounds
+ // of the buffer.
+ return
+ }
+ p.pool.Put(buf)
+}
+
+func newSizedBufferPool(size int, zero bool) *sizedBufferPool {
+ return &sizedBufferPool{
+ defaultSize: size,
+ shouldZero: zero,
+ }
+}
+
+// TieredBufferPool implements the BufferPool interface with multiple tiers of
+// buffer pools for different sizes of buffers.
+type TieredBufferPool struct {
+ sizedPools []*sizedBufferPool
+ fallbackPool simpleBufferPool
+}
+
+// NewTieredBufferPool returns a BufferPool implementation that uses multiple
+// underlying pools of the given pool sizes.
+func NewTieredBufferPool(poolSizes ...int) *TieredBufferPool {
+ sort.Ints(poolSizes)
+ pools := make([]*sizedBufferPool, len(poolSizes))
+ for i, s := range poolSizes {
+ pools[i] = newSizedBufferPool(s, true)
+ }
+ return &TieredBufferPool{
+ sizedPools: pools,
+ fallbackPool: simpleBufferPool{shouldZero: true},
+ }
+}
+
+// Get returns a buffer with specified length from the pool.
+func (p *TieredBufferPool) Get(size int) *[]byte {
+ return p.getPool(size).Get(size)
+}
+
+// Put returns a buffer to the pool.
+func (p *TieredBufferPool) Put(buf *[]byte) {
+ p.getPool(cap(*buf)).Put(buf)
+}
+
+func (p *TieredBufferPool) getPool(size int) bufferPool {
+ poolIdx := sort.Search(len(p.sizedPools), func(i int) bool {
+ return p.sizedPools[i].defaultSize >= size
+ })
+
+ if poolIdx == len(p.sizedPools) {
+ return &p.fallbackPool
+ }
+
+ return p.sizedPools[poolIdx]
+}
+
+// simpleBufferPool is an implementation of the BufferPool interface that
+// attempts to pool buffers with a sync.Pool. When Get is invoked, it tries to
+// acquire a buffer from the pool but if that buffer is too small, it returns it
+// to the pool and creates a new one.
+type simpleBufferPool struct {
+ pool sync.Pool
+ shouldZero bool
+}
+
+func (p *simpleBufferPool) Get(size int) *[]byte {
+ bs, ok := p.pool.Get().(*[]byte)
+ if ok && cap(*bs) >= size {
+ if p.shouldZero {
+ clear((*bs)[:cap(*bs)])
+ }
+ *bs = (*bs)[:size]
+ return bs
+ }
+
+ // A buffer was pulled from the pool, but it is too small. Put it back in
+ // the pool and create one large enough.
+ if ok {
+ p.pool.Put(bs)
+ }
+
+ // If we're going to allocate, round up to the nearest page. This way if
+ // requests frequently arrive with small variation we don't allocate
+ // repeatedly if we get unlucky and they increase over time. By default we
+ // only allocate here if size > 1MiB. Because goPageSize is a power of 2, we
+ // can round up efficiently.
+ allocSize := (size + goPageSize - 1) & ^(goPageSize - 1)
+
+ b := make([]byte, size, allocSize)
+ return &b
+}
+
+func (p *simpleBufferPool) Put(buf *[]byte) {
+ p.pool.Put(buf)
+}
diff --git a/vendor/google.golang.org/grpc/internal/transport/defaults.go b/vendor/google.golang.org/grpc/internal/transport/defaults.go
index bc8ee0747..0b2269a50 100644
--- a/vendor/google.golang.org/grpc/internal/transport/defaults.go
+++ b/vendor/google.golang.org/grpc/internal/transport/defaults.go
@@ -46,6 +46,7 @@ const (
defaultWriteQuota = 64 * 1024
defaultClientMaxHeaderListSize = uint32(16 << 20)
defaultServerMaxHeaderListSize = uint32(16 << 20)
+ upcomingDefaultHeaderListSize = uint32(8 << 10)
)
// MaxStreamID is the upper bound for the stream ID before the current
diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_client.go b/vendor/google.golang.org/grpc/internal/transport/http2_client.go
index 37b1acc34..c943503f3 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http2_client.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http2_client.go
@@ -871,11 +871,15 @@ func (t *http2Client) NewStream(ctx context.Context, callHdr *CallHdr, handler s
}
var sz int64
for _, f := range hdr.hf {
- if sz += int64(f.Size()); sz > int64(*t.maxSendHeaderListSize) {
+ sz += int64(f.Size())
+ if sz > int64(*t.maxSendHeaderListSize) {
hdrListSizeErr = status.Errorf(codes.Internal, "header list size to send violates the maximum size (%d bytes) set by server", *t.maxSendHeaderListSize)
return false
}
}
+ if sz > int64(upcomingDefaultHeaderListSize) {
+ t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In a future release, this will be restricted to %d bytes.", sz, upcomingDefaultHeaderListSize, upcomingDefaultHeaderListSize)
+ }
return true
}
for {
diff --git a/vendor/google.golang.org/grpc/internal/transport/http2_server.go b/vendor/google.golang.org/grpc/internal/transport/http2_server.go
index a1a14e14f..3a8c36e4f 100644
--- a/vendor/google.golang.org/grpc/internal/transport/http2_server.go
+++ b/vendor/google.golang.org/grpc/internal/transport/http2_server.go
@@ -940,13 +940,17 @@ func (t *http2Server) checkForHeaderListSize(hf []hpack.HeaderField) bool {
}
var sz int64
for _, f := range hf {
- if sz += int64(f.Size()); sz > int64(*t.maxSendHeaderListSize) {
+ sz += int64(f.Size())
+ if sz > int64(*t.maxSendHeaderListSize) {
if t.logger.V(logLevel) {
t.logger.Infof("Header list size to send violates the maximum size (%d bytes) set by client", *t.maxSendHeaderListSize)
}
return false
}
}
+ if sz > int64(upcomingDefaultHeaderListSize) {
+ t.logger.Warningf("Header list size to send (%d bytes) is larger than the upcoming default limit (%d bytes). In a future release, this will be restricted to %d bytes.", sz, upcomingDefaultHeaderListSize, upcomingDefaultHeaderListSize)
+ }
return true
}
diff --git a/vendor/google.golang.org/grpc/mem/buffer_pool.go b/vendor/google.golang.org/grpc/mem/buffer_pool.go
index 2ea763a49..3b02b9091 100644
--- a/vendor/google.golang.org/grpc/mem/buffer_pool.go
+++ b/vendor/google.golang.org/grpc/mem/buffer_pool.go
@@ -19,10 +19,10 @@
package mem
import (
- "sort"
- "sync"
+ "fmt"
"google.golang.org/grpc/internal"
+ "google.golang.org/grpc/internal/mem"
)
// BufferPool is a pool of buffers that can be shared and reused, resulting in
@@ -38,20 +38,23 @@ type BufferPool interface {
Put(*[]byte)
}
-const goPageSize = 4 << 10 // 4KiB. N.B. this must be a power of 2.
-
-var defaultBufferPoolSizes = []int{
- 256,
- goPageSize,
- 16 << 10, // 16KB (max HTTP/2 frame size used by gRPC)
- 32 << 10, // 32KB (default buffer size for io.Copy)
- 1 << 20, // 1MB
-}
-
-var defaultBufferPool BufferPool
+var (
+ defaultBufferPoolSizeExponents = []uint8{
+ 8,
+ 12, // Go page size, 4KB
+ 14, // 16KB (max HTTP/2 frame size used by gRPC)
+ 15, // 32KB (default buffer size for io.Copy)
+ 20, // 1MB
+ }
+ defaultBufferPool BufferPool
+)
func init() {
- defaultBufferPool = NewTieredBufferPool(defaultBufferPoolSizes...)
+ var err error
+ defaultBufferPool, err = NewBinaryTieredBufferPool(defaultBufferPoolSizeExponents...)
+ if err != nil {
+ panic(fmt.Sprintf("Failed to create default buffer pool: %v", err))
+ }
internal.SetDefaultBufferPool = func(pool BufferPool) {
defaultBufferPool = pool
@@ -72,134 +75,22 @@ func DefaultBufferPool() BufferPool {
// NewTieredBufferPool returns a BufferPool implementation that uses multiple
// underlying pools of the given pool sizes.
func NewTieredBufferPool(poolSizes ...int) BufferPool {
- sort.Ints(poolSizes)
- pools := make([]*sizedBufferPool, len(poolSizes))
- for i, s := range poolSizes {
- pools[i] = newSizedBufferPool(s)
- }
- return &tieredBufferPool{
- sizedPools: pools,
- }
+ return mem.NewTieredBufferPool(poolSizes...)
}
-// tieredBufferPool implements the BufferPool interface with multiple tiers of
-// buffer pools for different sizes of buffers.
-type tieredBufferPool struct {
- sizedPools []*sizedBufferPool
- fallbackPool simpleBufferPool
+// NewBinaryTieredBufferPool returns a BufferPool backed by multiple sub-pools.
+// This structure enables O(1) lookup time for Get and Put operations.
+//
+// The arguments provided are the exponents for the buffer capacities (powers
+// of 2), not the raw byte sizes. For example, to create a pool of 16KB buffers
+// (2^14 bytes), pass 14 as the argument.
+func NewBinaryTieredBufferPool(powerOfTwoExponents ...uint8) (BufferPool, error) {
+ return mem.NewBinaryTieredBufferPool(powerOfTwoExponents...)
}
-func (p *tieredBufferPool) Get(size int) *[]byte {
- return p.getPool(size).Get(size)
-}
-
-func (p *tieredBufferPool) Put(buf *[]byte) {
- p.getPool(cap(*buf)).Put(buf)
-}
-
-func (p *tieredBufferPool) getPool(size int) BufferPool {
- poolIdx := sort.Search(len(p.sizedPools), func(i int) bool {
- return p.sizedPools[i].defaultSize >= size
- })
-
- if poolIdx == len(p.sizedPools) {
- return &p.fallbackPool
- }
-
- return p.sizedPools[poolIdx]
-}
-
-// sizedBufferPool is a BufferPool implementation that is optimized for specific
-// buffer sizes. For example, HTTP/2 frames within gRPC have a default max size
-// of 16kb and a sizedBufferPool can be configured to only return buffers with a
-// capacity of 16kb. Note that however it does not support returning larger
-// buffers and in fact panics if such a buffer is requested. Because of this,
-// this BufferPool implementation is not meant to be used on its own and rather
-// is intended to be embedded in a tieredBufferPool such that Get is only
-// invoked when the required size is smaller than or equal to defaultSize.
-type sizedBufferPool struct {
- pool sync.Pool
- defaultSize int
-}
-
-func (p *sizedBufferPool) Get(size int) *[]byte {
- buf, ok := p.pool.Get().(*[]byte)
- if !ok {
- buf := make([]byte, size, p.defaultSize)
- return &buf
- }
- b := *buf
- clear(b[:cap(b)])
- *buf = b[:size]
- return buf
-}
-
-func (p *sizedBufferPool) Put(buf *[]byte) {
- if cap(*buf) < p.defaultSize {
- // Ignore buffers that are too small to fit in the pool. Otherwise, when
- // Get is called it will panic as it tries to index outside the bounds
- // of the buffer.
- return
- }
- p.pool.Put(buf)
-}
-
-func newSizedBufferPool(size int) *sizedBufferPool {
- return &sizedBufferPool{
- defaultSize: size,
- }
-}
-
-var _ BufferPool = (*simpleBufferPool)(nil)
-
-// simpleBufferPool is an implementation of the BufferPool interface that
-// attempts to pool buffers with a sync.Pool. When Get is invoked, it tries to
-// acquire a buffer from the pool but if that buffer is too small, it returns it
-// to the pool and creates a new one.
-type simpleBufferPool struct {
- pool sync.Pool
-}
-
-func (p *simpleBufferPool) Get(size int) *[]byte {
- bs, ok := p.pool.Get().(*[]byte)
- if ok && cap(*bs) >= size {
- clear((*bs)[:cap(*bs)])
- *bs = (*bs)[:size]
- return bs
- }
-
- // A buffer was pulled from the pool, but it is too small. Put it back in
- // the pool and create one large enough.
- if ok {
- p.pool.Put(bs)
- }
-
- // If we're going to allocate, round up to the nearest page. This way if
- // requests frequently arrive with small variation we don't allocate
- // repeatedly if we get unlucky and they increase over time. By default we
- // only allocate here if size > 1MiB. Because goPageSize is a power of 2, we
- // can round up efficiently.
- allocSize := (size + goPageSize - 1) & ^(goPageSize - 1)
-
- b := make([]byte, size, allocSize)
- return &b
-}
-
-func (p *simpleBufferPool) Put(buf *[]byte) {
- p.pool.Put(buf)
+// NopBufferPool is a buffer pool that returns new buffers without pooling.
+type NopBufferPool struct {
+ mem.NopBufferPool
}
var _ BufferPool = NopBufferPool{}
-
-// NopBufferPool is a buffer pool that returns new buffers without pooling.
-type NopBufferPool struct{}
-
-// Get returns a buffer with specified length from the pool.
-func (NopBufferPool) Get(length int) *[]byte {
- b := make([]byte, length)
- return &b
-}
-
-// Put returns a buffer to the pool.
-func (NopBufferPool) Put(*[]byte) {
-}
diff --git a/vendor/google.golang.org/grpc/picker_wrapper.go b/vendor/google.golang.org/grpc/picker_wrapper.go
index aa52bfe95..0183ab22f 100644
--- a/vendor/google.golang.org/grpc/picker_wrapper.go
+++ b/vendor/google.golang.org/grpc/picker_wrapper.go
@@ -192,7 +192,9 @@ func (pw *pickerWrapper) pick(ctx context.Context, failfast bool, info balancer.
// DoneInfo with default value works.
pickResult.Done(balancer.DoneInfo{})
}
- logger.Infof("blockingPicker: the picked transport is not ready, loop back to repick")
+ if logger.V(2) {
+ logger.Infof("blockingPicker: the picked transport is not ready, loop back to repick")
+ }
// If ok == false, ac.state is not READY.
// A valid picker always returns READY subConn. This means the state of ac
// just changed, and picker will be updated shortly.
diff --git a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection.pb.go b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection.pb.go
index 92fdc3afa..556ab033d 100644
--- a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection.pb.go
+++ b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection.pb.go
@@ -21,7 +21,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.36.10
+// protoc-gen-go v1.36.11
// protoc v5.27.1
// source: grpc/reflection/v1/reflection.proto
diff --git a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go
index 93a243631..81ced2fbf 100644
--- a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go
+++ b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1/reflection_grpc.pb.go
@@ -21,7 +21,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
-// - protoc-gen-go-grpc v1.6.0
+// - protoc-gen-go-grpc v1.6.1
// - protoc v5.27.1
// source: grpc/reflection/v1/reflection.proto
diff --git a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.pb.go b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.pb.go
index c803cf3ba..6a46bb51f 100644
--- a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.pb.go
+++ b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection.pb.go
@@ -18,7 +18,7 @@
// Code generated by protoc-gen-go. DO NOT EDIT.
// versions:
-// protoc-gen-go v1.36.10
+// protoc-gen-go v1.36.11
// protoc v5.27.1
// grpc/reflection/v1alpha/reflection.proto is a deprecated file.
diff --git a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go
index cee004ab5..18061e8ba 100644
--- a/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go
+++ b/vendor/google.golang.org/grpc/reflection/grpc_reflection_v1alpha/reflection_grpc.pb.go
@@ -18,7 +18,7 @@
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
// versions:
-// - protoc-gen-go-grpc v1.6.0
+// - protoc-gen-go-grpc v1.6.1
// - protoc v5.27.1
// grpc/reflection/v1alpha/reflection.proto is a deprecated file.
diff --git a/vendor/google.golang.org/grpc/resolver/map.go b/vendor/google.golang.org/grpc/resolver/map.go
index c3c15ac96..789a5abab 100644
--- a/vendor/google.golang.org/grpc/resolver/map.go
+++ b/vendor/google.golang.org/grpc/resolver/map.go
@@ -20,6 +20,7 @@ package resolver
import (
"encoding/base64"
+ "iter"
"sort"
"strings"
)
@@ -135,6 +136,7 @@ func (a *AddressMapV2[T]) Len() int {
}
// Keys returns a slice of all current map keys.
+// Deprecated: Use AddressMapV2.All() instead.
func (a *AddressMapV2[T]) Keys() []Address {
ret := make([]Address, 0, a.Len())
for _, entryList := range a.m {
@@ -146,6 +148,7 @@ func (a *AddressMapV2[T]) Keys() []Address {
}
// Values returns a slice of all current map values.
+// Deprecated: Use AddressMapV2.All() instead.
func (a *AddressMapV2[T]) Values() []T {
ret := make([]T, 0, a.Len())
for _, entryList := range a.m {
@@ -156,6 +159,19 @@ func (a *AddressMapV2[T]) Values() []T {
return ret
}
+// All returns an iterator over all elements.
+func (a *AddressMapV2[T]) All() iter.Seq2[Address, T] {
+ return func(yield func(Address, T) bool) {
+ for _, entryList := range a.m {
+ for _, entry := range entryList {
+ if !yield(entry.addr, entry.value) {
+ return
+ }
+ }
+ }
+ }
+}
+
type endpointMapKey string
// EndpointMap is a map of endpoints to arbitrary values keyed on only the
@@ -223,6 +239,7 @@ func (em *EndpointMap[T]) Len() int {
// the unordered set of addresses. Thus, endpoint information returned is not
// the full endpoint data (drops duplicated addresses and attributes) but can be
// used for EndpointMap accesses.
+// Deprecated: Use EndpointMap.All() instead.
func (em *EndpointMap[T]) Keys() []Endpoint {
ret := make([]Endpoint, 0, len(em.endpoints))
for _, en := range em.endpoints {
@@ -232,6 +249,7 @@ func (em *EndpointMap[T]) Keys() []Endpoint {
}
// Values returns a slice of all current map values.
+// Deprecated: Use EndpointMap.All() instead.
func (em *EndpointMap[T]) Values() []T {
ret := make([]T, 0, len(em.endpoints))
for _, val := range em.endpoints {
@@ -240,6 +258,22 @@ func (em *EndpointMap[T]) Values() []T {
return ret
}
+// All returns an iterator over all elements.
+// The map keys are endpoints specifying the addresses present in the endpoint
+// map, in which uniqueness is determined by the unordered set of addresses.
+// Thus, endpoint information returned is not the full endpoint data (drops
+// duplicated addresses and attributes) but can be used for EndpointMap
+// accesses.
+func (em *EndpointMap[T]) All() iter.Seq2[Endpoint, T] {
+ return func(yield func(Endpoint, T) bool) {
+ for _, en := range em.endpoints {
+ if !yield(en.decodedKey, en.value) {
+ return
+ }
+ }
+ }
+}
+
// Delete removes the specified endpoint from the map.
func (em *EndpointMap[T]) Delete(e Endpoint) {
en := encodeEndpoint(e)
diff --git a/vendor/google.golang.org/grpc/rpc_util.go b/vendor/google.golang.org/grpc/rpc_util.go
index 8160f9430..ee7f7dead 100644
--- a/vendor/google.golang.org/grpc/rpc_util.go
+++ b/vendor/google.golang.org/grpc/rpc_util.go
@@ -961,24 +961,32 @@ func recvAndDecompress(p *parser, s recvCompressor, dc Decompressor, maxReceiveM
return out, nil
}
-// decompress processes the given data by decompressing it using either a custom decompressor or a standard compressor.
-// If a custom decompressor is provided, it takes precedence. The function validates that the decompressed data
-// does not exceed the specified maximum size and returns an error if this limit is exceeded.
-// On success, it returns the decompressed data. Otherwise, it returns an error if decompression fails or the data exceeds the size limit.
+// decompress processes the given data by decompressing it using either
+// a custom decompressor or a standard compressor. If a custom decompressor
+// is provided, it takes precedence. The function validates that
+// the decompressed data does not exceed the specified maximum size and returns
+// an error if this limit is exceeded. On success, it returns the decompressed
+// data. Otherwise, it returns an error if decompression fails or the data
+// exceeds the size limit.
func decompress(compressor encoding.Compressor, d mem.BufferSlice, dc Decompressor, maxReceiveMessageSize int, pool mem.BufferPool) (mem.BufferSlice, error) {
if dc != nil {
- uncompressed, err := dc.Do(d.Reader())
+ r := d.Reader()
+ uncompressed, err := dc.Do(r)
if err != nil {
+ r.Close() // ensure buffers are reused
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the received message: %v", err)
}
if len(uncompressed) > maxReceiveMessageSize {
+ r.Close() // ensure buffers are reused
return nil, status.Errorf(codes.ResourceExhausted, "grpc: message after decompression larger than max (%d vs. %d)", len(uncompressed), maxReceiveMessageSize)
}
return mem.BufferSlice{mem.SliceBuffer(uncompressed)}, nil
}
if compressor != nil {
- dcReader, err := compressor.Decompress(d.Reader())
+ r := d.Reader()
+ dcReader, err := compressor.Decompress(r)
if err != nil {
+ r.Close() // ensure buffers are reused
return nil, status.Errorf(codes.Internal, "grpc: failed to decompress the message: %v", err)
}
@@ -990,11 +998,13 @@ func decompress(compressor encoding.Compressor, d mem.BufferSlice, dc Decompress
}
out, err := mem.ReadAll(dcReader, pool)
if err != nil {
+ r.Close() // ensure buffers are reused
out.Free()
return nil, status.Errorf(codes.Internal, "grpc: failed to read decompressed data: %v", err)
}
if out.Len() > maxReceiveMessageSize {
+ r.Close() // ensure buffers are reused
out.Free()
return nil, status.Errorf(codes.ResourceExhausted, "grpc: received message after decompression larger than max %d", maxReceiveMessageSize)
}
diff --git a/vendor/google.golang.org/grpc/server.go b/vendor/google.golang.org/grpc/server.go
index 8efb29a7b..5229adf71 100644
--- a/vendor/google.golang.org/grpc/server.go
+++ b/vendor/google.golang.org/grpc/server.go
@@ -192,6 +192,7 @@ var defaultServerOptions = serverOptions{
maxSendMessageSize: defaultServerMaxSendMessageSize,
connectionTimeout: 120 * time.Second,
writeBufferSize: defaultWriteBufSize,
+ sharedWriteBuffer: true,
readBufferSize: defaultReadBufSize,
bufferPool: mem.DefaultBufferPool(),
}
diff --git a/vendor/google.golang.org/grpc/version.go b/vendor/google.golang.org/grpc/version.go
index 76c2eed77..12f649dcb 100644
--- a/vendor/google.golang.org/grpc/version.go
+++ b/vendor/google.golang.org/grpc/version.go
@@ -19,4 +19,4 @@
package grpc
// Version is the current grpc version.
-const Version = "1.79.3"
+const Version = "1.80.0"
diff --git a/vendor/modules.txt b/vendor/modules.txt
index ef947c5a0..8506e9f03 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -295,7 +295,7 @@ github.com/coreos/go-oidc/v3/oidc
# github.com/coreos/go-semver v0.3.1
## explicit; go 1.8
github.com/coreos/go-semver/semver
-# github.com/coreos/go-systemd/v22 v22.6.0
+# github.com/coreos/go-systemd/v22 v22.7.0
## explicit; go 1.23
github.com/coreos/go-systemd/v22/journal
# github.com/cornelk/hashmap v1.0.8
@@ -497,15 +497,15 @@ github.com/go-git/gcfg
github.com/go-git/gcfg/scanner
github.com/go-git/gcfg/token
github.com/go-git/gcfg/types
-# github.com/go-git/go-billy/v5 v5.6.2
-## explicit; go 1.21
+# github.com/go-git/go-billy/v5 v5.8.0
+## explicit; go 1.24.0
github.com/go-git/go-billy/v5
github.com/go-git/go-billy/v5/helper/chroot
github.com/go-git/go-billy/v5/helper/polyfill
github.com/go-git/go-billy/v5/memfs
github.com/go-git/go-billy/v5/osfs
github.com/go-git/go-billy/v5/util
-# github.com/go-git/go-git/v5 v5.16.5
+# github.com/go-git/go-git/v5 v5.17.1
## explicit; go 1.24.0
github.com/go-git/go-git/v5
github.com/go-git/go-git/v5/config
@@ -562,7 +562,7 @@ github.com/go-jose/go-jose/v3
github.com/go-jose/go-jose/v3/cipher
github.com/go-jose/go-jose/v3/json
github.com/go-jose/go-jose/v3/jwt
-# github.com/go-jose/go-jose/v4 v4.1.3
+# github.com/go-jose/go-jose/v4 v4.1.4
## explicit; go 1.24.0
github.com/go-jose/go-jose/v4
github.com/go-jose/go-jose/v4/cipher
@@ -1371,7 +1371,7 @@ github.com/opencloud-eu/icap-client
# github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20260310090739-853d972b282d
## explicit; go 1.18
github.com/opencloud-eu/libre-graph-api-go
-# github.com/opencloud-eu/reva/v2 v2.42.6
+# github.com/opencloud-eu/reva/v2 v2.42.7-0.20260408072824-411780d0b756
## explicit; go 1.25.0
github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace
github.com/opencloud-eu/reva/v2/cmd/revad/runtime
@@ -1487,11 +1487,8 @@ github.com/opencloud-eu/reva/v2/pkg/auth/manager/json
github.com/opencloud-eu/reva/v2/pkg/auth/manager/ldap
github.com/opencloud-eu/reva/v2/pkg/auth/manager/loader
github.com/opencloud-eu/reva/v2/pkg/auth/manager/machine
-github.com/opencloud-eu/reva/v2/pkg/auth/manager/nextcloud
github.com/opencloud-eu/reva/v2/pkg/auth/manager/ocmshares
github.com/opencloud-eu/reva/v2/pkg/auth/manager/oidc
-github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql
-github.com/opencloud-eu/reva/v2/pkg/auth/manager/owncloudsql/accounts
github.com/opencloud-eu/reva/v2/pkg/auth/manager/publicshares
github.com/opencloud-eu/reva/v2/pkg/auth/manager/registry
github.com/opencloud-eu/reva/v2/pkg/auth/manager/serviceaccounts
@@ -1737,9 +1734,6 @@ github.com/opencloud-eu/reva/v2/pkg/user/manager/json
github.com/opencloud-eu/reva/v2/pkg/user/manager/ldap
github.com/opencloud-eu/reva/v2/pkg/user/manager/loader
github.com/opencloud-eu/reva/v2/pkg/user/manager/memory
-github.com/opencloud-eu/reva/v2/pkg/user/manager/nextcloud
-github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql
-github.com/opencloud-eu/reva/v2/pkg/user/manager/owncloudsql/accounts
github.com/opencloud-eu/reva/v2/pkg/user/manager/registry
github.com/opencloud-eu/reva/v2/pkg/utils
github.com/opencloud-eu/reva/v2/pkg/utils/cfg
@@ -1877,8 +1871,8 @@ github.com/rs/cors/internal
# github.com/rs/xid v1.6.0
## explicit; go 1.16
github.com/rs/xid
-# github.com/rs/zerolog v1.34.0
-## explicit; go 1.15
+# github.com/rs/zerolog v1.35.0
+## explicit; go 1.23
github.com/rs/zerolog
github.com/rs/zerolog/internal/cbor
github.com/rs/zerolog/internal/json
@@ -1899,17 +1893,17 @@ github.com/rwcarlsen/goexif/tiff
# github.com/sagikazarmark/locafero v0.11.0
## explicit; go 1.23.0
github.com/sagikazarmark/locafero
-# github.com/samber/lo v1.52.0
+# github.com/samber/lo v1.53.0
## explicit; go 1.18
github.com/samber/lo
github.com/samber/lo/internal/constraints
github.com/samber/lo/internal/xrand
github.com/samber/lo/internal/xtime
github.com/samber/lo/mutable
-# github.com/samber/slog-common v0.20.0
+# github.com/samber/slog-common v0.21.0
## explicit; go 1.21
github.com/samber/slog-common
-# github.com/samber/slog-zerolog/v2 v2.9.1
+# github.com/samber/slog-zerolog/v2 v2.9.2
## explicit; go 1.21
github.com/samber/slog-zerolog/v2
# github.com/segmentio/asm v1.2.1
@@ -2309,7 +2303,7 @@ go.opencensus.io/trace/tracestate
## explicit; go 1.24.0
go.opentelemetry.io/auto/sdk
go.opentelemetry.io/auto/sdk/internal/telemetry
-# go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0
+# go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.68.0
## explicit; go 1.25.0
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc/internal
@@ -2322,7 +2316,7 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp/internal/semconv
## explicit; go 1.25.0
go.opentelemetry.io/contrib/zpages
go.opentelemetry.io/contrib/zpages/internal
-# go.opentelemetry.io/otel v1.42.0
+# go.opentelemetry.io/otel v1.43.0
## explicit; go 1.25.0
go.opentelemetry.io/otel
go.opentelemetry.io/otel/attribute
@@ -2338,6 +2332,7 @@ go.opentelemetry.io/otel/semconv/internal/v4
go.opentelemetry.io/otel/semconv/v1.20.0
go.opentelemetry.io/otel/semconv/v1.20.0/httpconv
go.opentelemetry.io/otel/semconv/v1.37.0
+go.opentelemetry.io/otel/semconv/v1.37.0/rpcconv
go.opentelemetry.io/otel/semconv/v1.40.0
go.opentelemetry.io/otel/semconv/v1.40.0/httpconv
go.opentelemetry.io/otel/semconv/v1.40.0/otelconv
@@ -2363,12 +2358,12 @@ go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal
go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/counter
go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/observ
go.opentelemetry.io/otel/exporters/stdout/stdouttrace/internal/x
-# go.opentelemetry.io/otel/metric v1.42.0
+# go.opentelemetry.io/otel/metric v1.43.0
## explicit; go 1.25.0
go.opentelemetry.io/otel/metric
go.opentelemetry.io/otel/metric/embedded
go.opentelemetry.io/otel/metric/noop
-# go.opentelemetry.io/otel/sdk v1.42.0
+# go.opentelemetry.io/otel/sdk v1.43.0
## explicit; go 1.25.0
go.opentelemetry.io/otel/sdk
go.opentelemetry.io/otel/sdk/instrumentation
@@ -2378,7 +2373,7 @@ go.opentelemetry.io/otel/sdk/trace
go.opentelemetry.io/otel/sdk/trace/internal/env
go.opentelemetry.io/otel/sdk/trace/internal/observ
go.opentelemetry.io/otel/sdk/trace/tracetest
-# go.opentelemetry.io/otel/trace v1.42.0
+# go.opentelemetry.io/otel/trace v1.43.0
## explicit; go 1.25.0
go.opentelemetry.io/otel/trace
go.opentelemetry.io/otel/trace/embedded
@@ -2579,11 +2574,11 @@ google.golang.org/genproto/protobuf/field_mask
google.golang.org/genproto/googleapis/api
google.golang.org/genproto/googleapis/api/annotations
google.golang.org/genproto/googleapis/api/httpbody
-# google.golang.org/genproto/googleapis/rpc v0.0.0-20260226221140-a57be14db171
+# google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d
## explicit; go 1.25.0
google.golang.org/genproto/googleapis/rpc/errdetails
google.golang.org/genproto/googleapis/rpc/status
-# google.golang.org/grpc v1.79.3
+# google.golang.org/grpc v1.80.0
## explicit; go 1.24.0
google.golang.org/grpc
google.golang.org/grpc/attributes
@@ -2624,6 +2619,7 @@ google.golang.org/grpc/internal/grpclog
google.golang.org/grpc/internal/grpcsync
google.golang.org/grpc/internal/grpcutil
google.golang.org/grpc/internal/idle
+google.golang.org/grpc/internal/mem
google.golang.org/grpc/internal/metadata
google.golang.org/grpc/internal/pretty
google.golang.org/grpc/internal/proxyattributes