Bump reva

Fixes: #1871, #1867
This commit is contained in:
Ralf Haferkamp
2025-11-20 12:50:52 +01:00
committed by Ralf Haferkamp
parent 90e4127227
commit e85d8effc1
92 changed files with 11404 additions and 8988 deletions

14
go.mod
View File

@@ -64,7 +64,7 @@ require (
github.com/open-policy-agent/opa v1.10.1
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76
github.com/opencloud-eu/reva/v2 v2.39.3-0.20251113164418-9fd6b6864c10
github.com/opencloud-eu/reva/v2 v2.39.3-0.20251120114614-1b573c55f789
github.com/opensearch-project/opensearch-go/v4 v4.5.0
github.com/orcaman/concurrent-map v1.0.0
github.com/pkg/errors v0.9.1
@@ -101,14 +101,14 @@ require (
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0
go.opentelemetry.io/otel/sdk v1.38.0
go.opentelemetry.io/otel/trace v1.38.0
golang.org/x/crypto v0.43.0
golang.org/x/crypto v0.44.0
golang.org/x/exp v0.0.0-20250210185358-939b2ce775ac
golang.org/x/image v0.32.0
golang.org/x/net v0.46.0
golang.org/x/oauth2 v0.33.0
golang.org/x/sync v0.18.0
golang.org/x/term v0.37.0
golang.org/x/text v0.30.0
golang.org/x/text v0.31.0
google.golang.org/genproto/googleapis/api v0.0.0-20250929231259-57b25ae835d4
google.golang.org/grpc v1.76.0
google.golang.org/protobuf v1.36.10
@@ -242,7 +242,7 @@ require (
github.com/google/go-querystring v1.1.0 // indirect
github.com/google/go-tpm v0.9.6 // indirect
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 // indirect
github.com/google/renameio/v2 v2.0.0 // indirect
github.com/google/renameio/v2 v2.0.1 // indirect
github.com/gookit/goutil v0.7.1 // indirect
github.com/gorilla/handlers v1.5.1 // indirect
github.com/gorilla/schema v1.4.1 // indirect
@@ -334,6 +334,9 @@ require (
github.com/russellhaering/goxmldsig v1.5.0 // indirect
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd // indirect
github.com/samber/lo v1.51.0 // indirect
github.com/samber/slog-common v0.19.0 // indirect
github.com/samber/slog-zerolog/v2 v2.9.0 // indirect
github.com/segmentio/asm v1.2.0 // indirect
github.com/segmentio/kafka-go v0.4.49 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
@@ -403,3 +406,6 @@ replace go-micro.dev/v4 => github.com/butonic/go-micro/v4 v4.11.1-0.202411151126
exclude github.com/mattn/go-sqlite3 v2.0.3+incompatible
replace github.com/go-micro/plugins/v4/store/nats-js-kv => github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a
// to get the logger injection (https://github.com/pablodz/inotifywaitgo/pull/11)
replace github.com/pablodz/inotifywaitgo v0.0.9 => github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9

26
go.sum
View File

@@ -587,8 +587,8 @@ github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hf
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/renameio/v2 v2.0.0 h1:UifI23ZTGY8Tt29JbYFiuyIU3eX+RNFtUwefq9qAhxg=
github.com/google/renameio/v2 v2.0.0/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/google/renameio/v2 v2.0.1 h1:HyOM6qd9gF9sf15AvhbptGHUnaLTpEI9akAFFU3VyW0=
github.com/google/renameio/v2 v2.0.1/go.mod h1:BtmJXm5YlszgC+TD4HOEEUFgkJP3nLxehU6hfe7jRt4=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
@@ -957,10 +957,12 @@ github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-202505121527
github.com/opencloud-eu/go-micro-plugins/v4/store/nats-js-kv v0.0.0-20250512152754-23325793059a/go.mod h1:pjcozWijkNPbEtX5SIQaxEW/h8VAVZYTLx+70bmB3LY=
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89 h1:W1ms+lP5lUUIzjRGDg93WrQfZJZCaV1ZP3KeyXi8bzY=
github.com/opencloud-eu/icap-client v0.0.0-20250930132611-28a2afe62d89/go.mod h1:vigJkNss1N2QEceCuNw/ullDehncuJNFB6mEnzfq9UI=
github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9 h1:dIftlX03Bzfbujhp9B54FbgER0VBDWJi/w8RBxJlzxU=
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.20250724122329-41ba6b191e76 h1:vD/EdfDUrv4omSFjrinT8Mvf+8D7f9g4vgQ2oiDrVUI=
github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76/go.mod h1:pzatilMEHZFT3qV7C/X3MqOa3NlRQuYhlRhZTL+hN6Q=
github.com/opencloud-eu/reva/v2 v2.39.3-0.20251113164418-9fd6b6864c10 h1:9b5O3lzYHmR+aDNo81UYMcDGfUARrHw5Suk4YmqNgJA=
github.com/opencloud-eu/reva/v2 v2.39.3-0.20251113164418-9fd6b6864c10/go.mod h1:YxP7b+8olAhgbQBUUnsRQokgf1RkwpEBLq614XXXXHA=
github.com/opencloud-eu/reva/v2 v2.39.3-0.20251120114614-1b573c55f789 h1:2B/xhc0Q0W61embhafcBQyY3z5oJ7VS+OjHerd2Jcvg=
github.com/opencloud-eu/reva/v2 v2.39.3-0.20251120114614-1b573c55f789/go.mod h1:hJ+wEfhpmKix5/BQpwQPqgK/o9dugaumDFkRG/U6X8E=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
@@ -977,8 +979,6 @@ github.com/orcaman/concurrent-map v1.0.0/go.mod h1:Lu3tH6HLW3feq74c2GC+jIMS/K2CF
github.com/ovh/go-ovh v1.1.0/go.mod h1:AxitLZ5HBRPyUd+Zl60Ajaag+rNTdVXWIkzfrVuTXWA=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c h1:rp5dCmg/yLR3mgFuSOe4oEnDDmGLROTvMragMUXpTQw=
github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c/go.mod h1:X07ZCGwUbLaax7L0S3Tw4hpejzu63ZrrQiUe6W0hcy0=
github.com/pablodz/inotifywaitgo v0.0.9 h1:njquRbBU7fuwIe5rEvtaniVBjwWzcpdUVptSgzFqZsw=
github.com/pablodz/inotifywaitgo v0.0.9/go.mod h1:hAfx2oN+WKg8miwUKPs52trySpPignlRBRxWcXVHku0=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
@@ -1091,6 +1091,12 @@ github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd h1:CmH9+J6ZSsIjUK
github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk=
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/sacloud/libsacloud v1.36.2/go.mod h1:P7YAOVmnIn3DKHqCZcUKYUXmSwGBm3yS7IBEjKVSrjg=
github.com/samber/lo v1.51.0 h1:kysRYLbHy/MB7kQZf5DSN50JHmMsNEdeY24VzJFu7wI=
github.com/samber/lo v1.51.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=
github.com/samber/slog-common v0.19.0 h1:fNcZb8B2uOLooeYwFpAlKjkQTUafdjfqKcwcC89G9YI=
github.com/samber/slog-common v0.19.0/go.mod h1:dTz+YOU76aH007YUU0DffsXNsGFQRQllPQh9XyNoA3M=
github.com/samber/slog-zerolog/v2 v2.9.0 h1:6LkOabJmZdNLaUWkTC3IVVA+dq7b/V0FM6lz6/7+THI=
github.com/samber/slog-zerolog/v2 v2.9.0/go.mod h1:gnQW9VnCfM34v2pRMUIGMsZOVbYLqY/v0Wxu6atSVGc=
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.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
@@ -1351,8 +1357,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04=
golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0=
golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU=
golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1591,8 +1597,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=

View File

@@ -1,5 +1,24 @@
version: "2"
linters:
disable:
- errcheck
- errcheck
exclusions:
generated: lax
presets:
- comments
- common-false-positives
- legacy
- std-error-handling
paths:
- third_party$
- builtin$
- examples$
formatters:
enable:
- gofmt
- gofmt
exclusions:
generated: lax
paths:
- third_party$
- builtin$
- examples$

View File

@@ -1,6 +1,6 @@
[![Build Status](https://github.com/google/renameio/workflows/Test/badge.svg)](https://github.com/google/renameio/actions?query=workflow%3ATest)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/google/renameio)](https://pkg.go.dev/github.com/google/renameio)
[![Go Report Card](https://goreportcard.com/badge/github.com/google/renameio)](https://goreportcard.com/report/github.com/google/renameio)
[![PkgGoDev](https://pkg.go.dev/badge/github.com/google/renameio/v2)](https://pkg.go.dev/github.com/google/renameio/v2)
[![Go Report Card](https://goreportcard.com/badge/github.com/google/renameio/v2)](https://goreportcard.com/report/github.com/google/renameio/v2)
The `renameio` Go package provides a way to atomically create or replace a file or
symbolic link.

View File

@@ -77,3 +77,12 @@ func WithExistingPermissions() Option {
c.attemptPermCopy = true
})
}
// WithReplaceOnClose causes PendingFile.Close() to actually call
// CloseAtomicallyReplace(). This means PendingFile implements io.Closer while
// maintaining atomicity per default.
func WithReplaceOnClose() Option {
return optionFunc(func(c *config) {
c.renameOnClose = true
})
}

View File

@@ -114,9 +114,10 @@ func tempDir(dir, dest string) string {
type PendingFile struct {
*os.File
path string
done bool
closed bool
path string
done bool
closed bool
replaceOnClose bool
}
// Cleanup is a no-op if CloseAtomicallyReplace succeeded, and otherwise closes
@@ -131,7 +132,7 @@ func (t *PendingFile) Cleanup() error {
// reporting, there is nothing the caller can recover here.
var closeErr error
if !t.closed {
closeErr = t.Close()
closeErr = t.File.Close()
}
if err := os.Remove(t.Name()); err != nil {
return err
@@ -159,7 +160,7 @@ func (t *PendingFile) CloseAtomicallyReplace() error {
return err
}
t.closed = true
if err := t.Close(); err != nil {
if err := t.File.Close(); err != nil {
return err
}
if err := os.Rename(t.Name(), t.path); err != nil {
@@ -169,6 +170,15 @@ func (t *PendingFile) CloseAtomicallyReplace() error {
return nil
}
// Close closes the file. By default it just calls Close() on the underlying file. For PendingFiles created with
// WithReplaceOnClose it calls CloseAtomicallyReplace() instead.
func (t *PendingFile) Close() error {
if t.replaceOnClose {
return t.CloseAtomicallyReplace()
}
return t.File.Close()
}
// TempFile creates a temporary file destined to atomically creating or
// replacing the destination file at path.
//
@@ -189,6 +199,7 @@ type config struct {
attemptPermCopy bool
ignoreUmask bool
chmod *os.FileMode
renameOnClose bool
}
// NewPendingFile creates a temporary file destined to atomically creating or
@@ -244,7 +255,7 @@ func NewPendingFile(path string, opts ...Option) (*PendingFile, error) {
}
}
return &PendingFile{File: f, path: cfg.path}, nil
return &PendingFile{File: f, path: cfg.path, replaceOnClose: cfg.renameOnClose}, nil
}
// Symlink wraps os.Symlink, replacing an existing symlink with the same name

View File

@@ -24,7 +24,7 @@ import (
"github.com/opencloud-eu/reva/v2/pkg/appctx"
"github.com/rs/zerolog"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
)
@@ -42,7 +42,7 @@ func NewUnary(log zerolog.Logger, tp trace.TracerProvider) grpc.UnaryServerInter
}
_, file, _, ok := runtime.Caller(1)
if ok {
span.SetAttributes(semconv.CodeFilepathKey.String(file))
span.SetAttributes(semconv.CodeFilePathKey.String(file))
}
sub := log.With().Str("traceid", span.SpanContext().TraceID().String()).Logger()
@@ -67,7 +67,7 @@ func NewStream(log zerolog.Logger, tp trace.TracerProvider) grpc.StreamServerInt
}
_, file, _, ok := runtime.Caller(1)
if ok {
span.SetAttributes(semconv.CodeFilepathKey.String(file))
span.SetAttributes(semconv.CodeFilePathKey.String(file))
}
sub := log.With().Str("traceid", span.SpanContext().TraceID().String()).Logger()

View File

@@ -38,7 +38,7 @@ import (
tokenmgr "github.com/opencloud-eu/reva/v2/pkg/token/manager/registry"
"github.com/opencloud-eu/reva/v2/pkg/utils"
"github.com/pkg/errors"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"

View File

@@ -49,7 +49,7 @@ import (
"github.com/opencloud-eu/reva/v2/pkg/utils"
"github.com/pkg/errors"
"github.com/rs/zerolog"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/trace"
"google.golang.org/grpc/metadata"
)

View File

@@ -37,7 +37,7 @@ import (
"github.com/pkg/errors"
"github.com/rs/zerolog"
"go.opentelemetry.io/otel"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/trace"
)
@@ -127,8 +127,8 @@ func (s *svc) setHandler() {
ctx, span := tracer.Start(ctx, "HandlerFunc")
defer span.End()
span.SetAttributes(
semconv.HTTPMethodKey.String(r.Method),
semconv.HTTPURLKey.String(r.URL.String()),
semconv.HTTPRequestMethodKey.String(r.Method),
semconv.URLFullKey.String(r.URL.String()),
)
r = r.WithContext(ctx)
s.doRequest(w, r)

View File

@@ -58,7 +58,7 @@ import (
"github.com/rs/zerolog"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"golang.org/x/sync/errgroup"
"google.golang.org/protobuf/types/known/fieldmaskpb"
)
@@ -243,7 +243,7 @@ func (p *Handler) HandlePathPropfind(w http.ResponseWriter, r *http.Request, ns
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, "Invalid Depth header value")
span.SetAttributes(semconv.HTTPStatusCodeKey.Int(http.StatusBadRequest))
span.SetAttributes(semconv.HTTPResponseStatusCodeKey.Int(http.StatusBadRequest))
sublog.Debug().Str("depth", dh).Msg(err.Error())
w.WriteHeader(http.StatusBadRequest)
m := fmt.Sprintf("Invalid Depth header value: %v", dh)
@@ -255,7 +255,7 @@ func (p *Handler) HandlePathPropfind(w http.ResponseWriter, r *http.Request, ns
if depth == net.DepthInfinity && !p.c.AllowPropfindDepthInfinitiy {
span.RecordError(errors.ErrInvalidDepth)
span.SetStatus(codes.Error, "DEPTH: infinity is not supported")
span.SetAttributes(semconv.HTTPStatusCodeKey.Int(http.StatusBadRequest))
span.SetAttributes(semconv.HTTPResponseStatusCodeKey.Int(http.StatusBadRequest))
sublog.Debug().Str("depth", dh).Msg(errors.ErrInvalidDepth.Error())
w.WriteHeader(http.StatusBadRequest)
m := fmt.Sprintf("Invalid Depth header value: %v", dh)
@@ -312,7 +312,7 @@ func (p *Handler) HandleSpacesPropfind(w http.ResponseWriter, r *http.Request, s
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, "Invalid Depth header value")
span.SetAttributes(semconv.HTTPStatusCodeKey.Int(http.StatusBadRequest))
span.SetAttributes(semconv.HTTPResponseStatusCodeKey.Int(http.StatusBadRequest))
sublog.Debug().Str("depth", dh).Msg(err.Error())
w.WriteHeader(http.StatusBadRequest)
m := fmt.Sprintf("Invalid Depth header value: %v", dh)
@@ -324,7 +324,7 @@ func (p *Handler) HandleSpacesPropfind(w http.ResponseWriter, r *http.Request, s
if depth == net.DepthInfinity && !p.c.AllowPropfindDepthInfinitiy {
span.RecordError(errors.ErrInvalidDepth)
span.SetStatus(codes.Error, "DEPTH: infinity is not supported")
span.SetAttributes(semconv.HTTPStatusCodeKey.Int(http.StatusBadRequest))
span.SetAttributes(semconv.HTTPResponseStatusCodeKey.Int(http.StatusBadRequest))
sublog.Debug().Str("depth", dh).Msg(errors.ErrInvalidDepth.Error())
w.WriteHeader(http.StatusBadRequest)
m := fmt.Sprintf("Invalid Depth header value: %v", dh)

View File

@@ -32,7 +32,7 @@ import (
"github.com/opencloud-eu/reva/v2/pkg/appctx"
"github.com/opencloud-eu/reva/v2/pkg/rhttp/router"
"go.opentelemetry.io/otel/codes"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
)
// PublicFileHandler handles requests on a shared file. it needs to be wrapped in a collection
@@ -100,7 +100,7 @@ func (s *svc) handlePropfindOnToken(w http.ResponseWriter, r *http.Request, ns s
if !ok {
span.RecordError(ocdaverrors.ErrTokenStatInfoMissing)
span.SetStatus(codes.Error, ocdaverrors.ErrTokenStatInfoMissing.Error())
span.SetAttributes(semconv.HTTPStatusCodeKey.Int(http.StatusInternalServerError))
span.SetAttributes(semconv.HTTPResponseStatusCodeKey.Int(http.StatusInternalServerError))
w.WriteHeader(http.StatusInternalServerError)
b, err := ocdaverrors.Marshal(http.StatusInternalServerError, ocdaverrors.ErrTokenStatInfoMissing.Error(), "", "")
ocdaverrors.HandleWebdavError(appctx.GetLogger(ctx), w, b, err)
@@ -114,7 +114,7 @@ func (s *svc) handlePropfindOnToken(w http.ResponseWriter, r *http.Request, ns s
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, "Invalid Depth header value")
span.SetAttributes(semconv.HTTPStatusCodeKey.Int(http.StatusBadRequest))
span.SetAttributes(semconv.HTTPResponseStatusCodeKey.Int(http.StatusBadRequest))
sublog.Debug().Str("depth", dh).Msg(err.Error())
w.WriteHeader(http.StatusBadRequest)
m := fmt.Sprintf("Invalid Depth header value: %v", dh)
@@ -126,7 +126,7 @@ func (s *svc) handlePropfindOnToken(w http.ResponseWriter, r *http.Request, ns s
if depth == net.DepthInfinity && !s.c.AllowPropfindDepthInfinitiy {
span.RecordError(ocdaverrors.ErrInvalidDepth)
span.SetStatus(codes.Error, "DEPTH: infinity is not supported")
span.SetAttributes(semconv.HTTPStatusCodeKey.Int(http.StatusBadRequest))
span.SetAttributes(semconv.HTTPResponseStatusCodeKey.Int(http.StatusBadRequest))
sublog.Debug().Str("depth", dh).Msg(ocdaverrors.ErrInvalidDepth.Error())
w.WriteHeader(http.StatusBadRequest)
m := fmt.Sprintf("Invalid Depth header value: %v", dh)

View File

@@ -41,7 +41,7 @@ import (
rpc "github.com/cs3org/go-cs3apis/cs3/rpc/v1beta1"
provider "github.com/cs3org/go-cs3apis/cs3/storage/provider/v1beta1"
semconv "go.opentelemetry.io/otel/semconv/v1.20.0"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"github.com/opencloud-eu/reva/v2/pkg/appctx"
ctxpkg "github.com/opencloud-eu/reva/v2/pkg/ctx"
@@ -200,7 +200,7 @@ func (h *TrashbinHandler) listTrashbin(w http.ResponseWriter, r *http.Request, s
if err != nil {
span.RecordError(err)
span.SetStatus(codes.Error, "Invalid Depth header value")
span.SetAttributes(semconv.HTTPStatusCodeKey.Int(http.StatusBadRequest))
span.SetAttributes(semconv.HTTPResponseStatusCodeKey.Int(http.StatusBadRequest))
sublog.Debug().Str("depth", r.Header.Get(net.HeaderDepth)).Msg(err.Error())
w.WriteHeader(http.StatusBadRequest)
m := fmt.Sprintf("Invalid Depth header value: %v", r.Header.Get(net.HeaderDepth))

View File

@@ -28,7 +28,7 @@ import (
"go-micro.dev/v4/store"
"go.opentelemetry.io/otel"
"go.opentelemetry.io/otel/attribute"
semconv "go.opentelemetry.io/otel/semconv/v1.10.0"
semconv "go.opentelemetry.io/otel/semconv/v1.37.0"
"go.opentelemetry.io/otel/trace"
)

View File

@@ -67,7 +67,7 @@ func (bs *Blobstore) Upload(n *node.Node, source, copyTarget string) error {
_ = sourceFile.Close()
}()
tempFile, err := os.OpenFile(tempName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0700)
tempFile, err := os.OpenFile(tempName, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0600)
if err != nil {
return fmt.Errorf("unable to create temp file '%s': %v", tempName, err)
}

View File

@@ -22,6 +22,7 @@ package tree
import (
"fmt"
"log/slog"
"os"
"path/filepath"
"strconv"
@@ -31,6 +32,7 @@ import (
"github.com/opencloud-eu/reva/v2/pkg/storage/fs/posix/options"
"github.com/pablodz/inotifywaitgo/inotifywaitgo"
"github.com/rs/zerolog"
slogzerolog "github.com/samber/slog-zerolog/v2"
)
type InotifyWatcher struct {
@@ -56,6 +58,10 @@ func (iw *InotifyWatcher) Watch(path string) {
}
}()
}
// create a slog logger to be passed to the settings of inotifywatcher to log into
logger := slog.New(slogzerolog.Option{Level: slog.LevelDebug, Logger: iw.log}.NewZerologHandler())
events := make(chan inotifywaitgo.FileEvent)
errors := make(chan error)
@@ -76,6 +82,7 @@ func (iw *InotifyWatcher) Watch(path string) {
Monitor: true,
},
Verbose: false,
Log: logger,
})
for {

View File

@@ -272,7 +272,7 @@ func (tp *Tree) DownloadRevision(ctx context.Context, ref *provider.Reference, r
return ri, reader, nil
}
func (tp *Tree) RestoreRevision(ctx context.Context, srcNode, targetNode metadata.MetadataNode) error {
func (tp *Tree) RestoreRevision(ctx context.Context, srcNode, targetNode metadata.MetadataNode, mtime time.Time) error {
source := srcNode.InternalPath()
target := targetNode.InternalPath()
rf, err := os.Open(source)
@@ -305,8 +305,10 @@ func (tp *Tree) RestoreRevision(ctx context.Context, srcNode, targetNode metadat
return errtypes.InternalError("failed to copy blob xattrs to old revision to node: " + err.Error())
}
// always set the node mtime to the current time
mtime := time.Now()
// set the node mtime to the current time if no mtime was provided
if mtime.IsZero() {
mtime = time.Now()
}
err = os.Chtimes(target, mtime, mtime)
if err != nil {
return errtypes.InternalError("failed to update times:" + err.Error())

View File

@@ -19,7 +19,6 @@ import (
"github.com/opencloud-eu/reva/v2/pkg/storage/cache"
"github.com/opencloud-eu/reva/v2/pkg/storage/pkg/decomposedfs/metadata/prefixes"
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/filelocks"
)
var _metadataOffloadedAttr = prefixes.OcPrefix + "metadata_offloaded"
@@ -121,12 +120,11 @@ func (b HybridBackend) list(ctx context.Context, n MetadataNode, acquireLock boo
// listing xattrs failed, try again, either with lock or without
if acquireLock {
f, err := lockedfile.OpenFile(filePath+filelocks.LockFileSuffix, os.O_CREATE|os.O_WRONLY, 0600)
unlock, err := b.Lock(n)
if err != nil {
return nil, err
}
// Warning: do not remove the lockfile or we may lock the same file more than once, https://github.com/opencloud-eu/opencloud/issues/1793
defer f.Close()
defer func() { _ = unlock() }()
}
return xattr.List(filePath)
@@ -375,12 +373,11 @@ func (b HybridBackend) offloadMetadata(ctx context.Context, n MetadataNode) erro
func (b HybridBackend) Remove(ctx context.Context, n MetadataNode, key string, acquireLock bool) error {
path := n.InternalPath()
if acquireLock {
lockedFile, err := lockedfile.OpenFile(path+filelocks.LockFileSuffix, os.O_CREATE|os.O_WRONLY, 0600)
unlock, err := b.Lock(n)
if err != nil {
return err
}
// Warning: do not remove the lockfile or we may lock the same file more than once, https://github.com/opencloud-eu/opencloud/issues/1793
defer lockedFile.Close()
defer func() { _ = unlock() }()
}
if isOffloadingAttribute(key) {

View File

@@ -129,7 +129,7 @@ type Tree interface {
Delete(ctx context.Context, node *Node) (err error)
InitNewNode(ctx context.Context, n *Node, fsize uint64) (metadata.UnlockFunc, error)
RestoreRevision(ctx context.Context, source, target metadata.MetadataNode) (err error)
RestoreRevision(ctx context.Context, source, target metadata.MetadataNode, mtime time.Time) (err error)
WriteBlob(node *Node, source string) error
ReadBlob(node *Node) (io.ReadCloser, error)

View File

@@ -131,7 +131,7 @@ func (fs *Decomposedfs) RestoreRevision(ctx context.Context, ref *provider.Refer
// restore revision
restoredRevisionPath := fs.lu.InternalPath(spaceID, revisionKey)
if err := fs.tp.RestoreRevision(ctx, revisionNode, n); err != nil {
if err := fs.tp.RestoreRevision(ctx, revisionNode, n, time.Now()); err != nil {
return err
}

View File

@@ -330,7 +330,7 @@ func (tp *Tree) getRevisionNode(ctx context.Context, ref *provider.Reference, re
return n, nil
}
func (tp *Tree) RestoreRevision(ctx context.Context, sourceNode, targetNode metadata.MetadataNode) error {
func (tp *Tree) RestoreRevision(ctx context.Context, sourceNode, targetNode metadata.MetadataNode, mtime time.Time) error {
err := tp.lookup.CopyMetadata(ctx, sourceNode, targetNode, func(attributeName string, value []byte) (newValue []byte, copy bool) {
return value, strings.HasPrefix(attributeName, prefixes.ChecksumPrefix) ||
attributeName == prefixes.TypeAttr ||
@@ -340,10 +340,13 @@ func (tp *Tree) RestoreRevision(ctx context.Context, sourceNode, targetNode meta
if err != nil {
return errtypes.InternalError("failed to copy blob xattrs to old revision to node: " + err.Error())
}
// always set the node mtime to the current time
// set the node mtime to the current time if no mtime was provided
if mtime.IsZero() {
mtime = time.Now()
}
err = tp.lookup.MetadataBackend().SetMultiple(ctx, targetNode,
map[string][]byte{
prefixes.MTimeAttr: []byte(time.Now().UTC().Format(time.RFC3339Nano)),
prefixes.MTimeAttr: []byte(mtime.UTC().Format(time.RFC3339Nano)),
},
false)
if err != nil {

View File

@@ -292,7 +292,10 @@ func (session *DecomposedFsSession) Finalize(ctx context.Context) (err error) {
revisionNode := node.New(session.SpaceID(), session.NodeID(), "", "", session.Size(), session.ID(),
provider.ResourceType_RESOURCE_TYPE_FILE, session.SpaceOwner(), session.store.lu)
switch spaceRoot, err := session.store.lu.NodeFromSpaceID(ctx, session.SpaceID()); {
var (
spaceRoot *node.Node
)
switch spaceRoot, err = session.store.lu.NodeFromSpaceID(ctx, session.SpaceID()); {
case err != nil:
return fmt.Errorf("failed to get space root for space id %s: %v", session.SpaceID(), err)
case spaceRoot == nil:
@@ -310,6 +313,31 @@ func (session *DecomposedFsSession) Finalize(ctx context.Context) (err error) {
}
defer func() { _ = unlock() }()
isProcessing := revisionNode.IsProcessing(ctx)
var procssingID string
if isProcessing {
procssingID, _ = revisionNode.ProcessingID(ctx)
}
// another upload on this node is in progress or has finished since we started
if !isProcessing || procssingID != session.ID() {
versionID := revisionNode.ID + node.RevisionIDDelimiter + session.MTime().UTC().Format(time.RFC3339Nano)
revisionNode, err = node.ReadNode(ctx, session.store.lu, session.SpaceID(), versionID, false, spaceRoot, false)
if err != nil {
return fmt.Errorf("failed to read revision node %s for upload finalization: %w", versionID, err)
}
if !revisionNode.Exists {
return fmt.Errorf("revision node %s for upload finalization does not exist", versionID)
}
// lock this node as well, before writing the blob
revisionNodeUnlock, err := session.store.lu.MetadataBackend().Lock(revisionNode)
if err != nil {
return err
}
appctx.GetLogger(ctx).Debug().Str("new nodepath", revisionNode.InternalPath()).Msg("uploading to revision node, that was created for us by another upload")
defer func() { _ = revisionNodeUnlock() }()
}
// upload the data to the blobstore
_, subspan := tracer.Start(ctx, "WriteBlob")
err = session.store.tp.WriteBlob(revisionNode, session.binPath())
@@ -343,35 +371,45 @@ func (session *DecomposedFsSession) removeNode(ctx context.Context) {
// cleanup cleans up after the upload is finished
func (session *DecomposedFsSession) Cleanup(revertNodeMetadata, cleanBin, cleanInfo bool) {
ctx := session.Context(context.Background())
sublog := session.store.log.With().Str("cleanup sessionid", session.ID()).Bool("revertNodeMetadata", revertNodeMetadata).Bool("cleanBin", cleanBin).
Bool("cleanInfo", cleanInfo).Logger()
if revertNodeMetadata {
n, err := session.Node(ctx)
if err != nil {
appctx.GetLogger(ctx).Error().Err(err).Str("sessionid", session.ID()).Msg("reading node for session failed")
sublog.Error().Err(err).Msg("reading node for session failed")
} else {
if session.NodeExists() && session.info.MetaData["versionID"] != "" {
versionID := session.info.MetaData["versionID"]
revisionNode := node.NewBaseNode(n.SpaceID, versionID, session.store.lu)
sublog.Debug().Str("nodepath", n.InternalPath()).Str("versionID", versionID).Msg("restoring revision")
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")
}
if err := session.store.lu.CopyMetadata(ctx, revisionNode, n, func(attributeName string, value []byte) (newValue []byte, copy bool) {
return value, strings.HasPrefix(attributeName, prefixes.ChecksumPrefix) ||
attributeName == prefixes.TypeAttr ||
attributeName == prefixes.BlobIDAttr ||
attributeName == prefixes.BlobsizeAttr ||
attributeName == prefixes.MTimeAttr
}, true); err != nil {
appctx.GetLogger(ctx).Info().Str("version", versionID).Str("nodepath", n.InternalPath()).Err(err).Msg("renaming version node failed")
if !revisionNode.Exists {
sublog.Error().Str("versionID", versionID).Msg("revision node does not exist")
}
// restore the revision
mtime, err := revisionNode.GetMTime(ctx)
if err != nil {
sublog.Error().Err(err).Str("versionID", versionID).Msg("getting mtime of revision node failed")
mtime = time.Now()
}
if err := session.store.tp.RestoreRevision(ctx, revisionNode, n, mtime); err != nil {
sublog.Error().Err(err).Str("versionID", versionID).Msg("restoring revision node failed")
}
if err := os.RemoveAll(revisionNode.InternalPath()); err != nil {
appctx.GetLogger(ctx).Info().Str("version", versionID).Str("nodepath", n.InternalPath()).Err(err).Msg("error removing version")
sublog.Error().Err(err).Str("revisionpath", revisionNode.InternalPath()).Msg("removing restored revision file failed")
}
} else {
// if no other upload session is in progress (processing id != session id) or has finished (processing id == "")
latestSession, err := n.ProcessingID(ctx)
if err != nil {
appctx.GetLogger(ctx).Error().Err(err).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Str("uploadid", session.ID()).Msg("reading processingid for session failed")
sublog.Error().Err(err).Str("spaceid", n.SpaceID).Str("nodeid", n.ID).Str("uploadid", session.ID()).Msg("reading processingid for session failed")
}
if latestSession == session.ID() {
// actually delete the node

View File

@@ -1,5 +1,7 @@
package inotifywaitgo
import "log/slog"
type Settings struct {
// Directory to watch
Dir string
@@ -13,6 +15,8 @@ type Settings struct {
KillOthers bool
// verbose
Verbose bool
// Logger
Log *slog.Logger
}
type Options struct {

View File

@@ -3,8 +3,9 @@ package inotifywaitgo
import (
"bufio"
"encoding/csv"
"errors"
"fmt"
"log"
"log/slog"
"os"
"os/exec"
"strings"
@@ -14,13 +15,13 @@ import (
func WatchPath(s *Settings) {
// Check if inotifywait is installed
if ok, err := checkDependencies(); !ok || err != nil {
s.ErrorChan <- fmt.Errorf(NOT_INSTALLED)
s.ErrorChan <- errors.New(NOT_INSTALLED)
return
}
// Check if the directory exists
if _, err := os.Stat(s.Dir); os.IsNotExist(err) {
s.ErrorChan <- fmt.Errorf(DIR_NOT_EXISTS)
s.ErrorChan <- errors.New(DIR_NOT_EXISTS)
return
}
@@ -49,15 +50,20 @@ func WatchPath(s *Settings) {
return
}
logger := s.Log
if logger == nil {
logger = slog.New(slog.NewTextHandler(os.Stdout, nil))
}
// Read the output of inotifywait and split it into lines
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
line := scanner.Text()
log.Println(line)
logger.Debug(line)
parts, err := parseLine(line)
if err != nil || len(parts) < 2 {
s.ErrorChan <- fmt.Errorf(INVALID_OUTPUT)
s.ErrorChan <- errors.New(INVALID_OUTPUT)
continue
}
@@ -66,7 +72,7 @@ func WatchPath(s *Settings) {
if s.Verbose {
for _, eventStr := range eventStrs {
log.Printf("eventStr: <%s>, <%s>", eventStr, line)
logger.Debug("eventStr: <%s>, <%s>", eventStr, line)
}
}

38
vendor/github.com/samber/lo/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,38 @@
# Created by https://www.toptal.com/developers/gitignore/api/go
# Edit at https://www.toptal.com/developers/gitignore?templates=go
### Go ###
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
### Go Patch ###
/vendor/
/Godeps/
# End of https://www.toptal.com/developers/gitignore/api/go
cover.out
cover.html
.vscode
.idea/

8
vendor/github.com/samber/lo/Dockerfile generated vendored Normal file
View File

@@ -0,0 +1,8 @@
FROM golang:1.23.1
WORKDIR /go/src/github.com/samber/lo
COPY Makefile go.* ./
RUN make tools

21
vendor/github.com/samber/lo/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2022-2025 Samuel Berthe
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

42
vendor/github.com/samber/lo/Makefile generated vendored Normal file
View File

@@ -0,0 +1,42 @@
build:
go build -v ./...
test:
go test -race -v ./...
watch-test:
reflex -t 50ms -s -- sh -c 'gotest -race -v ./...'
bench:
go test -benchmem -count 3 -bench ./...
watch-bench:
reflex -t 50ms -s -- sh -c 'go test -benchmem -count 3 -bench ./...'
coverage:
go test -v -coverprofile=cover.out -covermode=atomic ./...
go tool cover -html=cover.out -o cover.html
# tools
tools:
go install github.com/cespare/reflex@latest
go install github.com/rakyll/gotest@latest
go install github.com/psampaz/go-mod-outdated@latest
go install github.com/jondot/goweight@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go get -t -u golang.org/x/tools/cmd/cover
go install github.com/sonatype-nexus-community/nancy@latest
go mod tidy
lint:
golangci-lint run --timeout 60s --max-same-issues 50 ./...
lint-fix:
golangci-lint run --timeout 60s --max-same-issues 50 --fix ./...
audit: tools
go list -json -m all | nancy sleuth
outdated: tools
go list -u -m -json all | go-mod-outdated -update -direct
weight: tools
goweight

4213
vendor/github.com/samber/lo/README.md generated vendored Normal file
View File

File diff suppressed because it is too large Load Diff

314
vendor/github.com/samber/lo/channel.go generated vendored Normal file
View File

@@ -0,0 +1,314 @@
package lo
import (
"context"
"sync"
"time"
"github.com/samber/lo/internal/rand"
)
type DispatchingStrategy[T any] func(msg T, index uint64, channels []<-chan T) int
// ChannelDispatcher distributes messages from input channels into N child channels.
// Close events are propagated to children.
// Underlying channels can have a fixed buffer capacity or be unbuffered when cap is 0.
func ChannelDispatcher[T any](stream <-chan T, count int, channelBufferCap int, strategy DispatchingStrategy[T]) []<-chan T {
children := createChannels[T](count, channelBufferCap)
roChildren := channelsToReadOnly(children)
go func() {
// propagate channel closing to children
defer closeChannels(children)
var i uint64 = 0
for {
msg, ok := <-stream
if !ok {
return
}
destination := strategy(msg, i, roChildren) % count
children[destination] <- msg
i++
}
}()
return roChildren
}
func createChannels[T any](count int, channelBufferCap int) []chan T {
children := make([]chan T, 0, count)
for i := 0; i < count; i++ {
children = append(children, make(chan T, channelBufferCap))
}
return children
}
func channelsToReadOnly[T any](children []chan T) []<-chan T {
roChildren := make([]<-chan T, 0, len(children))
for i := range children {
roChildren = append(roChildren, children[i])
}
return roChildren
}
func closeChannels[T any](children []chan T) {
for i := 0; i < len(children); i++ {
close(children[i])
}
}
func channelIsNotFull[T any](ch <-chan T) bool {
return cap(ch) == 0 || len(ch) < cap(ch)
}
// DispatchingStrategyRoundRobin distributes messages in a rotating sequential manner.
// If the channel capacity is exceeded, the next channel will be selected and so on.
func DispatchingStrategyRoundRobin[T any](msg T, index uint64, channels []<-chan T) int {
for {
i := int(index % uint64(len(channels)))
if channelIsNotFull(channels[i]) {
return i
}
index++
time.Sleep(10 * time.Microsecond) // prevent CPU from burning 🔥
}
}
// DispatchingStrategyRandom distributes messages in a random manner.
// If the channel capacity is exceeded, another random channel will be selected and so on.
func DispatchingStrategyRandom[T any](msg T, index uint64, channels []<-chan T) int {
for {
i := rand.IntN(len(channels))
if channelIsNotFull(channels[i]) {
return i
}
time.Sleep(10 * time.Microsecond) // prevent CPU from burning 🔥
}
}
// DispatchingStrategyWeightedRandom distributes messages in a weighted manner.
// If the channel capacity is exceeded, another random channel will be selected and so on.
func DispatchingStrategyWeightedRandom[T any](weights []int) DispatchingStrategy[T] {
seq := []int{}
for i := 0; i < len(weights); i++ {
for j := 0; j < weights[i]; j++ {
seq = append(seq, i)
}
}
return func(msg T, index uint64, channels []<-chan T) int {
for {
i := seq[rand.IntN(len(seq))]
if channelIsNotFull(channels[i]) {
return i
}
time.Sleep(10 * time.Microsecond) // prevent CPU from burning 🔥
}
}
}
// DispatchingStrategyFirst distributes messages in the first non-full channel.
// If the capacity of the first channel is exceeded, the second channel will be selected and so on.
func DispatchingStrategyFirst[T any](msg T, index uint64, channels []<-chan T) int {
for {
for i := range channels {
if channelIsNotFull(channels[i]) {
return i
}
}
time.Sleep(10 * time.Microsecond) // prevent CPU from burning 🔥
}
}
// DispatchingStrategyLeast distributes messages in the emptiest channel.
func DispatchingStrategyLeast[T any](msg T, index uint64, channels []<-chan T) int {
seq := Range(len(channels))
return MinBy(seq, func(item int, min int) bool {
return len(channels[item]) < len(channels[min])
})
}
// DispatchingStrategyMost distributes messages in the fullest channel.
// If the channel capacity is exceeded, the next channel will be selected and so on.
func DispatchingStrategyMost[T any](msg T, index uint64, channels []<-chan T) int {
seq := Range(len(channels))
return MaxBy(seq, func(item int, max int) bool {
return len(channels[item]) > len(channels[max]) && channelIsNotFull(channels[item])
})
}
// SliceToChannel returns a read-only channels of collection elements.
func SliceToChannel[T any](bufferSize int, collection []T) <-chan T {
ch := make(chan T, bufferSize)
go func() {
for i := range collection {
ch <- collection[i]
}
close(ch)
}()
return ch
}
// ChannelToSlice returns a slice built from channels items. Blocks until channel closes.
func ChannelToSlice[T any](ch <-chan T) []T {
collection := []T{}
for item := range ch {
collection = append(collection, item)
}
return collection
}
// Generator implements the generator design pattern.
func Generator[T any](bufferSize int, generator func(yield func(T))) <-chan T {
ch := make(chan T, bufferSize)
go func() {
// WARNING: infinite loop
generator(func(t T) {
ch <- t
})
close(ch)
}()
return ch
}
// Buffer creates a slice of n elements from a channel. Returns the slice and the slice length.
// @TODO: we should probably provide an helper that reuse the same buffer.
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++ {
item, ok := <-ch
if !ok {
return buffer, index, time.Since(now), false
}
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)
}
// BufferWithContext creates a slice of n elements from a channel, with context. Returns the slice and the slice length.
// @TODO: we should probably provide an helper that reuse the same buffer.
func BufferWithContext[T any](ctx context.Context, ch <-chan T, size int) (collection []T, length int, readTime time.Duration, ok bool) {
buffer := make([]T, 0, size)
now := time.Now()
for index := 0; index < size; index++ {
select {
case item, ok := <-ch:
if !ok {
return buffer, index, time.Since(now), false
}
buffer = append(buffer, item)
case <-ctx.Done():
return buffer, index, time.Since(now), true
}
}
return buffer, size, time.Since(now), true
}
// BufferWithTimeout creates a slice of n elements from a channel, with timeout. Returns the slice and the slice length.
func BufferWithTimeout[T any](ch <-chan T, size int, timeout time.Duration) (collection []T, length int, readTime time.Duration, ok bool) {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
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 has no priority. When all upstream channels reach EOF, downstream channel closes.
func FanIn[T any](channelBufferCap int, upstreams ...<-chan T) <-chan T {
out := make(chan T, channelBufferCap)
var wg sync.WaitGroup
// Start an output goroutine for each input channel in upstreams.
wg.Add(len(upstreams))
for i := range upstreams {
go func(index int) {
for n := range upstreams[index] {
out <- n
}
wg.Done()
}(i)
}
// Start a goroutine to close out once all the output goroutines are done.
go func() {
wg.Wait()
close(out)
}()
return out
}
// ChannelMerge collects messages from multiple input channels into a single buffered channel.
// Output messages has 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 reach EOF, downstream channels close. If any downstream
// channels is full, broadcasting is paused.
func FanOut[T any](count int, channelsBufferCap int, upstream <-chan T) []<-chan T {
downstreams := createChannels[T](count, channelsBufferCap)
go func() {
for msg := range upstream {
for i := range downstreams {
downstreams[i] <- msg
}
}
// Close out once all the output goroutines are done.
for i := range downstreams {
close(downstreams[i])
}
}()
return channelsToReadOnly(downstreams)
}

136
vendor/github.com/samber/lo/concurrency.go generated vendored Normal file
View File

@@ -0,0 +1,136 @@
package lo
import (
"context"
"sync"
"time"
)
type synchronize struct {
locker sync.Locker
}
func (s *synchronize) Do(cb func()) {
s.locker.Lock()
Try0(cb)
s.locker.Unlock()
}
// Synchronize wraps the underlying callback in a mutex. It receives an optional mutex.
func Synchronize(opt ...sync.Locker) *synchronize {
if len(opt) > 1 {
panic("unexpected arguments")
} else if len(opt) == 0 {
opt = append(opt, &sync.Mutex{})
}
return &synchronize{
locker: opt[0],
}
}
// Async executes a function in a goroutine and returns the result in a channel.
func Async[A any](f func() A) <-chan A {
ch := make(chan A, 1)
go func() {
ch <- f()
}()
return ch
}
// Async0 executes a function in a goroutine and returns a channel set once the function finishes.
func Async0(f func()) <-chan struct{} {
ch := make(chan struct{}, 1)
go func() {
f()
ch <- struct{}{}
}()
return ch
}
// Async1 is an alias to Async.
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.
func Async2[A, B any](f func() (A, B)) <-chan Tuple2[A, B] {
ch := make(chan Tuple2[A, B], 1)
go func() {
ch <- T2(f())
}()
return ch
}
// Async3 has the same behavior as Async, but returns the 3 results as a tuple inside the channel.
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() {
ch <- T3(f())
}()
return ch
}
// Async4 has the same behavior as Async, but returns the 4 results as a tuple inside the channel.
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() {
ch <- T4(f())
}()
return ch
}
// Async5 has the same behavior as Async, but returns the 5 results as a tuple inside the channel.
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() {
ch <- T5(f())
}()
return ch
}
// Async6 has the same behavior as Async, but returns the 6 results as a tuple inside the channel.
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() {
ch <- T6(f())
}()
return ch
}
// WaitFor runs periodically until a condition is validated.
func WaitFor(condition func(i int) bool, timeout time.Duration, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool) {
conditionWithContext := func(_ context.Context, currentIteration int) bool {
return condition(currentIteration)
}
return WaitForWithContext(context.Background(), conditionWithContext, timeout, heartbeatDelay)
}
// WaitForWithContext runs periodically until a condition is validated or context is canceled.
func WaitForWithContext(ctx context.Context, condition func(ctx context.Context, currentIteration int) bool, timeout time.Duration, heartbeatDelay time.Duration) (totalIterations int, elapsed time.Duration, conditionFound bool) {
start := time.Now()
if ctx.Err() != nil {
return totalIterations, time.Since(start), false
}
ctx, cleanCtx := context.WithTimeout(ctx, timeout)
ticker := time.NewTicker(heartbeatDelay)
defer func() {
cleanCtx()
ticker.Stop()
}()
for {
select {
case <-ctx.Done():
return totalIterations, time.Since(start), false
case <-ticker.C:
totalIterations++
if condition(ctx, totalIterations-1) {
return totalIterations, time.Since(start), true
}
}
}
}

151
vendor/github.com/samber/lo/condition.go generated vendored Normal file
View File

@@ -0,0 +1,151 @@
package lo
// Ternary is a 1 line if/else statement.
// Take care to avoid dereferencing potentially nil pointers in your A/B expressions, because they are both evaluated. See TernaryF to avoid this problem.
// Play: https://go.dev/play/p/t-D7WBL44h2
func Ternary[T any](condition bool, ifOutput T, elseOutput T) T {
if condition {
return ifOutput
}
return elseOutput
}
// TernaryF is a 1 line if/else statement whose options are functions
// Play: https://go.dev/play/p/AO4VW20JoqM
func TernaryF[T any](condition bool, ifFunc func() T, elseFunc func() T) T {
if condition {
return ifFunc()
}
return elseFunc()
}
type ifElse[T any] struct {
result T
done bool
}
// If.
// Play: https://go.dev/play/p/WSw3ApMxhyW
func If[T any](condition bool, result T) *ifElse[T] {
if condition {
return &ifElse[T]{result, true}
}
var t T
return &ifElse[T]{t, false}
}
// IfF.
// Play: https://go.dev/play/p/WSw3ApMxhyW
func IfF[T any](condition bool, resultF func() T) *ifElse[T] {
if condition {
return &ifElse[T]{resultF(), true}
}
var t T
return &ifElse[T]{t, false}
}
// ElseIf.
// Play: https://go.dev/play/p/WSw3ApMxhyW
func (i *ifElse[T]) ElseIf(condition bool, result T) *ifElse[T] {
if !i.done && condition {
i.result = result
i.done = true
}
return i
}
// ElseIfF.
// Play: https://go.dev/play/p/WSw3ApMxhyW
func (i *ifElse[T]) ElseIfF(condition bool, resultF func() T) *ifElse[T] {
if !i.done && condition {
i.result = resultF()
i.done = true
}
return i
}
// Else.
// Play: https://go.dev/play/p/WSw3ApMxhyW
func (i *ifElse[T]) Else(result T) T {
if i.done {
return i.result
}
return result
}
// ElseF.
// Play: https://go.dev/play/p/WSw3ApMxhyW
func (i *ifElse[T]) ElseF(resultF func() T) T {
if i.done {
return i.result
}
return resultF()
}
type switchCase[T comparable, R any] struct {
predicate T
result R
done bool
}
// Switch is a pure functional switch/case/default statement.
// Play: https://go.dev/play/p/TGbKUMAeRUd
func Switch[T comparable, R any](predicate T) *switchCase[T, R] {
var result R
return &switchCase[T, R]{
predicate,
result,
false,
}
}
// Case.
// Play: https://go.dev/play/p/TGbKUMAeRUd
func (s *switchCase[T, R]) Case(val T, result R) *switchCase[T, R] {
if !s.done && s.predicate == val {
s.result = result
s.done = true
}
return s
}
// CaseF.
// Play: https://go.dev/play/p/TGbKUMAeRUd
func (s *switchCase[T, R]) CaseF(val T, cb func() R) *switchCase[T, R] {
if !s.done && s.predicate == val {
s.result = cb()
s.done = true
}
return s
}
// Default.
// Play: https://go.dev/play/p/TGbKUMAeRUd
func (s *switchCase[T, R]) Default(result R) R {
if !s.done {
s.result = result
}
return s.result
}
// DefaultF.
// Play: https://go.dev/play/p/TGbKUMAeRUd
func (s *switchCase[T, R]) DefaultF(cb func() R) R {
if !s.done {
s.result = cb()
}
return s.result
}

6
vendor/github.com/samber/lo/constraints.go generated vendored Normal file
View File

@@ -0,0 +1,6 @@
package lo
// Clonable defines a constraint of types having Clone() T method.
type Clonable[T any] interface {
Clone() T
}

381
vendor/github.com/samber/lo/errors.go generated vendored Normal file
View File

@@ -0,0 +1,381 @@
package lo
import (
"errors"
"fmt"
"reflect"
)
const defaultAssertionFailureMessage = "assertion failed"
// Validate is a helper that creates an error when a condition is not met.
// Play: https://go.dev/play/p/vPyh51XpCBt
func Validate(ok bool, format string, args ...any) error {
if !ok {
return fmt.Errorf(format, args...)
}
return nil
}
func messageFromMsgAndArgs(msgAndArgs ...any) string {
if len(msgAndArgs) == 1 {
if msgAsStr, ok := msgAndArgs[0].(string); ok {
return msgAsStr
}
return fmt.Sprintf("%+v", msgAndArgs[0])
}
if len(msgAndArgs) > 1 {
return fmt.Sprintf(msgAndArgs[0].(string), msgAndArgs[1:]...)
}
return ""
}
// must panics if err is error or false.
func must(err any, messageArgs ...any) {
if err == nil {
return
}
switch e := err.(type) {
case bool:
if !e {
message := messageFromMsgAndArgs(messageArgs...)
if message == "" {
message = "not ok"
}
panic(message)
}
case error:
message := messageFromMsgAndArgs(messageArgs...)
if message != "" {
panic(message + ": " + e.Error())
} else {
panic(e.Error())
}
default:
panic("must: invalid err type '" + reflect.TypeOf(err).Name() + "', should either be a bool or an error")
}
}
// Must is a helper that wraps a call to a function returning a value and an error
// and panics if err is error or false.
// Play: https://go.dev/play/p/TMoWrRp3DyC
func Must[T any](val T, err any, messageArgs ...any) T {
must(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...)
}
// Must1 is an alias to Must
// Play: https://go.dev/play/p/TMoWrRp3DyC
func Must1[T any](val T, err any, messageArgs ...any) T {
return Must(val, err, messageArgs...)
}
// 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...)
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...)
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...)
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...)
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...)
return val1, val2, val3, val4, val5, val6
}
// Try calls the function and return false in case of error.
func Try(callback func() error) (ok bool) {
ok = true
defer func() {
if r := recover(); r != nil {
ok = false
}
}()
err := callback()
if err != nil {
ok = false
}
return
}
// Try0 has the same behavior as Try, but callback returns no variable.
// Play: https://go.dev/play/p/mTyyWUvn9u4
func Try0(callback func()) bool {
return Try(func() error {
callback()
return nil
})
}
// Try1 is an alias to Try.
// Play: https://go.dev/play/p/mTyyWUvn9u4
func Try1(callback func() error) bool {
return Try(callback)
}
// Try2 has the same behavior as Try, but callback returns 2 variables.
// Play: https://go.dev/play/p/mTyyWUvn9u4
func Try2[T any](callback func() (T, error)) bool {
return Try(func() error {
_, err := callback()
return err
})
}
// Try3 has the same behavior as Try, but callback returns 3 variables.
// Play: https://go.dev/play/p/mTyyWUvn9u4
func Try3[T, R any](callback func() (T, R, error)) bool {
return Try(func() error {
_, _, err := callback()
return err
})
}
// Try4 has the same behavior as Try, but callback returns 4 variables.
// Play: https://go.dev/play/p/mTyyWUvn9u4
func Try4[T, R, S any](callback func() (T, R, S, error)) bool {
return Try(func() error {
_, _, _, err := callback()
return err
})
}
// Try5 has the same behavior as Try, but callback returns 5 variables.
// Play: https://go.dev/play/p/mTyyWUvn9u4
func Try5[T, R, S, Q any](callback func() (T, R, S, Q, error)) bool {
return Try(func() error {
_, _, _, _, err := callback()
return err
})
}
// Try6 has the same behavior as Try, but callback returns 6 variables.
// Play: https://go.dev/play/p/mTyyWUvn9u4
func Try6[T, R, S, Q, U any](callback func() (T, R, S, Q, U, error)) bool {
return Try(func() error {
_, _, _, _, _, err := callback()
return err
})
}
// TryOr has the same behavior as Must, but returns a default value in case of error.
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
func TryOr[A any](callback func() (A, error), fallbackA A) (A, bool) {
return TryOr1(callback, fallbackA)
}
// TryOr1 has the same behavior as Must, but returns a default value in case of error.
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
func TryOr1[A any](callback func() (A, error), fallbackA A) (A, bool) {
ok := false
Try0(func() {
a, err := callback()
if err == nil {
fallbackA = a
ok = true
}
})
return fallbackA, ok
}
// TryOr2 has the same behavior as Must, but returns a default value in case of error.
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
func TryOr2[A, B any](callback func() (A, B, error), fallbackA A, fallbackB B) (A, B, bool) {
ok := false
Try0(func() {
a, b, err := callback()
if err == nil {
fallbackA = a
fallbackB = b
ok = true
}
})
return fallbackA, fallbackB, ok
}
// TryOr3 has the same behavior as Must, but returns a default value in case of error.
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
func TryOr3[A, B, C any](callback func() (A, B, C, error), fallbackA A, fallbackB B, fallbackC C) (A, B, C, bool) {
ok := false
Try0(func() {
a, b, c, err := callback()
if err == nil {
fallbackA = a
fallbackB = b
fallbackC = c
ok = true
}
})
return fallbackA, fallbackB, fallbackC, ok
}
// TryOr4 has the same behavior as Must, but returns a default value in case of error.
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
func TryOr4[A, B, C, D any](callback func() (A, B, C, D, error), fallbackA A, fallbackB B, fallbackC C, fallbackD D) (A, B, C, D, bool) {
ok := false
Try0(func() {
a, b, c, d, err := callback()
if err == nil {
fallbackA = a
fallbackB = b
fallbackC = c
fallbackD = d
ok = true
}
})
return fallbackA, fallbackB, fallbackC, fallbackD, ok
}
// TryOr5 has the same behavior as Must, but returns a default value in case of error.
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
func TryOr5[A, B, C, D, E any](callback func() (A, B, C, D, E, error), fallbackA A, fallbackB B, fallbackC C, fallbackD D, fallbackE E) (A, B, C, D, E, bool) {
ok := false
Try0(func() {
a, b, c, d, e, err := callback()
if err == nil {
fallbackA = a
fallbackB = b
fallbackC = c
fallbackD = d
fallbackE = e
ok = true
}
})
return fallbackA, fallbackB, fallbackC, fallbackD, fallbackE, ok
}
// TryOr6 has the same behavior as Must, but returns a default value in case of error.
// Play: https://go.dev/play/p/B4F7Wg2Zh9X
func TryOr6[A, B, C, D, E, F any](callback func() (A, B, C, D, E, F, error), fallbackA A, fallbackB B, fallbackC C, fallbackD D, fallbackE E, fallbackF F) (A, B, C, D, E, F, bool) {
ok := false
Try0(func() {
a, b, c, d, e, f, err := callback()
if err == nil {
fallbackA = a
fallbackB = b
fallbackC = c
fallbackD = d
fallbackE = e
fallbackF = f
ok = true
}
})
return fallbackA, fallbackB, fallbackC, fallbackD, fallbackE, fallbackF, ok
}
// TryWithErrorValue has the same behavior as Try, but also returns value passed to panic.
// Play: https://go.dev/play/p/Kc7afQIT2Fs
func TryWithErrorValue(callback func() error) (errorValue any, ok bool) {
ok = true
defer func() {
if r := recover(); r != nil {
ok = false
errorValue = r
}
}()
err := callback()
if err != nil {
ok = false
errorValue = err
}
return
}
// TryCatch has the same behavior as Try, but calls the catch function in case of error.
// Play: https://go.dev/play/p/PnOON-EqBiU
func TryCatch(callback func() error, catch func()) {
if !Try(callback) {
catch()
}
}
// TryCatchWithErrorValue has the same behavior as TryWithErrorValue, but calls the catch function in case of error.
// Play: https://go.dev/play/p/8Pc9gwX_GZO
func TryCatchWithErrorValue(callback func() error, catch func(any)) {
if err, ok := TryWithErrorValue(callback); !ok {
catch(err)
}
}
// ErrorsAs is a shortcut for errors.As(err, &&T).
// Play: https://go.dev/play/p/8wk5rH8UfrE
func ErrorsAs[T error](err error) (T, bool) {
var t T
ok := errors.As(err, &t)
return t, ok
}
// 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) {
if condition {
return
}
panicMessage := defaultAssertionFailureMessage
if len(message) > 0 {
panicMessage = fmt.Sprintf("%s: %s", defaultAssertionFailureMessage, message[0])
}
panic(panicMessage)
}
// 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) {
if condition {
return
}
panicMessage := fmt.Sprintf("%s: %s", defaultAssertionFailureMessage, fmt.Sprintf(format, args...))
panic(panicMessage)
}

651
vendor/github.com/samber/lo/find.go generated vendored Normal file
View File

@@ -0,0 +1,651 @@
package lo
import (
"fmt"
"time"
"github.com/samber/lo/internal/constraints"
"github.com/samber/lo/internal/rand"
)
// IndexOf returns the index at which the first occurrence of a value is found in an array or return -1
// if the value cannot be found.
func IndexOf[T comparable](collection []T, element T) int {
for i := range collection {
if collection[i] == element {
return i
}
}
return -1
}
// LastIndexOf returns the index at which the last occurrence of a value is found in an array or return -1
// if the value cannot be found.
func LastIndexOf[T comparable](collection []T, element T) int {
length := len(collection)
for i := length - 1; i >= 0; i-- {
if collection[i] == element {
return i
}
}
return -1
}
// Find search an element in a slice based on a predicate. It returns element and true if element was found.
func Find[T any](collection []T, predicate func(item T) bool) (T, bool) {
for i := range collection {
if predicate(collection[i]) {
return collection[i], true
}
}
var result T
return result, false
}
// FindIndexOf searches an element in a slice based on a predicate and returns the index and true.
// It returns -1 and false if the element is not found.
func FindIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bool) {
for i := range collection {
if predicate(collection[i]) {
return collection[i], i, true
}
}
var result T
return result, -1, false
}
// FindLastIndexOf searches last element in a slice based on a predicate and returns the index and true.
// It returns -1 and false if the element is not found.
func FindLastIndexOf[T any](collection []T, predicate func(item T) bool) (T, int, bool) {
length := len(collection)
for i := length - 1; i >= 0; i-- {
if predicate(collection[i]) {
return collection[i], i, true
}
}
var result T
return result, -1, false
}
// FindOrElse search an element in a slice based on a predicate. It returns the element if found or a given fallback value otherwise.
func FindOrElse[T any](collection []T, fallback T, predicate func(item T) bool) T {
for i := range collection {
if predicate(collection[i]) {
return collection[i]
}
}
return fallback
}
// FindKey returns the key of the first value matching.
func FindKey[K comparable, V comparable](object map[K]V, value V) (K, bool) {
for k := range object {
if object[k] == value {
return k, true
}
}
return Empty[K](), false
}
// FindKeyBy returns the key of the first element predicate returns truthy for.
func FindKeyBy[K comparable, V any](object map[K]V, predicate func(key K, value V) bool) (K, bool) {
for k := range object {
if predicate(k, object[k]) {
return k, true
}
}
return Empty[K](), false
}
// FindUniques returns a slice with all the unique elements of the collection.
// The order of result values is determined by the order they occur in the collection.
func FindUniques[T comparable, Slice ~[]T](collection Slice) Slice {
isDupl := make(map[T]bool, len(collection))
for i := range collection {
duplicated, ok := isDupl[collection[i]]
if !ok {
isDupl[collection[i]] = false
} else if !duplicated {
isDupl[collection[i]] = true
}
}
result := make(Slice, 0, len(collection)-len(isDupl))
for i := range collection {
if duplicated := isDupl[collection[i]]; !duplicated {
result = append(result, collection[i])
}
}
return result
}
// FindUniquesBy returns a slice with all the unique elements of the collection.
// The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is
// invoked for each element in array to generate the criterion by which uniqueness is computed.
func FindUniquesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice {
isDupl := make(map[U]bool, len(collection))
for i := range collection {
key := iteratee(collection[i])
duplicated, ok := isDupl[key]
if !ok {
isDupl[key] = false
} else if !duplicated {
isDupl[key] = true
}
}
result := make(Slice, 0, len(collection)-len(isDupl))
for i := range collection {
key := iteratee(collection[i])
if duplicated := isDupl[key]; !duplicated {
result = append(result, collection[i])
}
}
return result
}
// FindDuplicates returns a slice with the first occurrence of each duplicated elements of the collection.
// The order of result values is determined by the order they occur in the collection.
func FindDuplicates[T comparable, Slice ~[]T](collection Slice) Slice {
isDupl := make(map[T]bool, len(collection))
for i := range collection {
duplicated, ok := isDupl[collection[i]]
if !ok {
isDupl[collection[i]] = false
} else if !duplicated {
isDupl[collection[i]] = true
}
}
result := make(Slice, 0, len(collection)-len(isDupl))
for i := range collection {
if duplicated := isDupl[collection[i]]; duplicated {
result = append(result, collection[i])
isDupl[collection[i]] = false
}
}
return result
}
// FindDuplicatesBy returns a slice with the first occurrence of each duplicated elements of the collection.
// The order of result values is determined by the order they occur in the array. It accepts `iteratee` which is
// invoked for each element in array to generate the criterion by which uniqueness is computed.
func FindDuplicatesBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice {
isDupl := make(map[U]bool, len(collection))
for i := range collection {
key := iteratee(collection[i])
duplicated, ok := isDupl[key]
if !ok {
isDupl[key] = false
} else if !duplicated {
isDupl[key] = true
}
}
result := make(Slice, 0, len(collection)-len(isDupl))
for i := range collection {
key := iteratee(collection[i])
if duplicated := isDupl[key]; duplicated {
result = append(result, collection[i])
isDupl[key] = false
}
}
return result
}
// Min search the minimum value of a collection.
// Returns zero value when the collection is empty.
func Min[T constraints.Ordered](collection []T) T {
var min T
if len(collection) == 0 {
return min
}
min = collection[0]
for i := 1; i < len(collection); i++ {
item := collection[i]
if item < min {
min = item
}
}
return min
}
// MinIndex search the minimum value of a collection and the index of the minimum value.
// Returns (zero value, -1) when the collection is empty.
func MinIndex[T constraints.Ordered](collection []T) (T, int) {
var (
min T
index int
)
if len(collection) == 0 {
return min, -1
}
min = collection[0]
for i := 1; i < len(collection); i++ {
item := collection[i]
if item < min {
min = item
index = i
}
}
return min, index
}
// 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 T, b T) bool) T {
var min T
if len(collection) == 0 {
return min
}
min = collection[0]
for i := 1; i < len(collection); i++ {
item := collection[i]
if comparison(item, min) {
min = item
}
}
return min
}
// 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 T, b T) bool) (T, int) {
var (
min T
index int
)
if len(collection) == 0 {
return min, -1
}
min = collection[0]
for i := 1; i < len(collection); i++ {
item := collection[i]
if comparison(item, min) {
min = item
index = i
}
}
return min, index
}
// Earliest search the minimum time.Time of a collection.
// Returns zero value when the collection is empty.
func Earliest(times ...time.Time) time.Time {
var min time.Time
if len(times) == 0 {
return min
}
min = times[0]
for i := 1; i < len(times); i++ {
item := times[i]
if item.Before(min) {
min = item
}
}
return min
}
// EarliestBy search the minimum time.Time of a collection using the given iteratee function.
// Returns zero value when the collection is empty.
func EarliestBy[T any](collection []T, iteratee func(item T) time.Time) T {
var earliest T
if len(collection) == 0 {
return earliest
}
earliest = collection[0]
earliestTime := iteratee(collection[0])
for i := 1; i < len(collection); i++ {
itemTime := iteratee(collection[i])
if itemTime.Before(earliestTime) {
earliest = collection[i]
earliestTime = itemTime
}
}
return earliest
}
// Max searches the maximum value of a collection.
// Returns zero value when the collection is empty.
func Max[T constraints.Ordered](collection []T) T {
var max T
if len(collection) == 0 {
return max
}
max = collection[0]
for i := 1; i < len(collection); i++ {
item := collection[i]
if item > max {
max = item
}
}
return max
}
// MaxIndex searches the maximum value of a collection and the index of the maximum value.
// Returns (zero value, -1) when the collection is empty.
func MaxIndex[T constraints.Ordered](collection []T) (T, int) {
var (
max T
index int
)
if len(collection) == 0 {
return max, -1
}
max = collection[0]
for i := 1; i < len(collection); i++ {
item := collection[i]
if item > max {
max = item
index = i
}
}
return max, index
}
// 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 T, b T) bool) T {
var max T
if len(collection) == 0 {
return max
}
max = collection[0]
for i := 1; i < len(collection); i++ {
item := collection[i]
if comparison(item, max) {
max = item
}
}
return max
}
// 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 T, b T) bool) (T, int) {
var (
max T
index int
)
if len(collection) == 0 {
return max, -1
}
max = collection[0]
for i := 1; i < len(collection); i++ {
item := collection[i]
if comparison(item, max) {
max = item
index = i
}
}
return max, index
}
// Latest search the maximum time.Time of a collection.
// Returns zero value when the collection is empty.
func Latest(times ...time.Time) time.Time {
var max time.Time
if len(times) == 0 {
return max
}
max = times[0]
for i := 1; i < len(times); i++ {
item := times[i]
if item.After(max) {
max = item
}
}
return max
}
// LatestBy search the maximum time.Time of a collection using the given iteratee function.
// Returns zero value when the collection is empty.
func LatestBy[T any](collection []T, iteratee func(item T) time.Time) T {
var latest T
if len(collection) == 0 {
return latest
}
latest = collection[0]
latestTime := iteratee(collection[0])
for i := 1; i < len(collection); i++ {
itemTime := iteratee(collection[i])
if itemTime.After(latestTime) {
latest = collection[i]
latestTime = itemTime
}
}
return latest
}
// First returns the first element of a collection and check for availability of the first element.
func First[T any](collection []T) (T, bool) {
length := len(collection)
if length == 0 {
var t T
return t, false
}
return collection[0], true
}
// FirstOrEmpty returns the first element of a collection or zero value if empty.
func FirstOrEmpty[T any](collection []T) T {
i, _ := First(collection)
return i
}
// FirstOr returns the first element of a collection or the fallback value if empty.
func FirstOr[T any](collection []T, fallback T) T {
i, ok := First(collection)
if !ok {
return fallback
}
return i
}
// Last returns the last element of a collection or error if empty.
func Last[T any](collection []T) (T, bool) {
length := len(collection)
if length == 0 {
var t T
return t, false
}
return collection[length-1], true
}
// LastOrEmpty returns the last element of a collection or zero value if empty.
func LastOrEmpty[T any](collection []T) T {
i, _ := Last(collection)
return i
}
// LastOr returns the last element of a collection or the fallback value if empty.
func LastOr[T any](collection []T, fallback T) T {
i, ok := Last(collection)
if !ok {
return fallback
}
return i
}
// Nth returns the element at index `nth` of collection. If `nth` is negative, the nth element
// from the end is returned. An error is returned when nth is out of slice bounds.
func Nth[T any, N constraints.Integer](collection []T, nth N) (T, error) {
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)
}
if n >= 0 {
return collection[n], nil
}
return collection[l+n], nil
}
// NthOr returns the element at index `nth` of collection.
// If `nth` is negative, it returns the nth element from the end.
// If `nth` is out of slice bounds, it returns the fallback value instead of an error.
func NthOr[T any, N constraints.Integer](collection []T, nth N, fallback T) T {
value, err := Nth(collection, nth)
if err != nil {
return fallback
}
return value
}
// NthOrEmpty returns the element at index `nth` of collection.
// If `nth` is negative, it returns the nth element from the end.
// If `nth` is out of slice bounds, it returns the zero value (empty value) for that type.
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
}
return value
}
// randomIntGenerator is a function that should return a random integer in the range [0, n)
// where n is the parameter passed to the randomIntGenerator.
type randomIntGenerator func(n int) int
// Sample returns a random item from collection.
func Sample[T any](collection []T) T {
result := SampleBy(collection, rand.IntN)
return result
}
// SampleBy returns a random item from collection, using randomIntGenerator as the random index generator.
func SampleBy[T any](collection []T, randomIntGenerator randomIntGenerator) T {
size := len(collection)
if size == 0 {
return Empty[T]()
}
return collection[randomIntGenerator(size)]
}
// Samples returns N random unique items from collection.
func Samples[T any, Slice ~[]T](collection Slice, count int) Slice {
results := SamplesBy(collection, count, rand.IntN)
return results
}
// SamplesBy returns N random unique items from collection, using randomIntGenerator as the random index generator.
func SamplesBy[T any, Slice ~[]T](collection Slice, count int, randomIntGenerator randomIntGenerator) Slice {
size := len(collection)
copy := append(Slice{}, collection...)
results := Slice{}
for i := 0; i < size && i < count; i++ {
copyLength := size - i
index := randomIntGenerator(size - i)
results = append(results, copy[index])
// Removes element.
// It is faster to swap with last element and remove it.
copy[index] = copy[copyLength-1]
copy = copy[:copyLength-1]
}
return results
}

41
vendor/github.com/samber/lo/func.go generated vendored Normal file
View File

@@ -0,0 +1,41 @@
package lo
// Partial returns new function that, when called, has its first argument set to the provided value.
func Partial[T1, T2, R any](f func(a T1, b T2) R, arg1 T1) func(T2) R {
return func(t2 T2) R {
return f(arg1, t2)
}
}
// Partial1 returns new function that, when called, has its first argument set to the provided value.
func Partial1[T1, T2, R any](f func(T1, T2) R, arg1 T1) func(T2) R {
return Partial(f, arg1)
}
// Partial2 returns new function that, when called, has its first argument set to the provided value.
func Partial2[T1, T2, T3, R any](f func(T1, T2, T3) R, arg1 T1) func(T2, T3) R {
return func(t2 T2, t3 T3) R {
return f(arg1, t2, t3)
}
}
// Partial3 returns new function that, when called, has its first argument set to the provided value.
func Partial3[T1, T2, T3, T4, R any](f func(T1, T2, T3, T4) R, arg1 T1) func(T2, T3, T4) R {
return func(t2 T2, t3 T3, t4 T4) R {
return f(arg1, t2, t3, t4)
}
}
// Partial4 returns new function that, when called, has its first argument set to the provided value.
func Partial4[T1, T2, T3, T4, T5, R any](f func(T1, T2, T3, T4, T5) R, arg1 T1) func(T2, T3, T4, T5) R {
return func(t2 T2, t3 T3, t4 T4, t5 T5) R {
return f(arg1, t2, t3, t4, t5)
}
}
// Partial5 returns new function that, when called, has its first argument set to the provided value
func Partial5[T1, T2, T3, T4, T5, T6, R any](f func(T1, T2, T3, T4, T5, T6) R, arg1 T1) func(T2, T3, T4, T5, T6) R {
return func(t2 T2, t3 T3, t4 T4, t5 T5, t6 T6) R {
return f(arg1, t2, t3, t4, t5, t6)
}
}

View File

@@ -0,0 +1,42 @@
// Copyright 2021 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package constraints defines a set of useful constraints to be used
// with type parameters.
package constraints
// Signed is a constraint that permits any signed integer type.
// If future releases of Go add new predeclared signed integer types,
// this constraint will be modified to include them.
type Signed interface {
~int | ~int8 | ~int16 | ~int32 | ~int64
}
// Unsigned is a constraint that permits any unsigned integer type.
// If future releases of Go add new predeclared unsigned integer types,
// this constraint will be modified to include them.
type Unsigned interface {
~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr
}
// Integer is a constraint that permits any integer type.
// If future releases of Go add new predeclared integer types,
// this constraint will be modified to include them.
type Integer interface {
Signed | Unsigned
}
// Float is a constraint that permits any floating-point type.
// If future releases of Go add new predeclared floating-point types,
// this constraint will be modified to include them.
type Float interface {
~float32 | ~float64
}
// Complex is a constraint that permits any complex numeric type.
// If future releases of Go add new predeclared complex numeric types,
// this constraint will be modified to include them.
type Complex interface {
~complex64 | ~complex128
}

View File

@@ -0,0 +1,11 @@
//go:build !go1.21
package constraints
// Ordered is a constraint that permits any ordered type: any type
// that supports the operators < <= >= >.
// If future releases of Go add new ordered types,
// this constraint will be modified to include them.
type Ordered interface {
Integer | Float | ~string
}

View File

@@ -0,0 +1,9 @@
//go:build go1.21
package constraints
import (
"cmp"
)
type Ordered = cmp.Ordered

View File

@@ -0,0 +1,26 @@
//go:build !go1.22
package rand
import "math/rand"
func Shuffle(n int, swap func(i, j int)) {
rand.Shuffle(n, swap)
}
func IntN(n int) int {
// bearer:disable go_gosec_crypto_weak_random
return rand.Intn(n)
}
func Int64() int64 {
// bearer:disable go_gosec_crypto_weak_random
n := rand.Int63()
// bearer:disable go_gosec_crypto_weak_random
if rand.Intn(2) == 0 {
return -n
}
return n
}

View File

@@ -0,0 +1,17 @@
//go:build go1.22
package rand
import "math/rand/v2"
func Shuffle(n int, swap func(i, j int)) {
rand.Shuffle(n, swap)
}
func IntN(n int) int {
return rand.IntN(n)
}
func Int64() int64 {
return rand.Int64()
}

265
vendor/github.com/samber/lo/intersect.go generated vendored Normal file
View File

@@ -0,0 +1,265 @@
package lo
// Contains returns true if an element is present in a collection.
func Contains[T comparable](collection []T, element T) bool {
for i := range collection {
if collection[i] == element {
return true
}
}
return false
}
// ContainsBy returns true if predicate function return true.
func ContainsBy[T any](collection []T, predicate func(item T) bool) bool {
for i := range collection {
if predicate(collection[i]) {
return true
}
}
return false
}
// Every returns true if all elements of a subset are contained into a collection or if the subset is empty.
func Every[T comparable](collection []T, subset []T) bool {
for i := range subset {
if !Contains(collection, subset[i]) {
return false
}
}
return true
}
// EveryBy returns true if the predicate returns true for all elements in the collection or if the collection is empty.
func EveryBy[T any](collection []T, predicate func(item T) bool) bool {
for i := range collection {
if !predicate(collection[i]) {
return false
}
}
return true
}
// Some returns true if at least 1 element of a subset is contained into a collection.
// If the subset is empty Some returns false.
func Some[T comparable](collection []T, subset []T) bool {
for i := range subset {
if Contains(collection, subset[i]) {
return true
}
}
return false
}
// SomeBy returns true if the predicate returns true for any of the elements in the collection.
// If the collection is empty SomeBy returns false.
func SomeBy[T any](collection []T, predicate func(item T) bool) bool {
for i := range collection {
if predicate(collection[i]) {
return true
}
}
return false
}
// None returns true if no element of a subset are contained into a collection or if the subset is empty.
func None[T comparable](collection []T, subset []T) bool {
for i := range subset {
if Contains(collection, subset[i]) {
return false
}
}
return true
}
// NoneBy returns true if the predicate returns true for none of the elements in the collection or if the collection is empty.
func NoneBy[T any](collection []T, predicate func(item T) bool) bool {
for i := range collection {
if predicate(collection[i]) {
return false
}
}
return true
}
// Intersect returns the intersection between two collections.
func Intersect[T comparable, Slice ~[]T](list1 Slice, list2 Slice) Slice {
result := Slice{}
seen := map[T]struct{}{}
for i := range list1 {
seen[list1[i]] = struct{}{}
}
for i := range list2 {
if _, ok := seen[list2[i]]; ok {
result = append(result, list2[i])
}
}
return result
}
// Difference returns the difference between two collections.
// The first value is the collection of element absent of list2.
// The second value is the collection of element absent of list1.
func Difference[T comparable, Slice ~[]T](list1 Slice, 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{}{}
}
for i := range list1 {
if _, ok := seenRight[list1[i]]; !ok {
left = append(left, list1[i])
}
}
for i := range list2 {
if _, ok := seenLeft[list2[i]]; !ok {
right = append(right, list2[i])
}
}
return left, right
}
// Union returns all distinct elements from given collections.
// result returns will not change the order of elements relatively.
func Union[T comparable, Slice ~[]T](lists ...Slice) Slice {
var capLen int
for _, list := range lists {
capLen += len(list)
}
result := make(Slice, 0, capLen)
seen := make(map[T]struct{}, capLen)
for i := range lists {
for j := range lists[i] {
if _, ok := seen[lists[i][j]]; !ok {
seen[lists[i][j]] = struct{}{}
result = append(result, lists[i][j])
}
}
}
return result
}
// Without returns slice excluding all given values.
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{}{}
}
result := make(Slice, 0, len(collection))
for i := range collection {
if _, ok := excludeMap[collection[i]]; !ok {
result = append(result, collection[i])
}
}
return result
}
// WithoutBy filters a slice by excluding elements whose extracted keys match any in the exclude list.
// It returns a new slice containing only the elements whose keys are not in the exclude list.
func WithoutBy[T any, K comparable](collection []T, iteratee func(item T) K, exclude ...K) []T {
excludeMap := make(map[K]struct{}, len(exclude))
for _, e := range exclude {
excludeMap[e] = struct{}{}
}
result := make([]T, 0, len(collection))
for _, item := range collection {
if _, ok := excludeMap[iteratee(item)]; !ok {
result = append(result, item)
}
}
return result
}
// WithoutEmpty returns slice excluding zero values.
//
// Deprecated: Use lo.Compact instead.
func WithoutEmpty[T comparable, Slice ~[]T](collection Slice) Slice {
return Compact(collection)
}
// WithoutNth returns slice excluding nth value.
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{}{}
}
}
result := make(Slice, 0, len(collection))
for i := range collection {
if _, ok := toRemove[i]; !ok {
result = append(result, collection[i])
}
}
return result
}
// ElementsMatch returns true if lists contain the same set of elements (including empty set).
// If there are duplicate elements, the number of appearances of each of them in both lists should match.
// The order of elements is not checked.
func ElementsMatch[T comparable, Slice ~[]T](list1 Slice, list2 Slice) bool {
return ElementsMatchBy(list1, list2, func(item T) T { return item })
}
// ElementsMatchBy returns true if lists contain the same set of elements' keys (including empty set).
// If there are duplicate keys, the number of appearances of each of them in both lists should match.
// The order of elements is not checked.
func ElementsMatchBy[T any, K comparable](list1 []T, list2 []T, iteratee func(item T) K) bool {
if len(list1) != len(list2) {
return false
}
if len(list1) == 0 {
return true
}
counters := make(map[K]int, len(list1))
for _, el := range list1 {
counters[iteratee(el)]++
}
for _, el := range list2 {
counters[iteratee(el)]--
}
for _, count := range counters {
if count != 0 {
return false
}
}
return true
}

344
vendor/github.com/samber/lo/map.go generated vendored Normal file
View File

@@ -0,0 +1,344 @@
package lo
// Keys creates an array of the map keys.
// Play: https://go.dev/play/p/Uu11fHASqrU
func Keys[K comparable, V any](in ...map[K]V) []K {
size := 0
for i := range in {
size += len(in[i])
}
result := make([]K, 0, size)
for i := range in {
for k := range in[i] {
result = append(result, k)
}
}
return result
}
// UniqKeys creates an array of unique keys in the map.
// Play: https://go.dev/play/p/TPKAb6ILdHk
func UniqKeys[K comparable, V any](in ...map[K]V) []K {
size := 0
for i := range in {
size += len(in[i])
}
seen := make(map[K]struct{}, size)
result := make([]K, 0)
for i := range in {
for k := range in[i] {
if _, exists := seen[k]; exists {
continue
}
seen[k] = struct{}{}
result = append(result, k)
}
}
return result
}
// HasKey returns whether the given key exists.
// Play: https://go.dev/play/p/aVwubIvECqS
func HasKey[K comparable, V any](in map[K]V, key K) bool {
_, ok := in[key]
return ok
}
// Values creates an array of the map values.
// Play: https://go.dev/play/p/nnRTQkzQfF6
func Values[K comparable, V any](in ...map[K]V) []V {
size := 0
for i := range in {
size += len(in[i])
}
result := make([]V, 0, size)
for i := range in {
for k := range in[i] {
result = append(result, in[i][k])
}
}
return result
}
// UniqValues creates an array of unique values in the map.
// Play: https://go.dev/play/p/nf6bXMh7rM3
func UniqValues[K comparable, V comparable](in ...map[K]V) []V {
size := 0
for i := range in {
size += len(in[i])
}
seen := make(map[V]struct{}, size)
result := make([]V, 0)
for i := range in {
for k := range in[i] {
val := in[i][k]
if _, exists := seen[val]; exists {
continue
}
seen[val] = struct{}{}
result = append(result, val)
}
}
return result
}
// ValueOr returns the value of the given key or the fallback value if the key is not present.
// Play: https://go.dev/play/p/bAq9mHErB4V
func ValueOr[K comparable, V any](in map[K]V, key K, fallback V) V {
if v, ok := in[key]; ok {
return v
}
return fallback
}
// PickBy returns same map type filtered by given predicate.
// Play: https://go.dev/play/p/kdg8GR_QMmf
func PickBy[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, value V) bool) Map {
r := Map{}
for k := range in {
if predicate(k, in[k]) {
r[k] = in[k]
}
}
return r
}
// 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 {
r := Map{}
for i := range keys {
if v, ok := in[keys[i]]; ok {
r[keys[i]] = v
}
}
return r
}
// PickByValues returns same map type filtered by given values.
// Play: https://go.dev/play/p/1zdzSvbfsJc
func PickByValues[K comparable, V comparable, Map ~map[K]V](in Map, values []V) Map {
r := Map{}
for k := range in {
if Contains(values, in[k]) {
r[k] = in[k]
}
}
return r
}
// OmitBy returns same map type filtered by given predicate.
// Play: https://go.dev/play/p/EtBsR43bdsd
func OmitBy[K comparable, V any, Map ~map[K]V](in Map, predicate func(key K, value V) bool) Map {
r := Map{}
for k := range in {
if !predicate(k, in[k]) {
r[k] = in[k]
}
}
return r
}
// 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 {
r := Map{}
for k := range in {
r[k] = in[k]
}
for i := range keys {
delete(r, keys[i])
}
return r
}
// OmitByValues returns same map type filtered by given values.
// Play: https://go.dev/play/p/9UYZi-hrs8j
func OmitByValues[K comparable, V comparable, Map ~map[K]V](in Map, values []V) Map {
r := Map{}
for k := range in {
if !Contains(values, in[k]) {
r[k] = in[k]
}
}
return r
}
// Entries transforms a map into array of key/value pairs.
// Play:
func Entries[K comparable, V any](in map[K]V) []Entry[K, V] {
entries := make([]Entry[K, V], 0, len(in))
for k := range in {
entries = append(entries, Entry[K, V]{
Key: k,
Value: in[k],
})
}
return entries
}
// ToPairs transforms a map into array of key/value pairs.
// Alias of Entries().
// Play: https://go.dev/play/p/3Dhgx46gawJ
func ToPairs[K comparable, V any](in map[K]V) []Entry[K, V] {
return Entries(in)
}
// FromEntries transforms an array of key/value pairs into a map.
// Play: https://go.dev/play/p/oIr5KHFGCEN
func FromEntries[K comparable, V any](entries []Entry[K, V]) map[K]V {
out := make(map[K]V, len(entries))
for i := range entries {
out[entries[i].Key] = entries[i].Value
}
return out
}
// FromPairs transforms an array of key/value pairs into a map.
// Alias of FromEntries().
// Play: https://go.dev/play/p/oIr5KHFGCEN
func FromPairs[K comparable, V any](entries []Entry[K, V]) map[K]V {
return FromEntries(entries)
}
// Invert creates a map composed of the inverted keys and values. If map
// contains duplicate values, subsequent values overwrite property assignments
// of previous values.
// Play: https://go.dev/play/p/rFQ4rak6iA1
func Invert[K comparable, V comparable](in map[K]V) map[V]K {
out := make(map[V]K, len(in))
for k := range in {
out[in[k]] = k
}
return out
}
// Assign merges multiple maps from left to right.
// Play: https://go.dev/play/p/VhwfJOyxf5o
func Assign[K comparable, V any, Map ~map[K]V](maps ...Map) Map {
count := 0
for i := range maps {
count += len(maps[i])
}
out := make(Map, count)
for i := range maps {
for k := range maps[i] {
out[k] = maps[i][k]
}
}
return out
}
// ChunkEntries splits a map into an array of elements in groups of a length equal to its size. If the map cannot be split evenly,
// the final chunk will contain the remaining elements.
// Play: https://go.dev/play/p/X_YQL6mmoD-
func ChunkEntries[K comparable, V any](m map[K]V, size int) []map[K]V {
if size <= 0 {
panic("The chunk size must be greater than 0")
}
count := len(m)
if count == 0 {
return []map[K]V{}
}
chunksNum := count / size
if count%size != 0 {
chunksNum += 1
}
result := make([]map[K]V, 0, chunksNum)
for k, v := range m {
if len(result) == 0 || len(result[len(result)-1]) == size {
result = append(result, make(map[K]V, size))
}
result[len(result)-1][k] = v
}
return result
}
// MapKeys manipulates a map keys and transforms it to a map of another type.
// Play: https://go.dev/play/p/9_4WPIqOetJ
func MapKeys[K comparable, V any, R comparable](in map[K]V, iteratee func(value V, key K) R) map[R]V {
result := make(map[R]V, len(in))
for k := range in {
result[iteratee(in[k], k)] = in[k]
}
return result
}
// MapValues manipulates a map values and transforms it to a map of another type.
// Play: https://go.dev/play/p/T_8xAfvcf0W
func MapValues[K comparable, V any, R any](in map[K]V, iteratee func(value V, key K) R) map[K]R {
result := make(map[K]R, len(in))
for k := range in {
result[k] = iteratee(in[k], k)
}
return result
}
// MapEntries manipulates a 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 {
result := make(map[K2]V2, len(in))
for k1 := range in {
k2, v2 := iteratee(k1, in[k1])
result[k2] = v2
}
return result
}
// MapToSlice transforms a map into a slice based on specific iteratee
// Play: https://go.dev/play/p/ZuiCZpDt6LD
func MapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(key K, value V) R) []R {
result := make([]R, 0, len(in))
for k := range in {
result = append(result, iteratee(k, in[k]))
}
return result
}
// FilterMapToSlice transforms a map into a slice based on specific 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.
// 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 FilterMapToSlice[K comparable, V any, R any](in map[K]V, iteratee func(key K, value V) (R, bool)) []R {
result := make([]R, 0, len(in))
for k := range in {
if v, ok := iteratee(k, in[k]); ok {
result = append(result, v)
}
}
return result
}

142
vendor/github.com/samber/lo/math.go generated vendored Normal file
View File

@@ -0,0 +1,142 @@
package lo
import (
"github.com/samber/lo/internal/constraints"
)
// Range creates an array 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)
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
}
return result
}
// RangeFrom creates an array 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)
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
}
return result
}
// RangeWithSteps creates an array of numbers (positive and/or negative) progressing from start up to, but not including end.
// step set to zero will return empty array.
// 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
}
if start < end {
if step < 0 {
return result
}
for i := start; i < end; i += step {
result = append(result, i)
}
return result
}
if step > 0 {
return result
}
for i := start; i > end; i += step {
result = append(result, i)
}
return result
}
// Clamp clamps number within the inclusive lower and upper bounds.
// Play: https://go.dev/play/p/RU4lJNC2hlI
func Clamp[T constraints.Ordered](value T, min T, max T) T {
if value < min {
return min
} else if value > max {
return max
}
return value
}
// Sum sums the values in a collection. If collection is empty 0 is returned.
// Play: https://go.dev/play/p/upfeJVqs4Bt
func Sum[T constraints.Float | constraints.Integer | constraints.Complex](collection []T) T {
var sum T = 0
for i := range collection {
sum += collection[i]
}
return sum
}
// SumBy summarizes the values in a collection using the given return value from the iteration function. If collection is empty 0 is returned.
// Play: https://go.dev/play/p/Dz_a_7jN_ca
func SumBy[T any, R constraints.Float | constraints.Integer | constraints.Complex](collection []T, iteratee func(item T) R) R {
var sum R = 0
for i := range collection {
sum = sum + iteratee(collection[i])
}
return sum
}
// Product gets the product of the values in a collection. If collection is empty 0 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]
}
return product
}
// ProductBy summarizes the values in a collection using the given return value from the iteration function. If collection is empty 0 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 = product * iteratee(collection[i])
}
return product
}
// Mean calculates the mean of a collection of numbers.
func Mean[T constraints.Float | constraints.Integer](collection []T) T {
var length = T(len(collection))
if length == 0 {
return 0
}
var sum = Sum(collection)
return sum / length
}
// MeanBy calculates the mean of a collection of numbers using the given return value from the iteration function.
func MeanBy[T any, R constraints.Float | constraints.Integer](collection []T, iteratee func(item T) R) R {
var length = R(len(collection))
if length == 0 {
return 0
}
var sum = SumBy(collection, iteratee)
return sum / length
}

71
vendor/github.com/samber/lo/mutable/slice.go generated vendored Normal file
View File

@@ -0,0 +1,71 @@
package mutable
import "github.com/samber/lo/internal/rand"
// Filter is a generic function that modifies the input slice in-place to contain only the elements
// that satisfy the provided predicate function. The predicate function takes an element of the slice and its index,
// and should return true for elements that should be kept and false for elements that should be removed.
// The function returns the modified slice, which may be shorter than the original if some elements were removed.
// Note that the order of elements in the original slice is preserved in the output.
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
j++
}
}
return collection[:j]
}
// FilterI is a generic function that modifies the input slice in-place to contain only the elements
// that satisfy the provided predicate function. The predicate function takes an element of the slice and its index,
// and should return true for elements that should be kept and false for elements that should be removed.
// The function returns the modified slice, which may be shorter than the original if some elements were removed.
// 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
j++
}
}
return collection[:j]
}
// 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.
func Map[T any, Slice ~[]T](collection Slice, fn func(item T) T) {
for i := range collection {
collection[i] = fn(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) {
for i := range collection {
collection[i] = fn(collection[i], i)
}
}
// Shuffle returns an array of shuffled values. Uses the Fisher-Yates shuffle algorithm.
// Play: https://go.dev/play/p/2xb3WdLjeSJ
func Shuffle[T any, Slice ~[]T](collection Slice) {
rand.Shuffle(len(collection), func(i, j int) {
collection[i], collection[j] = collection[j], collection[i]
})
}
// Reverse reverses array so that the first element becomes the last, the second element becomes the second to last, and so on.
// Play: https://go.dev/play/p/O-M5pmCRgzV
func Reverse[T any, Slice ~[]T](collection Slice) {
length := len(collection)
half := length / 2
for i := 0; i < half; i = i + 1 {
j := length - 1 - i
collection[i], collection[j] = collection[j], collection[i]
}
}

375
vendor/github.com/samber/lo/retry.go generated vendored Normal file
View File

@@ -0,0 +1,375 @@
package lo
import (
"sync"
"time"
)
type debounce struct {
after time.Duration
mu *sync.Mutex
timer *time.Timer
done bool
callbacks []func()
}
func (d *debounce) reset() {
d.mu.Lock()
defer d.mu.Unlock()
if d.done {
return
}
if d.timer != nil {
d.timer.Stop()
}
d.timer = time.AfterFunc(d.after, func() {
for i := range d.callbacks {
d.callbacks[i]()
}
})
}
func (d *debounce) cancel() {
d.mu.Lock()
defer d.mu.Unlock()
if d.timer != nil {
d.timer.Stop()
d.timer = nil
}
d.done = true
}
// NewDebounce creates a debounced instance that delays invoking functions given until after wait milliseconds have elapsed.
// Play: https://go.dev/play/p/mz32VMK2nqe
func NewDebounce(duration time.Duration, f ...func()) (func(), func()) {
d := &debounce{
after: duration,
mu: new(sync.Mutex),
timer: nil,
done: false,
callbacks: f,
}
return func() {
d.reset()
}, d.cancel
}
type debounceByItem struct {
mu *sync.Mutex
timer *time.Timer
count int
}
type debounceBy[T comparable] struct {
after time.Duration
mu *sync.Mutex
items map[T]*debounceByItem
callbacks []func(key T, count int)
}
func (d *debounceBy[T]) reset(key T) {
d.mu.Lock()
if _, ok := d.items[key]; !ok {
d.items[key] = &debounceByItem{
mu: new(sync.Mutex),
timer: nil,
}
}
item := d.items[key]
d.mu.Unlock()
item.mu.Lock()
defer item.mu.Unlock()
item.count++
if item.timer != nil {
item.timer.Stop()
}
item.timer = time.AfterFunc(d.after, func() {
item.mu.Lock()
count := item.count
item.count = 0
item.mu.Unlock()
for i := range d.callbacks {
d.callbacks[i](key, count)
}
})
}
func (d *debounceBy[T]) cancel(key T) {
d.mu.Lock()
defer d.mu.Unlock()
if item, ok := d.items[key]; ok {
item.mu.Lock()
if item.timer != nil {
item.timer.Stop()
item.timer = nil
}
item.mu.Unlock()
delete(d.items, key)
}
}
// NewDebounceBy creates a debounced instance for each distinct key, that delays invoking functions given until after wait milliseconds have elapsed.
// Play: https://go.dev/play/p/d3Vpt6pxhY8
func NewDebounceBy[T comparable](duration time.Duration, f ...func(key T, count int)) (func(key T), func(key T)) {
d := &debounceBy[T]{
after: duration,
mu: new(sync.Mutex),
items: map[T]*debounceByItem{},
callbacks: f,
}
return func(key T) {
d.reset(key)
}, d.cancel
}
// Attempt invokes a function N times until it returns valid output. Returns either the caught error or nil.
// When the first argument is less than `1`, the function runs until a successful response is returned.
// Play: https://go.dev/play/p/3ggJZ2ZKcMj
func Attempt(maxIteration int, f func(index int) error) (int, error) {
var err error
for i := 0; maxIteration <= 0 || i < maxIteration; i++ {
// for retries >= 0 {
err = f(i)
if err == nil {
return i + 1, nil
}
}
return maxIteration, err
}
// AttemptWithDelay invokes a function N times until it returns valid output,
// with a pause between each call. Returns either the caught error or nil.
// When the first argument is less than `1`, the function runs until a successful
// response is returned.
// Play: https://go.dev/play/p/tVs6CygC7m1
func AttemptWithDelay(maxIteration int, delay time.Duration, f func(index int, duration time.Duration) error) (int, time.Duration, error) {
var err error
start := time.Now()
for i := 0; maxIteration <= 0 || i < maxIteration; i++ {
err = f(i, time.Since(start))
if err == nil {
return i + 1, time.Since(start), nil
}
if maxIteration <= 0 || i+1 < maxIteration {
time.Sleep(delay)
}
}
return maxIteration, time.Since(start), err
}
// AttemptWhile invokes a function N times until it returns valid output.
// Returns either the caught error or nil, along with a bool value to determine
// whether the function should be invoked again. It will terminate the invoke
// immediately if the second return value is false. When the first
// argument is less than `1`, the function runs until a successful response is
// returned.
func AttemptWhile(maxIteration int, f func(int) (error, bool)) (int, error) {
var err error
var shouldContinueInvoke bool
for i := 0; maxIteration <= 0 || i < maxIteration; i++ {
// for retries >= 0 {
err, shouldContinueInvoke = f(i)
if !shouldContinueInvoke { // if shouldContinueInvoke is false, then return immediately
return i + 1, err
}
if err == nil {
return i + 1, nil
}
}
return maxIteration, err
}
// AttemptWhileWithDelay invokes a function N times until it returns valid output,
// with a pause between each call. Returns either the caught error or nil, along
// with a bool value to determine whether the function should be invoked again.
// It will terminate the invoke immediately if the second return value is false.
// When the first argument is less than `1`, the function runs until a successful
// response is returned.
func AttemptWhileWithDelay(maxIteration int, delay time.Duration, f func(int, time.Duration) (error, bool)) (int, time.Duration, error) {
var err error
var shouldContinueInvoke bool
start := time.Now()
for i := 0; maxIteration <= 0 || i < maxIteration; i++ {
err, shouldContinueInvoke = f(i, time.Since(start))
if !shouldContinueInvoke { // if shouldContinueInvoke is false, then return immediately
return i + 1, time.Since(start), err
}
if err == nil {
return i + 1, time.Since(start), nil
}
if maxIteration <= 0 || i+1 < maxIteration {
time.Sleep(delay)
}
}
return maxIteration, time.Since(start), err
}
type transactionStep[T any] struct {
exec func(T) (T, error)
onRollback func(T) T
}
// NewTransaction instantiate a new transaction.
func NewTransaction[T any]() *Transaction[T] {
return &Transaction[T]{
steps: []transactionStep[T]{},
}
}
// Transaction implements a Saga pattern
type Transaction[T any] struct {
steps []transactionStep[T]
}
// Then adds a step to the chain of callbacks. It returns the same Transaction.
func (t *Transaction[T]) Then(exec func(T) (T, error), onRollback func(T) T) *Transaction[T] {
t.steps = append(t.steps, transactionStep[T]{
exec: exec,
onRollback: onRollback,
})
return t
}
// Process runs the Transaction steps and rollbacks in case of errors.
func (t *Transaction[T]) Process(state T) (T, error) {
var i int
var err error
for i < len(t.steps) {
state, err = t.steps[i].exec(state)
if err != nil {
break
}
i++
}
if err == nil {
return state, nil
}
for i > 0 {
i--
state = t.steps[i].onRollback(state)
}
return state, err
}
// @TODO: single mutex per key ?
type throttleBy[T comparable] struct {
mu *sync.Mutex
timer *time.Timer
interval time.Duration
callbacks []func(key T)
countLimit int
count map[T]int
}
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]++
for _, f := range th.callbacks {
f(key)
}
}
if th.timer == nil {
th.timer = time.AfterFunc(th.interval, func() {
th.reset()
})
}
}
func (th *throttleBy[T]) reset() {
th.mu.Lock()
defer th.mu.Unlock()
if th.timer != nil {
th.timer.Stop()
}
th.count = map[T]int{}
th.timer = nil
}
// NewThrottle creates a throttled instance that invokes given functions only once in every interval.
// This returns 2 functions, First one is throttled function and Second one is a function to reset interval
func NewThrottle(interval time.Duration, f ...func()) (throttle func(), reset func()) {
return NewThrottleWithCount(interval, 1, f...)
}
// NewThrottleWithCount is NewThrottle with count limit, throttled function will be invoked count times in every interval.
func NewThrottleWithCount(interval time.Duration, count int, f ...func()) (throttle func(), reset func()) {
callbacks := Map(f, func(item func(), _ int) func(struct{}) {
return func(struct{}) {
item()
}
})
throttleFn, reset := NewThrottleByWithCount[struct{}](interval, count, callbacks...)
return func() {
throttleFn(struct{}{})
}, reset
}
// NewThrottleBy creates a throttled instance that invokes given functions only once in every interval.
// This returns 2 functions, First one is throttled function and Second one is a function to reset interval
func NewThrottleBy[T comparable](interval time.Duration, f ...func(key T)) (throttle func(key T), reset func()) {
return NewThrottleByWithCount[T](interval, 1, f...)
}
// NewThrottleByWithCount is NewThrottleBy with count limit, throttled function will be invoked count times in every interval.
func NewThrottleByWithCount[T comparable](interval time.Duration, count int, f ...func(key T)) (throttle func(key T), reset func()) {
if count <= 0 {
count = 1
}
th := &throttleBy[T]{
mu: new(sync.Mutex),
interval: interval,
callbacks: f,
countLimit: count,
count: map[T]int{},
}
return th.throttledFunc, th.reset
}

745
vendor/github.com/samber/lo/slice.go generated vendored Normal file
View File

@@ -0,0 +1,745 @@
package lo
import (
"sort"
"github.com/samber/lo/internal/constraints"
"github.com/samber/lo/mutable"
)
// Filter iterates over elements of collection, returning an array of all elements predicate returns truthy for.
// Play: https://go.dev/play/p/Apjg3WeSi7K
func Filter[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) bool) Slice {
result := make(Slice, 0, len(collection))
for i := range collection {
if predicate(collection[i], i) {
result = append(result, collection[i])
}
}
return result
}
// Map manipulates a slice and transforms it to a slice of another type.
// Play: https://go.dev/play/p/OkPcYAhBo0D
func Map[T any, R any](collection []T, iteratee func(item T, index int) R) []R {
result := make([]R, len(collection))
for i := range collection {
result[i] = iteratee(collection[i], i)
}
return result
}
// UniqMap manipulates a slice and transforms it to a slice of another type with unique values.
func UniqMap[T any, R comparable](collection []T, iteratee func(item T, index int) R) []R {
result := make([]R, 0, len(collection))
seen := make(map[R]struct{}, len(collection))
for i := range collection {
r := iteratee(collection[i], i)
if _, ok := seen[r]; !ok {
result = append(result, r)
seen[r] = struct{}{}
}
}
return result
}
// FilterMap returns a slice which 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
// - whether the result element should be included or not.
//
// Play: https://go.dev/play/p/-AuYXfy7opz
func FilterMap[T any, R any](collection []T, callback func(item T, index int) (R, bool)) []R {
result := make([]R, 0, len(collection))
for i := range collection {
if r, ok := callback(collection[i], i); ok {
result = append(result, r)
}
}
return result
}
// FlatMap 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.
// Play: https://go.dev/play/p/YSoYmQTA8-U
func FlatMap[T any, R any](collection []T, iteratee func(item T, index int) []R) []R {
result := make([]R, 0, len(collection))
for i := range collection {
result = append(result, iteratee(collection[i], i)...)
}
return result
}
// 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/R4UHXZNaaUG
func Reduce[T any, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) R {
for i := range collection {
initial = accumulator(initial, collection[i], i)
}
return initial
}
// ReduceRight helper 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 any, R any](collection []T, accumulator func(agg R, item T, index int) R, initial R) R {
for i := len(collection) - 1; i >= 0; i-- {
initial = accumulator(initial, collection[i], i)
}
return initial
}
// ForEach iterates over elements of collection and invokes iteratee for each element.
// Play: https://go.dev/play/p/oofyiUPRf8t
func ForEach[T any](collection []T, iteratee func(item T, index int)) {
for i := range collection {
iteratee(collection[i], i)
}
}
// ForEachWhile iterates over elements of collection and invokes iteratee 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) (goon bool)) {
for i := range collection {
if !iteratee(collection[i], i) {
break
}
}
}
// Times invokes the iteratee n times, returning an array of the results of each invocation.
// The iteratee is invoked with index as argument.
// Play: https://go.dev/play/p/vgQj3Glr6lT
func Times[T any](count int, iteratee func(index int) T) []T {
result := make([]T, count)
for i := 0; i < count; i++ {
result[i] = iteratee(i)
}
return result
}
// Uniq returns a duplicate-free version of an array, 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 array.
// Play: https://go.dev/play/p/DTzbeXZ6iEN
func Uniq[T comparable, Slice ~[]T](collection Slice) Slice {
result := make(Slice, 0, len(collection))
seen := make(map[T]struct{}, len(collection))
for i := range collection {
if _, ok := seen[collection[i]]; ok {
continue
}
seen[collection[i]] = struct{}{}
result = append(result, collection[i])
}
return result
}
// UniqBy returns a duplicate-free version of an array, 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 array. It accepts `iteratee` which is
// invoked for each element in array to generate the criterion by which uniqueness is computed.
// Play: https://go.dev/play/p/g42Z3QSb53u
func UniqBy[T any, U comparable, Slice ~[]T](collection Slice, iteratee func(item T) U) Slice {
result := make(Slice, 0, len(collection))
seen := make(map[U]struct{}, len(collection))
for i := range collection {
key := iteratee(collection[i])
if _, ok := seen[key]; ok {
continue
}
seen[key] = struct{}{}
result = append(result, collection[i])
}
return result
}
// 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 {
result := map[U]Slice{}
for i := range collection {
key := iteratee(collection[i])
result[key] = append(result[key], collection[i])
}
return result
}
// GroupByMap returns an object composed of keys generated from the results of running each element of collection through iteratee.
func GroupByMap[T any, K comparable, V any](collection []T, iteratee func(item T) (K, V)) map[K][]V {
result := map[K][]V{}
for i := range collection {
k, v := iteratee(collection[i])
result[k] = append(result[k], v)
}
return result
}
// Chunk returns an array of elements split into groups the length of size. If array can't be split evenly,
// the final chunk will be the remaining elements.
// Play: https://go.dev/play/p/EeKl0AuTehH
func Chunk[T any, Slice ~[]T](collection Slice, size int) []Slice {
if size <= 0 {
panic("Second parameter must be greater than 0")
}
chunksNum := len(collection) / size
if len(collection)%size != 0 {
chunksNum += 1
}
result := make([]Slice, 0, chunksNum)
for i := 0; i < chunksNum; i++ {
last := (i + 1) * size
if last > len(collection) {
last = len(collection)
}
result = append(result, collection[i*size:last:last])
}
return result
}
// PartitionBy returns an array 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.
// Play: https://go.dev/play/p/NfQ_nGjkgXW
func PartitionBy[T any, K comparable, Slice ~[]T](collection Slice, iteratee func(item T) K) []Slice {
result := []Slice{}
seen := map[K]int{}
for i := range collection {
key := iteratee(collection[i])
resultIndex, ok := seen[key]
if !ok {
resultIndex = len(result)
seen[key] = resultIndex
result = append(result, Slice{})
}
result[resultIndex] = append(result[resultIndex], collection[i])
}
return result
// unordered:
// groups := GroupBy[T, K](collection, iteratee)
// return Values[K, []T](groups)
}
// Flatten returns an array a single level deep.
// Play: https://go.dev/play/p/rbp9ORaMpjw
func Flatten[T any, Slice ~[]T](collection []Slice) Slice {
totalLen := 0
for i := range collection {
totalLen += len(collection[i])
}
result := make(Slice, 0, totalLen)
for i := range collection {
result = append(result, collection[i]...)
}
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 {
if len(collections) == 0 {
return Slice{}
}
maxSize := 0
totalSize := 0
for i := range collections {
size := len(collections[i])
totalSize += size
if size > maxSize {
maxSize = size
}
}
if maxSize == 0 {
return Slice{}
}
result := make(Slice, totalSize)
resultIdx := 0
for i := 0; i < maxSize; i++ {
for j := range collections {
if len(collections[j])-1 < i {
continue
}
result[resultIdx] = collections[j][i]
resultIdx++
}
}
return result
}
// Shuffle returns an array of shuffled values. Uses the Fisher-Yates shuffle algorithm.
// Play: https://go.dev/play/p/ZTGG7OUCdnp
//
// Deprecated: use mutable.Shuffle() instead.
func Shuffle[T any, Slice ~[]T](collection Slice) Slice {
mutable.Shuffle(collection)
return collection
}
// Reverse reverses array so that the first element becomes the last, the second element becomes the second to last, and so on.
// Play: https://go.dev/play/p/iv2e9jslfBM
//
// Deprecated: use mutable.Reverse() instead.
func Reverse[T any, Slice ~[]T](collection Slice) Slice {
mutable.Reverse(collection)
return collection
}
// Fill fills elements of array with `initial` value.
// Play: https://go.dev/play/p/VwR34GzqEub
func Fill[T Clonable[T], Slice ~[]T](collection Slice, initial T) Slice {
result := make(Slice, 0, len(collection))
for range collection {
result = append(result, initial.Clone())
}
return result
}
// Repeat builds a slice with N copies of initial value.
// Play: https://go.dev/play/p/g3uHXbmc3b6
func Repeat[T Clonable[T]](count int, initial T) []T {
result := make([]T, 0, count)
for i := 0; i < count; i++ {
result = append(result, initial.Clone())
}
return result
}
// 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 {
result := make([]T, 0, count)
for i := 0; i < count; i++ {
result = append(result, predicate(i))
}
return result
}
// KeyBy transforms a slice or an array of structs to a map based on a pivot callback.
// Play: https://go.dev/play/p/mdaClUAT-zZ
func KeyBy[K comparable, V any](collection []V, iteratee func(item V) K) map[K]V {
result := make(map[K]V, len(collection))
for i := range collection {
k := iteratee(collection[i])
result[k] = collection[i]
}
return result
}
// Associate returns a map containing key-value pairs provided by transform function applied to elements of the given slice.
// If any of two pairs would 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 array.
// Play: https://go.dev/play/p/WHa2CfMO3Lr
func Associate[T any, K comparable, V any](collection []T, transform func(item T) (K, V)) map[K]V {
result := make(map[K]V, len(collection))
for i := range collection {
k, v := transform(collection[i])
result[k] = v
}
return result
}
// SliceToMap returns a map containing key-value pairs provided by transform function applied to elements of the given slice.
// If any of two pairs would 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 array.
// Alias of Associate().
// Play: https://go.dev/play/p/WHa2CfMO3Lr
func SliceToMap[T any, K comparable, V any](collection []T, transform func(item T) (K, V)) map[K]V {
return Associate(collection, transform)
}
// FilterSliceToMap returns a map containing key-value pairs provided by transform function applied to elements of the given slice.
// If any of two pairs would 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 array.
// The third return value of the transform function is a boolean that indicates whether the key-value pair should be included in the map.
func FilterSliceToMap[T any, K comparable, V any](collection []T, transform func(item T) (K, V, bool)) map[K]V {
result := make(map[K]V, len(collection))
for i := range collection {
k, v, ok := transform(collection[i])
if ok {
result[k] = v
}
}
return result
}
// Keyify returns a map with each unique element of the slice as a key.
func Keyify[T comparable, Slice ~[]T](collection Slice) map[T]struct{} {
result := make(map[T]struct{}, len(collection))
for i := range collection {
result[collection[i]] = struct{}{}
}
return result
}
// Drop drops n elements from the beginning of a slice or array.
// Play: https://go.dev/play/p/JswS7vXRJP2
func Drop[T any, Slice ~[]T](collection Slice, n int) Slice {
if len(collection) <= n {
return make(Slice, 0)
}
result := make(Slice, 0, len(collection)-n)
return append(result, collection[n:]...)
}
// DropRight drops n elements from the end of a slice or array.
// Play: https://go.dev/play/p/GG0nXkSJJa3
func DropRight[T any, Slice ~[]T](collection Slice, n int) Slice {
if len(collection) <= n {
return Slice{}
}
result := make(Slice, 0, len(collection)-n)
return append(result, collection[:len(collection)-n]...)
}
// DropWhile drops elements from the beginning of a slice or array while the predicate returns true.
// Play: https://go.dev/play/p/7gBPYw2IK16
func DropWhile[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, 0, len(collection)-i)
return append(result, collection[i:]...)
}
// DropRightWhile drops elements from the end of a slice or array while the predicate returns true.
// Play: https://go.dev/play/p/3-n71oEC0Hz
func DropRightWhile[T any, Slice ~[]T](collection Slice, predicate func(item T) bool) Slice {
i := len(collection) - 1
for ; i >= 0; i-- {
if !predicate(collection[i]) {
break
}
}
result := make(Slice, 0, i+1)
return append(result, collection[:i+1]...)
}
// DropByIndex drops elements from a slice or array 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](collection []T, indexes ...int) []T {
initialSize := len(collection)
if initialSize == 0 {
return make([]T, 0)
}
for i := range indexes {
if indexes[i] < 0 {
indexes[i] = initialSize + indexes[i]
}
}
indexes = Uniq(indexes)
sort.Ints(indexes)
result := make([]T, 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
}
// Reject is the opposite of Filter, this method returns the elements of collection that predicate does not return truthy for.
// Play: https://go.dev/play/p/YkLMODy1WEL
func Reject[T any, Slice ~[]T](collection Slice, predicate func(item T, index int) bool) Slice {
result := Slice{}
for i := range collection {
if !predicate(collection[i], i) {
result = append(result, collection[i])
}
}
return result
}
// RejectMap is the opposite of FilterMap, this method returns a slice which 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
// - whether the result element should be included or not.
func RejectMap[T any, R any](collection []T, callback func(item T, index int) (R, bool)) []R {
result := []R{}
for i := range collection {
if r, ok := callback(collection[i], i); !ok {
result = append(result, r)
}
}
return result
}
// FilterReject mixes Filter and Reject, this method returns two slices, one for the elements of collection that
// predicate returns truthy for and one for the elements that predicate does not return truthy for.
func FilterReject[T any, Slice ~[]T](collection Slice, predicate func(T, int) bool) (kept Slice, rejected Slice) {
kept = make(Slice, 0, len(collection))
rejected = make(Slice, 0, len(collection))
for i := range collection {
if predicate(collection[i], i) {
kept = append(kept, collection[i])
} else {
rejected = append(rejected, collection[i])
}
}
return kept, rejected
}
// Count counts the number of elements in the collection that compare equal to value.
// Play: https://go.dev/play/p/Y3FlK54yveC
func Count[T comparable](collection []T, value T) (count int) {
for i := range collection {
if collection[i] == value {
count++
}
}
return count
}
// CountBy counts the number of elements in the collection for which predicate is true.
// Play: https://go.dev/play/p/ByQbNYQQi4X
func CountBy[T any](collection []T, predicate func(item T) bool) (count int) {
for i := range collection {
if predicate(collection[i]) {
count++
}
}
return count
}
// 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 {
result := make(map[T]int)
for i := range collection {
result[collection[i]]++
}
return result
}
// CountValuesBy counts the number of each element return from mapper 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 {
result := make(map[U]int)
for i := range collection {
result[mapper(collection[i])]++
}
return result
}
// Subset returns a copy of a slice from `offset` up to `length` elements. Like `slice[start:start+length]`, but does not panic on overflow.
// Play: https://go.dev/play/p/tOQu1GhFcog
func Subset[T any, Slice ~[]T](collection Slice, offset int, length uint) Slice {
size := len(collection)
if offset < 0 {
offset = size + offset
if offset < 0 {
offset = 0
}
}
if offset > size {
return Slice{}
}
if length > uint(size)-uint(offset) {
length = uint(size - offset)
}
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.
// Play: https://go.dev/play/p/8XWYhfMMA1h
func Slice[T any, Slice ~[]T](collection Slice, start int, end int) Slice {
size := len(collection)
if start >= end {
return Slice{}
}
if start > size {
start = size
}
if start < 0 {
start = 0
}
if end > size {
end = size
}
if end < 0 {
end = 0
}
return collection[start:end]
}
// Replace returns a copy of the slice with the first n non-overlapping instances of old replaced by new.
// Play: https://go.dev/play/p/XfPzmf9gql6
func Replace[T comparable, Slice ~[]T](collection Slice, old T, new T, n int) Slice {
result := make(Slice, len(collection))
copy(result, collection)
for i := range result {
if result[i] == old && n != 0 {
result[i] = new
n--
}
}
return result
}
// ReplaceAll returns a copy of the slice with all non-overlapping instances of old replaced by new.
// Play: https://go.dev/play/p/a9xZFUHfYcV
func ReplaceAll[T comparable, Slice ~[]T](collection Slice, old T, new T) Slice {
return Replace(collection, old, new, -1)
}
// 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 {
var zero T
result := make(Slice, 0, len(collection))
for i := range collection {
if collection[i] != zero {
result = append(result, collection[i])
}
}
return result
}
// IsSorted checks if a slice is sorted.
// Play: https://go.dev/play/p/mc3qR-t4mcx
func IsSorted[T constraints.Ordered](collection []T) bool {
for i := 1; i < len(collection); i++ {
if collection[i-1] > collection[i] {
return false
}
}
return true
}
// IsSortedByKey checks if a slice is sorted by iteratee.
// Play: https://go.dev/play/p/wiG6XyBBu49
func IsSortedByKey[T any, K constraints.Ordered](collection []T, iteratee func(item T) K) bool {
size := len(collection)
for i := 0; i < size-1; i++ {
if iteratee(collection[i]) > iteratee(collection[i+1]) {
return false
}
}
return true
}
// 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
func Splice[T any, Slice ~[]T](collection Slice, i int, elements ...T) Slice {
sizeCollection := len(collection)
sizeElements := len(elements)
output := make(Slice, 0, sizeCollection+sizeElements) // preallocate memory for the output slice
if sizeElements == 0 {
return append(output, collection...) // simple copy
} else if i > sizeCollection {
// positive overflow
return append(append(output, collection...), elements...)
} else if i < -sizeCollection {
// negative overflow
return append(append(output, elements...), collection...)
} else if i < 0 {
// backward
i = sizeCollection + i
}
return append(append(append(output, collection[:i]...), elements...), collection[i:]...)
}

234
vendor/github.com/samber/lo/string.go generated vendored Normal file
View File

@@ -0,0 +1,234 @@
package lo
import (
"math"
"regexp"
"strings"
"unicode"
"unicode/utf8"
"github.com/samber/lo/internal/rand"
"golang.org/x/text/cases"
"golang.org/x/text/language"
)
var (
LowerCaseLettersCharset = []rune("abcdefghijklmnopqrstuvwxyz")
UpperCaseLettersCharset = []rune("ABCDEFGHIJKLMNOPQRSTUVWXYZ")
LettersCharset = append(LowerCaseLettersCharset, UpperCaseLettersCharset...)
NumbersCharset = []rune("0123456789")
AlphanumericCharset = append(LettersCharset, NumbersCharset...)
SpecialCharset = []rune("!@#$%^&*()_+-=[]{}|;':\",./<>?")
AllCharset = append(AlphanumericCharset, SpecialCharset...)
// bearer:disable go_lang_permissive_regex_validation
splitWordReg = regexp.MustCompile(`([a-z])([A-Z0-9])|([a-zA-Z])([0-9])|([0-9])([a-zA-Z])|([A-Z])([A-Z])([a-z])`)
// bearer:disable go_lang_permissive_regex_validation
splitNumberLetterReg = regexp.MustCompile(`([0-9])([a-zA-Z])`)
maximumCapacity = math.MaxInt>>1 + 1
)
// RandomString return a random string.
// Play: https://go.dev/play/p/rRseOQVVum4
func RandomString(size int, charset []rune) string {
if size <= 0 {
panic("lo.RandomString: Size parameter must be greater than 0")
}
if len(charset) <= 0 {
panic("lo.RandomString: Charset parameter must not be empty")
}
// see https://stackoverflow.com/questions/22892120/how-to-generate-a-random-string-of-a-fixed-length-in-go
sb := strings.Builder{}
sb.Grow(size)
// Calculate the number of bits required to represent the charset,
// e.g., for 62 characters, it would need 6 bits (since 62 -> 64 = 2^6)
letterIdBits := int(math.Log2(float64(nearestPowerOfTwo(len(charset)))))
// Determine the corresponding bitmask,
// e.g., for 62 characters, the bitmask would be 111111.
var letterIdMask int64 = 1<<letterIdBits - 1
// Available count, since rand.Int64() returns a non-negative number, the first bit is fixed, so there are 63 random bits
// e.g., for 62 characters, this value is 10 (63 / 6).
letterIdMax := 63 / letterIdBits
// Generate the random string in a loop.
for i, cache, remain := size-1, rand.Int64(), letterIdMax; i >= 0; {
// Regenerate the random number if all available bits have been used
if remain == 0 {
cache, remain = rand.Int64(), letterIdMax
}
// Select a character from the charset
if idx := int(cache & letterIdMask); idx < len(charset) {
sb.WriteRune(charset[idx])
i--
}
// Shift the bits to the right to prepare for the next character selection,
// e.g., for 62 characters, shift by 6 bits.
cache >>= letterIdBits
// Decrease the remaining number of uses for the current random number.
remain--
}
return sb.String()
}
// nearestPowerOfTwo returns the nearest power of two.
func nearestPowerOfTwo(cap int) int {
n := cap - 1
n |= n >> 1
n |= n >> 2
n |= n >> 4
n |= n >> 8
n |= n >> 16
if n < 0 {
return 1
}
if n >= maximumCapacity {
return maximumCapacity
}
return n + 1
}
// Substring return part of a 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)
if offset < 0 {
offset = size + offset
if offset < 0 {
offset = 0
}
}
if offset >= size {
return Empty[T]()
}
if length > uint(size)-uint(offset) {
length = uint(size - offset)
}
return T(strings.ReplaceAll(string(rs[offset:offset+int(length)]), "\x00", ""))
}
// ChunkString returns an array of strings split into groups the length of size. If array can't be split evenly,
// the final chunk will be the remaining elements.
// Play: https://go.dev/play/p/__FLTuJVz54
func ChunkString[T ~string](str T, size int) []T {
if size <= 0 {
panic("lo.ChunkString: Size parameter must be greater than 0")
}
if len(str) == 0 {
return []T{""}
}
if size >= len(str) {
return []T{str}
}
var chunks = make([]T, 0, ((len(str)-1)/size)+1)
currentLen := 0
currentStart := 0
for i := range str {
if currentLen == size {
chunks = append(chunks, str[currentStart:i])
currentLen = 0
currentStart = i
}
currentLen++
}
chunks = append(chunks, str[currentStart:])
return chunks
}
// RuneLength is an alias to utf8.RuneCountInString which returns the number of runes in string.
// Play: https://go.dev/play/p/tuhgW_lWY8l
func RuneLength(str string) int {
return utf8.RuneCountInString(str)
}
// PascalCase converts string to pascal case.
func PascalCase(str string) string {
items := Words(str)
for i := range items {
items[i] = Capitalize(items[i])
}
return strings.Join(items, "")
}
// CamelCase converts string to camel case.
func CamelCase(str string) string {
items := Words(str)
for i, item := range items {
item = strings.ToLower(item)
if i > 0 {
item = Capitalize(item)
}
items[i] = item
}
return strings.Join(items, "")
}
// KebabCase converts string to kebab case.
func KebabCase(str string) string {
items := Words(str)
for i := range items {
items[i] = strings.ToLower(items[i])
}
return strings.Join(items, "-")
}
// SnakeCase converts string to snake case.
func SnakeCase(str string) string {
items := Words(str)
for i := range items {
items[i] = strings.ToLower(items[i])
}
return strings.Join(items, "_")
}
// Words splits string into an array of its words.
func Words(str string) []string {
str = splitWordReg.ReplaceAllString(str, `$1$3$5$7 $2$4$6$8$9`)
// example: Int8Value => Int 8Value => Int 8 Value
str = splitNumberLetterReg.ReplaceAllString(str, "$1 $2")
var result strings.Builder
for _, r := range str {
if unicode.IsLetter(r) || unicode.IsDigit(r) {
result.WriteRune(r)
} else {
result.WriteRune(' ')
}
}
return strings.Fields(result.String())
}
// Capitalize converts the first character of string to upper case and the remaining to lower case.
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.
func Ellipsis(str string, length int) string {
str = strings.TrimSpace(str)
if len(str) > length {
if len(str) < 3 || length < 3 {
return "..."
}
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)
}

85
vendor/github.com/samber/lo/time.go generated vendored Normal file
View File

@@ -0,0 +1,85 @@
package lo
import "time"
// Duration returns the time taken to execute a function.
func Duration(cb func()) time.Duration {
return Duration0(cb)
}
// Duration0 returns the time taken to execute a function.
func Duration0(cb func()) time.Duration {
start := time.Now()
cb()
return time.Since(start)
}
// Duration1 returns the time taken to execute a function.
func Duration1[A any](cb func() A) (A, time.Duration) {
start := time.Now()
a := cb()
return a, time.Since(start)
}
// Duration2 returns the time taken to execute a function.
func Duration2[A, B any](cb func() (A, B)) (A, B, time.Duration) {
start := time.Now()
a, b := cb()
return a, b, time.Since(start)
}
// Duration3 returns the time taken to execute a function.
func Duration3[A, B, C any](cb func() (A, B, C)) (A, B, C, time.Duration) {
start := time.Now()
a, b, c := cb()
return a, b, c, time.Since(start)
}
// Duration4 returns the time taken to execute a function.
func Duration4[A, B, C, D any](cb func() (A, B, C, D)) (A, B, C, D, time.Duration) {
start := time.Now()
a, b, c, d := cb()
return a, b, c, d, time.Since(start)
}
// Duration5 returns the time taken to execute a function.
func Duration5[A, B, C, D, E any](cb func() (A, B, C, D, E)) (A, B, C, D, E, time.Duration) {
start := time.Now()
a, b, c, d, e := cb()
return a, b, c, d, e, time.Since(start)
}
// Duration6 returns the time taken to execute a function.
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) {
start := time.Now()
a, b, c, d, e, f := cb()
return a, b, c, d, e, f, time.Since(start)
}
// Duration7 returns the time taken to execute a function.
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) {
start := time.Now()
a, b, c, d, e, f, g := cb()
return a, b, c, d, e, f, g, time.Since(start)
}
// Duration8 returns the time taken to execute a function.
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) {
start := time.Now()
a, b, c, d, e, f, g, h := cb()
return a, b, c, d, e, f, g, h, time.Since(start)
}
// Duration9 returns the time taken to execute a function.
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) {
start := time.Now()
a, b, c, d, e, f, g, h, i := cb()
return a, b, c, d, e, f, g, h, i, time.Since(start)
}
// Duration10 returns the time taken to execute a function.
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) {
start := time.Now()
a, b, c, d, e, f, g, h, i, j := cb()
return a, b, c, d, e, f, g, h, i, j, time.Since(start)
}

1149
vendor/github.com/samber/lo/tuples.go generated vendored Normal file
View File

File diff suppressed because it is too large Load Diff

189
vendor/github.com/samber/lo/type_manipulation.go generated vendored Normal file
View File

@@ -0,0 +1,189 @@
package lo
import "reflect"
// IsNil checks if a value is nil or if it's a reference type with a nil underlying value.
func IsNil(x any) bool {
defer func() { recover() }() // nolint:errcheck
return x == nil || reflect.ValueOf(x).IsNil()
}
// IsNotNil checks if a value is not nil or if it's not a reference type with a nil underlying value.
func IsNotNil(x any) bool {
return !IsNil(x)
}
// ToPtr returns a pointer copy of value.
func ToPtr[T any](x T) *T {
return &x
}
// Nil returns a nil pointer of type.
func Nil[T any]() *T {
return nil
}
// EmptyableToPtr returns a pointer copy of value if it's nonzero.
// Otherwise, returns nil pointer.
func EmptyableToPtr[T any](x T) *T {
// 🤮
isZero := reflect.ValueOf(&x).Elem().IsZero()
if isZero {
return nil
}
return &x
}
// FromPtr returns the pointer value or empty.
func FromPtr[T any](x *T) T {
if x == nil {
return Empty[T]()
}
return *x
}
// FromPtrOr returns the pointer value or the fallback value.
func FromPtrOr[T any](x *T, fallback T) T {
if x == nil {
return fallback
}
return *x
}
// ToSlicePtr returns a slice of pointer copy of value.
func ToSlicePtr[T any](collection []T) []*T {
result := make([]*T, len(collection))
for i := range collection {
result[i] = &collection[i]
}
return result
}
// FromSlicePtr returns a slice with the pointer values.
// Returns a zero value in case of a nil pointer element.
func FromSlicePtr[T any](collection []*T) []T {
return Map(collection, func(x *T, _ int) T {
if x == nil {
return Empty[T]()
}
return *x
})
}
// FromSlicePtrOr returns a slice with the pointer values or the fallback value.
// Play: https://go.dev/play/p/lbunFvzlUDX
func FromSlicePtrOr[T any](collection []*T, fallback T) []T {
return Map(collection, func(x *T, _ int) T {
if x == nil {
return fallback
}
return *x
})
}
// ToAnySlice returns a slice with all elements mapped to `any` type
func ToAnySlice[T any](collection []T) []any {
result := make([]any, len(collection))
for i := range collection {
result[i] = collection[i]
}
return result
}
// FromAnySlice returns an `any` slice with all elements mapped to a type.
// Returns false in case of type conversion failure.
func FromAnySlice[T any](in []any) (out []T, ok bool) {
defer func() {
if r := recover(); r != nil {
out = []T{}
ok = false
}
}()
result := make([]T, len(in))
for i := range in {
result[i] = in[i].(T)
}
return result, true
}
// Empty returns the zero value (https://go.dev/ref/spec#The_zero_value).
func Empty[T any]() T {
var zero T
return zero
}
// IsEmpty returns true if argument is a zero value.
func IsEmpty[T comparable](v T) bool {
var zero T
return zero == v
}
// IsNotEmpty returns true if argument is not a zero value.
func IsNotEmpty[T comparable](v T) bool {
var zero T
return zero != v
}
// Coalesce returns the first non-empty arguments. Arguments must be comparable.
func Coalesce[T comparable](values ...T) (result T, ok bool) {
for i := range values {
if values[i] != result {
result = values[i]
ok = true
return
}
}
return
}
// CoalesceOrEmpty returns the first non-empty arguments. Arguments must be comparable.
func CoalesceOrEmpty[T comparable](v ...T) T {
result, _ := Coalesce(v...)
return result
}
// CoalesceSlice returns the first non-zero slice.
func CoalesceSlice[T any](v ...[]T) ([]T, bool) {
for i := range v {
if v[i] != nil && len(v[i]) > 0 {
return v[i], true
}
}
return []T{}, false
}
// CoalesceSliceOrEmpty returns the first non-zero slice.
func CoalesceSliceOrEmpty[T any](v ...[]T) []T {
for i := range v {
if v[i] != nil && len(v[i]) > 0 {
return v[i]
}
}
return []T{}
}
// CoalesceMap returns the first non-zero map.
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 {
return v[i], true
}
}
return map[K]V{}, false
}
// CoalesceMapOrEmpty returns the first non-zero map.
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 {
return v[i]
}
}
return map[K]V{}
}

123
vendor/github.com/samber/lo/types.go generated vendored Normal file
View File

@@ -0,0 +1,123 @@
package lo
// Entry defines a key/value pairs.
type Entry[K comparable, V any] struct {
Key K
Value V
}
// Tuple2 is a group of 2 elements (pair).
type Tuple2[A, B any] struct {
A A
B B
}
// Unpack returns values contained in tuple.
func (t Tuple2[A, B]) Unpack() (A, B) {
return t.A, t.B
}
// Tuple3 is a group of 3 elements.
type Tuple3[A, B, C any] struct {
A A
B B
C C
}
// Unpack returns values contained in tuple.
func (t Tuple3[A, B, C]) Unpack() (A, B, C) {
return t.A, t.B, t.C
}
// Tuple4 is a group of 4 elements.
type Tuple4[A, B, C, D any] struct {
A A
B B
C C
D D
}
// Unpack returns values contained in tuple.
func (t Tuple4[A, B, C, D]) Unpack() (A, B, C, D) {
return t.A, t.B, t.C, t.D
}
// Tuple5 is a group of 5 elements.
type Tuple5[A, B, C, D, E any] struct {
A A
B B
C C
D D
E E
}
// Unpack returns values contained in tuple.
func (t Tuple5[A, B, C, D, E]) Unpack() (A, B, C, D, E) {
return t.A, t.B, t.C, t.D, t.E
}
// Tuple6 is a group of 6 elements.
type Tuple6[A, B, C, D, E, F any] struct {
A A
B B
C C
D D
E E
F F
}
// Unpack returns values contained in tuple.
func (t Tuple6[A, B, C, D, E, F]) Unpack() (A, B, C, D, E, F) {
return t.A, t.B, t.C, t.D, t.E, t.F
}
// Tuple7 is a group of 7 elements.
type Tuple7[A, B, C, D, E, F, G any] struct {
A A
B B
C C
D D
E E
F F
G G
}
// Unpack returns values contained in tuple.
func (t Tuple7[A, B, C, D, E, F, G]) Unpack() (A, B, C, D, E, F, G) {
return t.A, t.B, t.C, t.D, t.E, t.F, t.G
}
// Tuple8 is a group of 8 elements.
type Tuple8[A, B, C, D, E, F, G, H any] struct {
A A
B B
C C
D D
E E
F F
G G
H H
}
// Unpack returns values contained in tuple.
func (t Tuple8[A, B, C, D, E, F, G, H]) Unpack() (A, B, C, D, E, F, G, H) {
return t.A, t.B, t.C, t.D, t.E, t.F, t.G, t.H
}
// Tuple9 is a group of 9 elements.
type Tuple9[A, B, C, D, E, F, G, H, I any] struct {
A A
B B
C C
D D
E E
F F
G G
H H
I I
}
// Unpack returns values contained in tuple.
func (t Tuple9[A, B, C, D, E, F, G, H, I]) Unpack() (A, B, C, D, E, F, G, H, I) {
return t.A, t.B, t.C, t.D, t.E, t.F, t.G, t.H, t.I
}

38
vendor/github.com/samber/slog-common/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,38 @@
# Created by https://www.toptal.com/developers/gitignore/api/go
# Edit at https://www.toptal.com/developers/gitignore?templates=go
### Go ###
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
### Go Patch ###
/vendor/
/Godeps/
# End of https://www.toptal.com/developers/gitignore/api/go
cover.out
cover.html
.vscode
.idea/

21
vendor/github.com/samber/slog-common/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Samuel Berthe
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

41
vendor/github.com/samber/slog-common/Makefile generated vendored Normal file
View File

@@ -0,0 +1,41 @@
build:
go build -v ./...
test:
go test -race -v ./...
watch-test:
reflex -t 50ms -s -- sh -c 'gotest -race -v ./...'
bench:
go test -benchmem -count 3 -bench ./...
watch-bench:
reflex -t 50ms -s -- sh -c 'go test -benchmem -count 3 -bench ./...'
coverage:
go test -v -coverprofile=cover.out -covermode=atomic ./...
go tool cover -html=cover.out -o cover.html
tools:
go install github.com/cespare/reflex@latest
go install github.com/rakyll/gotest@latest
go install github.com/psampaz/go-mod-outdated@latest
go install github.com/jondot/goweight@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go get -t -u golang.org/x/tools/cmd/cover
go install github.com/sonatype-nexus-community/nancy@latest
go mod tidy
lint:
golangci-lint run --timeout 60s --max-same-issues 50 ./...
lint-fix:
golangci-lint run --timeout 60s --max-same-issues 50 --fix ./...
audit:
go list -json -m all | nancy sleuth
outdated:
go list -u -m -json all | go-mod-outdated -update -direct
weight:
goweight

109
vendor/github.com/samber/slog-common/README.md generated vendored Normal file
View File

@@ -0,0 +1,109 @@
# Nothing to see here (internal package)
[![tag](https://img.shields.io/github/tag/samber/slog-common.svg)](https://github.com/samber/slog-common/releases)
![Go Version](https://img.shields.io/badge/Go-%3E%3D%201.21-%23007d9c)
[![GoDoc](https://godoc.org/github.com/samber/slog-common?status.svg)](https://pkg.go.dev/github.com/samber/slog-common)
![Build Status](https://github.com/samber/slog-common/actions/workflows/test.yml/badge.svg)
[![Go report](https://goreportcard.com/badge/github.com/samber/slog-common)](https://goreportcard.com/report/github.com/samber/slog-common)
[![Coverage](https://img.shields.io/codecov/c/github/samber/slog-common)](https://codecov.io/gh/samber/slog-common)
[![Contributors](https://img.shields.io/github/contributors/samber/slog-common)](https://github.com/samber/slog-common/graphs/contributors)
[![License](https://img.shields.io/github/license/samber/slog-common)](./LICENSE)
![gif-nothing-to-see-meme](https://media.giphy.com/media/xUStFKHmuFPYk/giphy.gif)
A toolchain for [slog](https://pkg.go.dev/log/slog) Go library.
This project gathers common functions for my [slog](https://pkg.go.dev/log/slog) Go libraries:
<div align="center">
<hr>
<sup><b>Sponsored by:</b></sup>
<br>
<a href="https://quickwit.io?utm_campaign=github_sponsorship&utm_medium=referral&utm_content=samber-slog-common&utm_source=github">
<div>
<img src="https://github.com/samber/oops/assets/2951285/49aaaa2b-b8c6-4f21-909f-c12577bb6a2e" width="240" alt="Quickwit">
</div>
<div>
Cloud-native search engine for observability - An OSS alternative to Splunk, Elasticsearch, Loki, and Tempo.
</div>
</a>
<hr>
</div>
**See also:**
- [slog-multi](https://github.com/samber/slog-multi): `slog.Handler` chaining, fanout, routing, failover, load balancing...
- [slog-formatter](https://github.com/samber/slog-formatter): `slog` attribute formatting
- [slog-sampling](https://github.com/samber/slog-sampling): `slog` sampling policy
- [slog-mock](https://github.com/samber/slog-mock): `slog.Handler` for test purposes
**HTTP middlewares:**
- [slog-gin](https://github.com/samber/slog-gin): Gin middleware for `slog` logger
- [slog-echo](https://github.com/samber/slog-echo): Echo middleware for `slog` logger
- [slog-fiber](https://github.com/samber/slog-fiber): Fiber middleware for `slog` logger
- [slog-chi](https://github.com/samber/slog-chi): Chi middleware for `slog` logger
- [slog-http](https://github.com/samber/slog-http): `net/http` middleware for `slog` logger
**Loggers:**
- [slog-zap](https://github.com/samber/slog-zap): A `slog` handler for `Zap`
- [slog-zerolog](https://github.com/samber/slog-zerolog): A `slog` handler for `Zerolog`
- [slog-logrus](https://github.com/samber/slog-logrus): A `slog` handler for `Logrus`
**Log sinks:**
- [slog-datadog](https://github.com/samber/slog-datadog): A `slog` handler for `Datadog`
- [slog-betterstack](https://github.com/samber/slog-betterstack): A `slog` handler for `Betterstack`
- [slog-rollbar](https://github.com/samber/slog-rollbar): A `slog` handler for `Rollbar`
- [slog-loki](https://github.com/samber/slog-loki): A `slog` handler for `Loki`
- [slog-sentry](https://github.com/samber/slog-sentry): A `slog` handler for `Sentry`
- [slog-syslog](https://github.com/samber/slog-syslog): A `slog` handler for `Syslog`
- [slog-logstash](https://github.com/samber/slog-logstash): A `slog` handler for `Logstash`
- [slog-fluentd](https://github.com/samber/slog-fluentd): A `slog` handler for `Fluentd`
- [slog-graylog](https://github.com/samber/slog-graylog): A `slog` handler for `Graylog`
- [slog-quickwit](https://github.com/samber/slog-quickwit): A `slog` handler for `Quickwit`
- [slog-slack](https://github.com/samber/slog-slack): A `slog` handler for `Slack`
- [slog-telegram](https://github.com/samber/slog-telegram): A `slog` handler for `Telegram`
- [slog-mattermost](https://github.com/samber/slog-mattermost): A `slog` handler for `Mattermost`
- [slog-microsoft-teams](https://github.com/samber/slog-microsoft-teams): A `slog` handler for `Microsoft Teams`
- [slog-webhook](https://github.com/samber/slog-webhook): A `slog` handler for `Webhook`
- [slog-kafka](https://github.com/samber/slog-kafka): A `slog` handler for `Kafka`
- [slog-nats](https://github.com/samber/slog-nats): A `slog` handler for `NATS`
- [slog-parquet](https://github.com/samber/slog-parquet): A `slog` handler for `Parquet` + `Object Storage`
- [slog-channel](https://github.com/samber/slog-channel): A `slog` handler for Go channels
## 🤝 Contributing
- Ping me on twitter [@samuelberthe](https://twitter.com/samuelberthe) (DMs, mentions, whatever :))
- Fork the [project](https://github.com/samber/slog-common)
- Fix [open issues](https://github.com/samber/slog-common/issues) or request new features
Don't hesitate ;)
```bash
# Install some dev dependencies
make tools
# Run tests
make test
# or
make watch-test
```
## 👤 Contributors
![Contributors](https://contrib.rocks/image?repo=samber/slog-common)
## 💫 Show your support
Give a ⭐️ if this project helped you!
[![GitHub Sponsors](https://img.shields.io/github/sponsors/samber?style=for-the-badge)](https://github.com/sponsors/samber)
## 📝 License
Copyright © 2023 [Samuel Berthe](https://github.com/samber).
This project is [MIT](./LICENSE) licensed.

318
vendor/github.com/samber/slog-common/attributes.go generated vendored Normal file
View File

@@ -0,0 +1,318 @@
package slogcommon
import (
"encoding"
"fmt"
"log/slog"
"net/http"
"reflect"
"runtime"
"slices"
"strings"
"github.com/samber/lo"
)
type ReplaceAttrFn = func(groups []string, a slog.Attr) slog.Attr
func AppendRecordAttrsToAttrs(attrs []slog.Attr, groups []string, record *slog.Record) []slog.Attr {
output := make([]slog.Attr, 0, len(attrs)+record.NumAttrs())
output = append(output, attrs...)
record.Attrs(func(attr slog.Attr) bool {
for i := len(groups) - 1; i >= 0; i-- {
attr = slog.Group(groups[i], attr)
}
output = append(output, attr)
return true
})
return output
}
func ReplaceAttrs(fn ReplaceAttrFn, groups []string, attrs ...slog.Attr) []slog.Attr {
for i := range attrs {
attr := attrs[i]
value := attr.Value.Resolve()
if value.Kind() == slog.KindGroup {
attrs[i].Value = slog.GroupValue(ReplaceAttrs(fn, append(groups, attr.Key), value.Group()...)...)
} else if fn != nil {
attrs[i] = fn(groups, attr)
}
}
return attrs
}
func AttrsToMap(attrs ...slog.Attr) map[string]any {
output := map[string]any{}
attrsByKey := groupValuesByKey(attrs)
for k, values := range attrsByKey {
v := mergeAttrValues(values...)
if v.Kind() == slog.KindGroup {
output[k] = AttrsToMap(v.Group()...)
} else {
output[k] = v.Any()
}
}
return output
}
func RecordToAttrsMap(r slog.Record) map[string]any {
attrs := make([]slog.Attr, r.NumAttrs())
r.Attrs(func(attr slog.Attr) bool {
attrs = append(attrs, attr)
return true
})
return AttrsToMap(attrs...)
}
func groupValuesByKey(attrs []slog.Attr) map[string][]slog.Value {
result := map[string][]slog.Value{}
for _, item := range attrs {
key := item.Key
result[key] = append(result[key], item.Value)
}
return result
}
func mergeAttrValues(values ...slog.Value) slog.Value {
v := values[0]
for i := 1; i < len(values); i++ {
if v.Kind() != slog.KindGroup || values[i].Kind() != slog.KindGroup {
v = values[i]
continue
}
v = slog.GroupValue(append(v.Group(), values[i].Group()...)...)
}
return v
}
func AttrToValue(attr slog.Attr) (string, any) {
k := attr.Key
v := attr.Value
kind := v.Kind()
switch kind {
case slog.KindAny:
return k, v.Any()
case slog.KindLogValuer:
return k, v.Any()
case slog.KindGroup:
return k, AttrsToMap(v.Group()...)
case slog.KindInt64:
return k, v.Int64()
case slog.KindUint64:
return k, v.Uint64()
case slog.KindFloat64:
return k, v.Float64()
case slog.KindString:
return k, v.String()
case slog.KindBool:
return k, v.Bool()
case slog.KindDuration:
return k, v.Duration()
case slog.KindTime:
return k, v.Time().UTC()
default:
return k, AnyValueToString(v)
}
}
func AnyValueToString(v slog.Value) string {
if tm, ok := v.Any().(encoding.TextMarshaler); ok {
data, err := tm.MarshalText()
if err != nil {
return ""
}
return string(data)
}
return fmt.Sprintf("%+v", v.Any())
}
func AttrsToString(attrs ...slog.Attr) map[string]string {
output := make(map[string]string, len(attrs))
for i := range attrs {
attr := attrs[i]
k, v := attr.Key, attr.Value
output[k] = ValueToString(v)
}
return output
}
func ValueToString(v slog.Value) string {
switch v.Kind() {
case slog.KindAny, slog.KindLogValuer, slog.KindGroup:
return AnyValueToString(v)
case slog.KindInt64, slog.KindUint64, slog.KindFloat64, slog.KindString, slog.KindBool, slog.KindDuration:
return v.String()
case slog.KindTime:
return v.Time().UTC().String()
default:
return AnyValueToString(v)
}
}
func ReplaceError(attrs []slog.Attr, errorKeys ...string) []slog.Attr {
replaceAttr := func(groups []string, a slog.Attr) slog.Attr {
if len(groups) > 1 {
return a
}
for i := range errorKeys {
if a.Key == errorKeys[i] {
if err, ok := a.Value.Any().(error); ok {
return slog.Any(a.Key, FormatError(err))
}
}
}
return a
}
return ReplaceAttrs(replaceAttr, []string{}, attrs...)
}
func ExtractError(attrs []slog.Attr, errorKeys ...string) ([]slog.Attr, error) {
for i := range attrs {
attr := attrs[i]
if !slices.Contains(errorKeys, attr.Key) {
continue
}
if err, ok := attr.Value.Resolve().Any().(error); ok {
output := make([]slog.Attr, 0, len(attrs)-1)
output = append(output, attrs[:i]...)
output = append(output, attrs[i+1:]...)
return output, err
}
}
return attrs, nil
}
func FormatErrorKey(values map[string]any, errorKeys ...string) map[string]any {
for _, errorKey := range errorKeys {
if err, ok := values[errorKey]; ok {
if e, ok := err.(error); ok {
values[errorKey] = FormatError(e)
break
}
}
}
return values
}
func FormatError(err error) map[string]any {
return map[string]any{
"kind": reflect.TypeOf(err).String(),
"error": err.Error(),
"stack": nil, // @TODO
}
}
func FormatRequest(req *http.Request, ignoreHeaders bool) map[string]any {
output := map[string]any{
"host": req.Host,
"method": req.Method,
"url": map[string]any{
"url": req.URL.String(),
"scheme": req.URL.Scheme,
"host": req.URL.Host,
"path": req.URL.Path,
"raw_query": req.URL.RawQuery,
"fragment": req.URL.Fragment,
"query": lo.MapEntries(req.URL.Query(), func(key string, values []string) (string, string) {
return key, strings.Join(values, ",")
}),
},
}
if !ignoreHeaders {
output["headers"] = lo.MapEntries(req.Header, func(key string, values []string) (string, string) {
return key, strings.Join(values, ",")
})
}
return output
}
func Source(sourceKey string, r *slog.Record) slog.Attr {
fs := runtime.CallersFrames([]uintptr{r.PC})
f, _ := fs.Next()
var args []any
if f.Function != "" {
args = append(args, slog.String("function", f.Function))
}
if f.File != "" {
args = append(args, slog.String("file", f.File))
}
if f.Line != 0 {
args = append(args, slog.Int("line", f.Line))
}
return slog.Group(sourceKey, args...)
}
func StringSource(sourceKey string, r *slog.Record) slog.Attr {
fs := runtime.CallersFrames([]uintptr{r.PC})
f, _ := fs.Next()
return slog.String(sourceKey, fmt.Sprintf("%s:%d (%s)", f.File, f.Line, f.Function))
}
func FindAttribute(attrs []slog.Attr, groups []string, key string) (slog.Attr, bool) {
// group traversal
if len(groups) > 0 {
for _, attr := range attrs {
if attr.Value.Kind() == slog.KindGroup && attr.Key == groups[0] {
attr, found := FindAttribute(attr.Value.Group(), groups[1:], key)
if found {
return attr, true
}
}
}
return slog.Attr{}, false
}
// starting here, groups is empty
for _, attr := range attrs {
if attr.Key == key {
return attr, true
}
}
return slog.Attr{}, false
}
func RemoveEmptyAttrs(attrs []slog.Attr) []slog.Attr {
return lo.FilterMap(attrs, func(attr slog.Attr, _ int) (slog.Attr, bool) {
if attr.Key == "" {
return attr, false
}
if attr.Value.Kind() == slog.KindGroup {
values := RemoveEmptyAttrs(attr.Value.Group())
if len(values) == 0 {
return attr, false
}
attr.Value = slog.GroupValue(values...)
return attr, true
}
return attr, !attr.Value.Equal(slog.Value{})
})
}

24
vendor/github.com/samber/slog-common/context.go generated vendored Normal file
View File

@@ -0,0 +1,24 @@
package slogcommon
import (
"context"
"log/slog"
)
func ContextExtractor(ctx context.Context, fns []func(ctx context.Context) []slog.Attr) []slog.Attr {
attrs := []slog.Attr{}
for _, fn := range fns {
attrs = append(attrs, fn(ctx)...)
}
return attrs
}
func ExtractFromContext(keys ...any) func(ctx context.Context) []slog.Attr {
return func(ctx context.Context) []slog.Attr {
attrs := make([]slog.Attr, 0, len(keys))
for _, key := range keys {
attrs = append(attrs, slog.Any(key.(string), ctx.Value(key)))
}
return attrs
}
}

27
vendor/github.com/samber/slog-common/finder.go generated vendored Normal file
View File

@@ -0,0 +1,27 @@
package slogcommon
import "log/slog"
func FindAttrByKey(attrs []slog.Attr, key string) (slog.Attr, bool) {
for i := range attrs {
if attrs[i].Key == key {
return attrs[i], true
}
}
return slog.Attr{}, false
}
func FindAttrByGroupAndKey(attrs []slog.Attr, groups []string, key string) (slog.Attr, bool) {
if len(groups) == 0 {
return FindAttrByKey(attrs, key)
}
for i := range attrs {
if attrs[i].Key == key && attrs[i].Value.Kind() == slog.KindGroup {
return FindAttrByGroupAndKey(attrs[i].Value.Group(), groups[1:], key)
}
}
return slog.Attr{}, false
}

65
vendor/github.com/samber/slog-common/groups.go generated vendored Normal file
View File

@@ -0,0 +1,65 @@
package slogcommon
import (
"log/slog"
"slices"
"github.com/samber/lo"
)
func AppendAttrsToGroup(groups []string, actualAttrs []slog.Attr, newAttrs ...slog.Attr) []slog.Attr {
if len(groups) == 0 {
actualAttrsCopy := make([]slog.Attr, 0, len(actualAttrs)+len(newAttrs))
actualAttrsCopy = append(actualAttrsCopy, actualAttrs...)
actualAttrsCopy = append(actualAttrsCopy, newAttrs...)
return UniqAttrs(actualAttrsCopy)
}
actualAttrs = slices.Clone(actualAttrs)
for i := range actualAttrs {
attr := actualAttrs[i]
if attr.Key == groups[0] && attr.Value.Kind() == slog.KindGroup {
actualAttrs[i] = slog.Group(groups[0], lo.ToAnySlice(AppendAttrsToGroup(groups[1:], attr.Value.Group(), newAttrs...))...)
return actualAttrs
}
}
return UniqAttrs(
append(
actualAttrs,
slog.Group(
groups[0],
lo.ToAnySlice(AppendAttrsToGroup(groups[1:], []slog.Attr{}, newAttrs...))...,
),
),
)
}
// @TODO: should be recursive
func UniqAttrs(attrs []slog.Attr) []slog.Attr {
return uniqByLast(attrs, func(item slog.Attr) string {
return item.Key
})
}
func uniqByLast[T any, U comparable](collection []T, iteratee func(item T) U) []T {
result := make([]T, 0, len(collection))
seen := make(map[U]int, len(collection))
seenIndex := 0
for _, item := range collection {
key := iteratee(item)
if index, ok := seen[key]; ok {
result[index] = item
continue
}
seen[key] = seenIndex
seenIndex++
result = append(result, item)
}
return result
}

38
vendor/github.com/samber/slog-zerolog/v2/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,38 @@
# Created by https://www.toptal.com/developers/gitignore/api/go
# Edit at https://www.toptal.com/developers/gitignore?templates=go
### Go ###
# If you prefer the allow list template instead of the deny list, see community template:
# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
#
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib
# Test binary, built with `go test -c`
*.test
# Output of the go coverage tool, specifically when used with LiteIDE
*.out
# Dependency directories (remove the comment below to include it)
# vendor/
# Go workspace file
go.work
### Go Patch ###
/vendor/
/Godeps/
# End of https://www.toptal.com/developers/gitignore/api/go
cover.out
cover.html
.vscode
.idea/

21
vendor/github.com/samber/slog-zerolog/v2/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Samuel Berthe
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

41
vendor/github.com/samber/slog-zerolog/v2/Makefile generated vendored Normal file
View File

@@ -0,0 +1,41 @@
build:
go build -v ./...
test:
go test -race -v ./...
watch-test:
reflex -t 50ms -s -- sh -c 'gotest -race -v ./...'
bench:
go test -benchmem -count 3 -bench ./...
watch-bench:
reflex -t 50ms -s -- sh -c 'go test -benchmem -count 3 -bench ./...'
coverage:
go test -v -coverprofile=cover.out -covermode=atomic ./...
go tool cover -html=cover.out -o cover.html
tools:
go install github.com/cespare/reflex@latest
go install github.com/rakyll/gotest@latest
go install github.com/psampaz/go-mod-outdated@latest
go install github.com/jondot/goweight@latest
go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
go get -t -u golang.org/x/tools/cmd/cover
go install github.com/sonatype-nexus-community/nancy@latest
go mod tidy
lint:
golangci-lint run --timeout 60s --max-same-issues 50 ./...
lint-fix:
golangci-lint run --timeout 60s --max-same-issues 50 --fix ./...
audit:
go list -json -m all | nancy sleuth
outdated:
go list -u -m -json all | go-mod-outdated -update -direct
weight:
goweight

262
vendor/github.com/samber/slog-zerolog/v2/README.md generated vendored Normal file
View File

@@ -0,0 +1,262 @@
# slog: Zerolog handler
[![tag](https://img.shields.io/github/tag/samber/slog-zerolog.svg)](https://github.com/samber/slog-zerolog/releases)
![Go Version](https://img.shields.io/badge/Go-%3E%3D%201.21-%23007d9c)
[![GoDoc](https://godoc.org/github.com/samber/slog-zerolog?status.svg)](https://pkg.go.dev/github.com/samber/slog-zerolog)
![Build Status](https://github.com/samber/slog-zerolog/actions/workflows/test.yml/badge.svg)
[![Go report](https://goreportcard.com/badge/github.com/samber/slog-zerolog)](https://goreportcard.com/report/github.com/samber/slog-zerolog)
[![Coverage](https://img.shields.io/codecov/c/github/samber/slog-zerolog)](https://codecov.io/gh/samber/slog-zerolog)
[![Contributors](https://img.shields.io/github/contributors/samber/slog-zerolog)](https://github.com/samber/slog-zerolog/graphs/contributors)
[![License](https://img.shields.io/github/license/samber/slog-zerolog)](./LICENSE)
A [Zerolog](https://github.com/rs/zerolog) Handler for [slog](https://pkg.go.dev/log/slog) Go library.
<div align="center">
<hr>
<sup><b>Sponsored by:</b></sup>
<br>
<a href="https://www.dash0.com?utm_campaign=148395251-samber%20github%20sponsorship&utm_source=github&utm_medium=sponsorship&utm_content=samber">
<div>
<img src="https://github.com/user-attachments/assets/b1f2e876-0954-4dc3-824d-935d29ba8f3f" width="200" alt="Dash0">
</div>
<div>
100% OpenTelemetry-native observability platform<br>Simple to use, built on open standards, and designed for full cost control
</div>
</a>
<hr>
</div>
**See also:**
- [slog-multi](https://github.com/samber/slog-multi): `slog.Handler` chaining, fanout, routing, failover, load balancing...
- [slog-formatter](https://github.com/samber/slog-formatter): `slog` attribute formatting
- [slog-sampling](https://github.com/samber/slog-sampling): `slog` sampling policy
- [slog-mock](https://github.com/samber/slog-mock): `slog.Handler` for test purposes
**HTTP middlewares:**
- [slog-gin](https://github.com/samber/slog-gin): Gin middleware for `slog` logger
- [slog-echo](https://github.com/samber/slog-echo): Echo middleware for `slog` logger
- [slog-fiber](https://github.com/samber/slog-fiber): Fiber middleware for `slog` logger
- [slog-chi](https://github.com/samber/slog-chi): Chi middleware for `slog` logger
- [slog-http](https://github.com/samber/slog-http): `net/http` middleware for `slog` logger
**Loggers:**
- [slog-zap](https://github.com/samber/slog-zap): A `slog` handler for `Zap`
- [slog-zerolog](https://github.com/samber/slog-zerolog): A `slog` handler for `Zerolog`
- [slog-logrus](https://github.com/samber/slog-logrus): A `slog` handler for `Logrus`
**Log sinks:**
- [slog-datadog](https://github.com/samber/slog-datadog): A `slog` handler for `Datadog`
- [slog-betterstack](https://github.com/samber/slog-betterstack): A `slog` handler for `Betterstack`
- [slog-rollbar](https://github.com/samber/slog-rollbar): A `slog` handler for `Rollbar`
- [slog-loki](https://github.com/samber/slog-loki): A `slog` handler for `Loki`
- [slog-sentry](https://github.com/samber/slog-sentry): A `slog` handler for `Sentry`
- [slog-syslog](https://github.com/samber/slog-syslog): A `slog` handler for `Syslog`
- [slog-logstash](https://github.com/samber/slog-logstash): A `slog` handler for `Logstash`
- [slog-fluentd](https://github.com/samber/slog-fluentd): A `slog` handler for `Fluentd`
- [slog-graylog](https://github.com/samber/slog-graylog): A `slog` handler for `Graylog`
- [slog-quickwit](https://github.com/samber/slog-quickwit): A `slog` handler for `Quickwit`
- [slog-slack](https://github.com/samber/slog-slack): A `slog` handler for `Slack`
- [slog-telegram](https://github.com/samber/slog-telegram): A `slog` handler for `Telegram`
- [slog-mattermost](https://github.com/samber/slog-mattermost): A `slog` handler for `Mattermost`
- [slog-microsoft-teams](https://github.com/samber/slog-microsoft-teams): A `slog` handler for `Microsoft Teams`
- [slog-webhook](https://github.com/samber/slog-webhook): A `slog` handler for `Webhook`
- [slog-kafka](https://github.com/samber/slog-kafka): A `slog` handler for `Kafka`
- [slog-nats](https://github.com/samber/slog-nats): A `slog` handler for `NATS`
- [slog-parquet](https://github.com/samber/slog-parquet): A `slog` handler for `Parquet` + `Object Storage`
- [slog-channel](https://github.com/samber/slog-channel): A `slog` handler for Go channels
## 🚀 Install
```sh
go get github.com/samber/slog-zerolog/v2
```
**Compatibility**: go >= 1.21
No breaking changes will be made to exported APIs before v3.0.0.
## 💡 Usage
GoDoc: [https://pkg.go.dev/github.com/samber/slog-zerolog/v2](https://pkg.go.dev/github.com/samber/slog-zerolog/v2)
### Handler options
```go
type Option struct {
// log level (default: debug)
// you can use ZeroLogLeveler to retrieve the level from the global zerolog instance or a custom one
Level slog.Leveler
// optional: zerolog logger (default: zerolog.Logger)
Logger *zerolog.Logger
// optional: don't add timestamp to record
NoTimestamp bool
// optional: customize json payload builder
Converter Converter
// optional: fetch attributes from context
AttrFromContext []func(ctx context.Context) []slog.Attr
// optional: see slog.HandlerOptions
AddSource bool
ReplaceAttr func(groups []string, a slog.Attr) slog.Attr
}
```
Other global parameters:
```go
slogzerolog.SourceKey = "source"
slogzerolog.ErrorKeys = []string{"error", "err"}
slogzerolog.LogLevels = map[slog.Level]zerolog.Level{...}
```
### Example
```go
import (
"github.com/rs/zerolog"
slogzerolog "github.com/samber/slog-zerolog/v2"
"os"
"log/slog"
)
func main() {
zerologLogger := zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr})
logger := slog.New(slogzerolog.Option{Level: slog.LevelDebug, Logger: &zerologLogger}.NewZerologHandler())
logger = logger.
With("environment", "dev").
With("release", "v1.0.0")
// log error
logger.
With("category", "sql").
With("query.statement", "SELECT COUNT(*) FROM users;").
With("query.duration", 1*time.Second).
With("error", fmt.Errorf("could not count users")).
Error("caramba!")
// log user signup
logger.
With(
slog.Group("user",
slog.String("id", "user-123"),
slog.Time("created_at", time.Now()),
),
).
Info("user registration")
}
```
### Tracing
Import the samber/slog-otel library.
```go
import (
slogzerolog "github.com/samber/slog-zerolog"
slogotel "github.com/samber/slog-otel"
"go.opentelemetry.io/otel/sdk/trace"
)
func main() {
tp := trace.NewTracerProvider(
trace.WithSampler(trace.AlwaysSample()),
)
tracer := tp.Tracer("hello/world")
ctx, span := tracer.Start(context.Background(), "foo")
defer span.End()
span.AddEvent("bar")
logger := slog.New(
slogzerolog.Option{
// ...
AttrFromContext: []func(ctx context.Context) []slog.Attr{
slogotel.ExtractOtelAttrFromContext([]string{"tracing"}, "trace_id", "span_id"),
},
}.NewZerologHandler(),
)
logger.ErrorContext(ctx, "a message")
}
```
### Zerolog level mapping
Use the `slogzerolog.ZeroLogLeveler` as `slogzerolog.Option.Level` (`slog.Leveler`) to set the `slog.Level` from
`zerolog.Level`.
Currently following levels are mapped:
| zerolog | slog |
|----------|-----------|
| Trace-N | Debug-1-N |
| Trace | Debug-1 |
| Debug | Debug |
| Info | Info |
| Warn | Warn |
| Error | Error |
| Panic | Error |
| Fatal | Error |
| NoLevel | Info |
| Disabled | Debug-1 |
| * | Info |
```go
import (
"github.com/rs/zerolog"
slogzerolog "github.com/samber/slog-zerolog/v2"
"os"
"log/slog"
)
func main() {
zerologLogger := zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr})
logger := slog.New(slogzerolog.Option{Level: ZeroLogLeveler{&zerologLogger}, Logger: &zerologLogger}.NewZerologHandler())
logger.Trace("caramba!")
}
```
## 🤝 Contributing
- Ping me on twitter [@samuelberthe](https://twitter.com/samuelberthe) (DMs, mentions, whatever :))
- Fork the [project](https://github.com/samber/slog-zerolog)
- Fix [open issues](https://github.com/samber/slog-zerolog/issues) or request new features
Don't hesitate ;)
```bash
# Install some dev dependencies
make tools
# Run tests
make test
# or
make watch-test
```
## 👤 Contributors
![Contributors](https://contrib.rocks/image?repo=samber/slog-zerolog)
## 💫 Show your support
Give a ⭐️ if this project helped you!
[![GitHub Sponsors](https://img.shields.io/github/sponsors/samber?style=for-the-badge)](https://github.com/sponsors/samber)
## 📝 License
Copyright © 2023 [Samuel Berthe](https://github.com/samber).
This project is [MIT](./LICENSE) licensed.

30
vendor/github.com/samber/slog-zerolog/v2/converter.go generated vendored Normal file
View File

@@ -0,0 +1,30 @@
package slogzerolog
import (
"log/slog"
slogcommon "github.com/samber/slog-common"
)
var SourceKey = "source"
var ErrorKeys = []string{"error", "err"}
type Converter func(addSource bool, replaceAttr func(groups []string, a slog.Attr) slog.Attr, loggerAttr []slog.Attr, groups []string, record *slog.Record) map[string]any
func DefaultConverter(addSource bool, replaceAttr func(groups []string, a slog.Attr) slog.Attr, loggerAttr []slog.Attr, groups []string, record *slog.Record) map[string]any {
// aggregate all attributes
attrs := slogcommon.AppendRecordAttrsToAttrs(loggerAttr, groups, record)
// developer formatters
attrs = slogcommon.ReplaceError(attrs, ErrorKeys...)
if addSource {
attrs = append(attrs, slogcommon.Source(SourceKey, record))
}
attrs = slogcommon.ReplaceAttrs(replaceAttr, []string{}, attrs...)
attrs = slogcommon.RemoveEmptyAttrs(attrs)
// handler formatter
output := slogcommon.AttrsToMap(attrs...)
return output
}

109
vendor/github.com/samber/slog-zerolog/v2/handler.go generated vendored Normal file
View File

@@ -0,0 +1,109 @@
package slogzerolog
import (
"context"
"log/slog"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
slogcommon "github.com/samber/slog-common"
)
type Option struct {
// log level (default: debug)
// you can use ZeroLogLeveler to retrieve the level from the global zerolog instance or a custom one
Level slog.Leveler
// optional: zerolog logger (default: zerolog.Logger)
Logger *zerolog.Logger
// optional: don't add timestamp to record
NoTimestamp bool
// optional: customize json payload builder
Converter Converter
// optional: fetch attributes from context
AttrFromContext []func(ctx context.Context) []slog.Attr
// optional: see slog.HandlerOptions
AddSource bool
ReplaceAttr func(groups []string, a slog.Attr) slog.Attr
}
func (o Option) NewZerologHandler() slog.Handler {
if o.Level == nil {
o.Level = slog.LevelDebug
}
if o.Logger == nil {
// should be selected lazily ?
o.Logger = &log.Logger
}
if o.AttrFromContext == nil {
o.AttrFromContext = []func(ctx context.Context) []slog.Attr{}
}
return &ZerologHandler{
option: o,
attrs: []slog.Attr{},
groups: []string{},
}
}
var _ slog.Handler = (*ZerologHandler)(nil)
type ZerologHandler struct {
option Option
attrs []slog.Attr
groups []string
}
func (h *ZerologHandler) Enabled(_ context.Context, level slog.Level) bool {
return level >= h.option.Level.Level()
}
func (h *ZerologHandler) Handle(ctx context.Context, record slog.Record) error {
converter := DefaultConverter
if h.option.Converter != nil {
converter = h.option.Converter
}
level := LogLevels[record.Level]
fromContext := slogcommon.ContextExtractor(ctx, h.option.AttrFromContext)
args := converter(h.option.AddSource, h.option.ReplaceAttr, append(h.attrs, fromContext...), h.groups, &record)
event := h.option.Logger.
WithLevel(level).
Ctx(ctx).
CallerSkipFrame(3)
if !h.option.NoTimestamp {
event.Time(zerolog.TimestampFieldName, record.Time)
}
event.Fields(args).Msg(record.Message)
return nil
}
func (h *ZerologHandler) WithAttrs(attrs []slog.Attr) slog.Handler {
return &ZerologHandler{
option: h.option,
attrs: slogcommon.AppendAttrsToGroup(h.groups, h.attrs, attrs...),
groups: h.groups,
}
}
func (h *ZerologHandler) WithGroup(name string) slog.Handler {
// https://cs.opensource.google/go/x/exp/+/46b07846:slog/handler.go;l=247
if name == "" {
return h
}
return &ZerologHandler{
option: h.option,
attrs: h.attrs,
groups: append(h.groups, name),
}
}

58
vendor/github.com/samber/slog-zerolog/v2/zerolog.go generated vendored Normal file
View File

@@ -0,0 +1,58 @@
package slogzerolog
import (
"log/slog"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
var LogLevels = map[slog.Level]zerolog.Level{
slog.LevelDebug: zerolog.DebugLevel,
slog.LevelInfo: zerolog.InfoLevel,
slog.LevelWarn: zerolog.WarnLevel,
slog.LevelError: zerolog.ErrorLevel,
}
var reverseLogLevels map[zerolog.Level]slog.Level
func init() {
reverseLogLevels = map[zerolog.Level]slog.Level{}
for level, z := range LogLevels {
reverseLogLevels[z] = level
}
}
// ZeroLogLeveler can be used for Option.Level (implements slog.Leveler).
// If no Logger is provided, the global zerolog.Logger is used.
type ZeroLogLeveler struct {
// optional: zerolog logger (default: log.Logger)
Logger *zerolog.Logger
}
var _ slog.Leveler = ZeroLogLeveler{}
func (z ZeroLogLeveler) Level() slog.Level {
var logger = log.Logger
if z.Logger != nil {
logger = *z.Logger
}
zeroLogLevel := logger.GetLevel()
level, ok := reverseLogLevels[zeroLogLevel]
if !ok {
switch zeroLogLevel {
case zerolog.PanicLevel, zerolog.FatalLevel:
return slog.LevelError
case zerolog.NoLevel:
return slog.LevelInfo
case zerolog.Disabled:
return slog.LevelDebug - 1
default:
if zeroLogLevel < zerolog.DebugLevel {
return slog.Level(int(slog.LevelDebug) + int(zeroLogLevel))
}
return slog.LevelInfo
}
}
return level
}

View File

@@ -1,3 +0,0 @@
# Semconv v1.10.0
[![PkgGoDev](https://pkg.go.dev/badge/go.opentelemetry.io/otel/semconv/v1.10.0)](https://pkg.go.dev/go.opentelemetry.io/otel/semconv/v1.10.0)

View File

@@ -1,9 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Package semconv implements OpenTelemetry semantic conventions.
//
// OpenTelemetry semantic conventions are agreed standardized naming
// patterns for OpenTelemetry things. This package represents the conventions
// as of the v1.10.0 version of the OpenTelemetry specification.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.10.0"

View File

@@ -1,9 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package semconv // import "go.opentelemetry.io/otel/semconv/v1.10.0"
const (
// ExceptionEventName is the name of the Span event representing an exception.
ExceptionEventName = "exception"
)

View File

@@ -1,103 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package semconv // import "go.opentelemetry.io/otel/semconv/v1.10.0"
import (
"net/http"
"go.opentelemetry.io/otel/attribute"
"go.opentelemetry.io/otel/codes"
"go.opentelemetry.io/otel/semconv/internal"
"go.opentelemetry.io/otel/trace"
)
// HTTP scheme attributes.
var (
HTTPSchemeHTTP = HTTPSchemeKey.String("http")
HTTPSchemeHTTPS = HTTPSchemeKey.String("https")
)
var sc = &internal.SemanticConventions{
EnduserIDKey: EnduserIDKey,
HTTPClientIPKey: HTTPClientIPKey,
HTTPFlavorKey: HTTPFlavorKey,
HTTPHostKey: HTTPHostKey,
HTTPMethodKey: HTTPMethodKey,
HTTPRequestContentLengthKey: HTTPRequestContentLengthKey,
HTTPRouteKey: HTTPRouteKey,
HTTPSchemeHTTP: HTTPSchemeHTTP,
HTTPSchemeHTTPS: HTTPSchemeHTTPS,
HTTPServerNameKey: HTTPServerNameKey,
HTTPStatusCodeKey: HTTPStatusCodeKey,
HTTPTargetKey: HTTPTargetKey,
HTTPURLKey: HTTPURLKey,
HTTPUserAgentKey: HTTPUserAgentKey,
NetHostIPKey: NetHostIPKey,
NetHostNameKey: NetHostNameKey,
NetHostPortKey: NetHostPortKey,
NetPeerIPKey: NetPeerIPKey,
NetPeerNameKey: NetPeerNameKey,
NetPeerPortKey: NetPeerPortKey,
NetTransportIP: NetTransportIP,
NetTransportOther: NetTransportOther,
NetTransportTCP: NetTransportTCP,
NetTransportUDP: NetTransportUDP,
NetTransportUnix: NetTransportUnix,
}
// NetAttributesFromHTTPRequest generates attributes of the net
// namespace as specified by the OpenTelemetry specification for a
// span. The network parameter is a string that net.Dial function
// from standard library can understand.
func NetAttributesFromHTTPRequest(network string, request *http.Request) []attribute.KeyValue {
return sc.NetAttributesFromHTTPRequest(network, request)
}
// EndUserAttributesFromHTTPRequest generates attributes of the
// enduser namespace as specified by the OpenTelemetry specification
// for a span.
func EndUserAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
return sc.EndUserAttributesFromHTTPRequest(request)
}
// HTTPClientAttributesFromHTTPRequest generates attributes of the
// http namespace as specified by the OpenTelemetry specification for
// a span on the client side.
func HTTPClientAttributesFromHTTPRequest(request *http.Request) []attribute.KeyValue {
return sc.HTTPClientAttributesFromHTTPRequest(request)
}
// HTTPServerMetricAttributesFromHTTPRequest generates low-cardinality attributes
// to be used with server-side HTTP metrics.
func HTTPServerMetricAttributesFromHTTPRequest(serverName string, request *http.Request) []attribute.KeyValue {
return sc.HTTPServerMetricAttributesFromHTTPRequest(serverName, request)
}
// HTTPServerAttributesFromHTTPRequest generates attributes of the
// http namespace as specified by the OpenTelemetry specification for
// a span on the server side. Currently, only basic authentication is
// supported.
func HTTPServerAttributesFromHTTPRequest(serverName, route string, request *http.Request) []attribute.KeyValue {
return sc.HTTPServerAttributesFromHTTPRequest(serverName, route, request)
}
// HTTPAttributesFromHTTPStatusCode generates attributes of the http
// namespace as specified by the OpenTelemetry specification for a
// span.
func HTTPAttributesFromHTTPStatusCode(code int) []attribute.KeyValue {
return sc.HTTPAttributesFromHTTPStatusCode(code)
}
// SpanStatusFromHTTPStatusCode generates a status code and a message
// as specified by the OpenTelemetry specification for a span.
func SpanStatusFromHTTPStatusCode(code int) (codes.Code, string) {
return internal.SpanStatusFromHTTPStatusCode(code)
}
// SpanStatusFromHTTPStatusCodeAndSpanKind generates a status code and a message
// as specified by the OpenTelemetry specification for a span.
// Exclude 4xx for SERVER to set the appropriate status.
func SpanStatusFromHTTPStatusCodeAndSpanKind(code int, spanKind trace.SpanKind) (codes.Code, string) {
return internal.SpanStatusFromHTTPStatusCodeAndSpanKind(code, spanKind)
}

View File

@@ -1,970 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
// Code generated from semantic convention specification. DO NOT EDIT.
package semconv // import "go.opentelemetry.io/otel/semconv/v1.10.0"
import "go.opentelemetry.io/otel/attribute"
// A cloud environment (e.g. GCP, Azure, AWS)
const (
// Name of the cloud provider.
//
// Type: Enum
// Required: No
// Stability: stable
CloudProviderKey = attribute.Key("cloud.provider")
// The cloud account ID the resource is assigned to.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '111111111111', 'opentelemetry'
CloudAccountIDKey = attribute.Key("cloud.account.id")
// The geographical region the resource is running.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'us-central1', 'us-east-1'
// Note: Refer to your provider's docs to see the available regions, for example
// [Alibaba Cloud regions](https://www.alibabacloud.com/help/doc-
// detail/40654.htm), [AWS regions](https://aws.amazon.com/about-aws/global-
// infrastructure/regions_az/), [Azure regions](https://azure.microsoft.com/en-
// us/global-infrastructure/geographies/), [Google Cloud
// regions](https://cloud.google.com/about/locations), or [Tencent Cloud
// regions](https://intl.cloud.tencent.com/document/product/213/6091).
CloudRegionKey = attribute.Key("cloud.region")
// Cloud regions often have multiple, isolated locations known as zones to
// increase availability. Availability zone represents the zone where the resource
// is running.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'us-east-1c'
// Note: Availability zones are called "zones" on Alibaba Cloud and Google Cloud.
CloudAvailabilityZoneKey = attribute.Key("cloud.availability_zone")
// The cloud platform in use.
//
// Type: Enum
// Required: No
// Stability: stable
// Note: The prefix of the service SHOULD match the one specified in
// `cloud.provider`.
CloudPlatformKey = attribute.Key("cloud.platform")
)
var (
// Alibaba Cloud
CloudProviderAlibabaCloud = CloudProviderKey.String("alibaba_cloud")
// Amazon Web Services
CloudProviderAWS = CloudProviderKey.String("aws")
// Microsoft Azure
CloudProviderAzure = CloudProviderKey.String("azure")
// Google Cloud Platform
CloudProviderGCP = CloudProviderKey.String("gcp")
// Tencent Cloud
CloudProviderTencentCloud = CloudProviderKey.String("tencent_cloud")
)
var (
// Alibaba Cloud Elastic Compute Service
CloudPlatformAlibabaCloudECS = CloudPlatformKey.String("alibaba_cloud_ecs")
// Alibaba Cloud Function Compute
CloudPlatformAlibabaCloudFc = CloudPlatformKey.String("alibaba_cloud_fc")
// AWS Elastic Compute Cloud
CloudPlatformAWSEC2 = CloudPlatformKey.String("aws_ec2")
// AWS Elastic Container Service
CloudPlatformAWSECS = CloudPlatformKey.String("aws_ecs")
// AWS Elastic Kubernetes Service
CloudPlatformAWSEKS = CloudPlatformKey.String("aws_eks")
// AWS Lambda
CloudPlatformAWSLambda = CloudPlatformKey.String("aws_lambda")
// AWS Elastic Beanstalk
CloudPlatformAWSElasticBeanstalk = CloudPlatformKey.String("aws_elastic_beanstalk")
// AWS App Runner
CloudPlatformAWSAppRunner = CloudPlatformKey.String("aws_app_runner")
// Azure Virtual Machines
CloudPlatformAzureVM = CloudPlatformKey.String("azure_vm")
// Azure Container Instances
CloudPlatformAzureContainerInstances = CloudPlatformKey.String("azure_container_instances")
// Azure Kubernetes Service
CloudPlatformAzureAKS = CloudPlatformKey.String("azure_aks")
// Azure Functions
CloudPlatformAzureFunctions = CloudPlatformKey.String("azure_functions")
// Azure App Service
CloudPlatformAzureAppService = CloudPlatformKey.String("azure_app_service")
// Google Cloud Compute Engine (GCE)
CloudPlatformGCPComputeEngine = CloudPlatformKey.String("gcp_compute_engine")
// Google Cloud Run
CloudPlatformGCPCloudRun = CloudPlatformKey.String("gcp_cloud_run")
// Google Cloud Kubernetes Engine (GKE)
CloudPlatformGCPKubernetesEngine = CloudPlatformKey.String("gcp_kubernetes_engine")
// Google Cloud Functions (GCF)
CloudPlatformGCPCloudFunctions = CloudPlatformKey.String("gcp_cloud_functions")
// Google Cloud App Engine (GAE)
CloudPlatformGCPAppEngine = CloudPlatformKey.String("gcp_app_engine")
// Tencent Cloud Cloud Virtual Machine (CVM)
CloudPlatformTencentCloudCvm = CloudPlatformKey.String("tencent_cloud_cvm")
// Tencent Cloud Elastic Kubernetes Service (EKS)
CloudPlatformTencentCloudEKS = CloudPlatformKey.String("tencent_cloud_eks")
// Tencent Cloud Serverless Cloud Function (SCF)
CloudPlatformTencentCloudScf = CloudPlatformKey.String("tencent_cloud_scf")
)
// Resources used by AWS Elastic Container Service (ECS).
const (
// The Amazon Resource Name (ARN) of an [ECS container instance](https://docs.aws.
// amazon.com/AmazonECS/latest/developerguide/ECS_instances.html).
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'arn:aws:ecs:us-
// west-1:123456789123:container/32624152-9086-4f0e-acae-1a75b14fe4d9'
AWSECSContainerARNKey = attribute.Key("aws.ecs.container.arn")
// The ARN of an [ECS cluster](https://docs.aws.amazon.com/AmazonECS/latest/develo
// perguide/clusters.html).
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster'
AWSECSClusterARNKey = attribute.Key("aws.ecs.cluster.arn")
// The [launch type](https://docs.aws.amazon.com/AmazonECS/latest/developerguide/l
// aunch_types.html) for an ECS task.
//
// Type: Enum
// Required: No
// Stability: stable
AWSECSLaunchtypeKey = attribute.Key("aws.ecs.launchtype")
// The ARN of an [ECS task definition](https://docs.aws.amazon.com/AmazonECS/lates
// t/developerguide/task_definitions.html).
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'arn:aws:ecs:us-
// west-1:123456789123:task/10838bed-421f-43ef-870a-f43feacbbb5b'
AWSECSTaskARNKey = attribute.Key("aws.ecs.task.arn")
// The task definition family this task definition is a member of.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry-family'
AWSECSTaskFamilyKey = attribute.Key("aws.ecs.task.family")
// The revision for this task definition.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '8', '26'
AWSECSTaskRevisionKey = attribute.Key("aws.ecs.task.revision")
)
var (
// ec2
AWSECSLaunchtypeEC2 = AWSECSLaunchtypeKey.String("ec2")
// fargate
AWSECSLaunchtypeFargate = AWSECSLaunchtypeKey.String("fargate")
)
// Resources used by AWS Elastic Kubernetes Service (EKS).
const (
// The ARN of an EKS cluster.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'arn:aws:ecs:us-west-2:123456789123:cluster/my-cluster'
AWSEKSClusterARNKey = attribute.Key("aws.eks.cluster.arn")
)
// Resources specific to Amazon Web Services.
const (
// The name(s) of the AWS log group(s) an application is writing to.
//
// Type: string[]
// Required: No
// Stability: stable
// Examples: '/aws/lambda/my-function', 'opentelemetry-service'
// Note: Multiple log groups must be supported for cases like multi-container
// applications, where a single application has sidecar containers, and each write
// to their own log group.
AWSLogGroupNamesKey = attribute.Key("aws.log.group.names")
// The Amazon Resource Name(s) (ARN) of the AWS log group(s).
//
// Type: string[]
// Required: No
// Stability: stable
// Examples: 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:*'
// Note: See the [log group ARN format
// documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-
// access-control-overview-cwl.html#CWL_ARN_Format).
AWSLogGroupARNsKey = attribute.Key("aws.log.group.arns")
// The name(s) of the AWS log stream(s) an application is writing to.
//
// Type: string[]
// Required: No
// Stability: stable
// Examples: 'logs/main/10838bed-421f-43ef-870a-f43feacbbb5b'
AWSLogStreamNamesKey = attribute.Key("aws.log.stream.names")
// The ARN(s) of the AWS log stream(s).
//
// Type: string[]
// Required: No
// Stability: stable
// Examples: 'arn:aws:logs:us-west-1:123456789012:log-group:/aws/my/group:log-
// stream:logs/main/10838bed-421f-43ef-870a-f43feacbbb5b'
// Note: See the [log stream ARN format
// documentation](https://docs.aws.amazon.com/AmazonCloudWatch/latest/logs/iam-
// access-control-overview-cwl.html#CWL_ARN_Format). One log group can contain
// several log streams, so these ARNs necessarily identify both a log group and a
// log stream.
AWSLogStreamARNsKey = attribute.Key("aws.log.stream.arns")
)
// A container instance.
const (
// Container name used by container runtime.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry-autoconf'
ContainerNameKey = attribute.Key("container.name")
// Container ID. Usually a UUID, as for example used to [identify Docker
// containers](https://docs.docker.com/engine/reference/run/#container-
// identification). The UUID might be abbreviated.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'a3bf90e006b2'
ContainerIDKey = attribute.Key("container.id")
// The container runtime managing this container.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'docker', 'containerd', 'rkt'
ContainerRuntimeKey = attribute.Key("container.runtime")
// Name of the image the container was built on.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'gcr.io/opentelemetry/operator'
ContainerImageNameKey = attribute.Key("container.image.name")
// Container image tag.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '0.1'
ContainerImageTagKey = attribute.Key("container.image.tag")
)
// The software deployment.
const (
// Name of the [deployment
// environment](https://en.wikipedia.org/wiki/Deployment_environment) (aka
// deployment tier).
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'staging', 'production'
DeploymentEnvironmentKey = attribute.Key("deployment.environment")
)
// The device on which the process represented by this resource is running.
const (
// A unique identifier representing the device
//
// Type: string
// Required: No
// Stability: stable
// Examples: '2ab2916d-a51f-4ac8-80ee-45ac31a28092'
// Note: The device identifier MUST only be defined using the values outlined
// below. This value is not an advertising identifier and MUST NOT be used as
// such. On iOS (Swift or Objective-C), this value MUST be equal to the [vendor id
// entifier](https://developer.apple.com/documentation/uikit/uidevice/1620059-iden
// tifierforvendor). On Android (Java or Kotlin), this value MUST be equal to the
// Firebase Installation ID or a globally unique UUID which is persisted across
// sessions in your application. More information can be found
// [here](https://developer.android.com/training/articles/user-data-ids) on best
// practices and exact implementation details. Caution should be taken when
// storing personal data or anything which can identify a user. GDPR and data
// protection laws may apply, ensure you do your own due diligence.
DeviceIDKey = attribute.Key("device.id")
// The model identifier for the device
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'iPhone3,4', 'SM-G920F'
// Note: It's recommended this value represents a machine readable version of the
// model identifier rather than the market or consumer-friendly name of the
// device.
DeviceModelIdentifierKey = attribute.Key("device.model.identifier")
// The marketing name for the device model
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'iPhone 6s Plus', 'Samsung Galaxy S6'
// Note: It's recommended this value represents a human readable version of the
// device model rather than a machine readable alternative.
DeviceModelNameKey = attribute.Key("device.model.name")
// The name of the device manufacturer
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'Apple', 'Samsung'
// Note: The Android OS provides this field via
// [Build](https://developer.android.com/reference/android/os/Build#MANUFACTURER).
// iOS apps SHOULD hardcode the value `Apple`.
DeviceManufacturerKey = attribute.Key("device.manufacturer")
)
// A serverless instance.
const (
// The name of the single function that this runtime instance executes.
//
// Type: string
// Required: Always
// Stability: stable
// Examples: 'my-function'
// Note: This is the name of the function as configured/deployed on the FaaS
// platform and is usually different from the name of the callback function (which
// may be stored in the
// [`code.namespace`/`code.function`](../../trace/semantic_conventions/span-
// general.md#source-code-attributes) span attributes).
FaaSNameKey = attribute.Key("faas.name")
// The unique ID of the single function that this runtime instance executes.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'arn:aws:lambda:us-west-2:123456789012:function:my-function'
// Note: Depending on the cloud provider, use:
// * **AWS Lambda:** The function
// [ARN](https://docs.aws.amazon.com/general/latest/gr/aws-arns-and-
// namespaces.html).
// Take care not to use the "invoked ARN" directly but replace any
// [alias suffix](https://docs.aws.amazon.com/lambda/latest/dg/configuration-
// aliases.html) with the resolved function version, as the same runtime instance
// may be invokable with multiple
// different aliases.
// * **GCP:** The [URI of the resource](https://cloud.google.com/iam/docs/full-
// resource-names)
// * **Azure:** The [Fully Qualified Resource ID](https://docs.microsoft.com/en-
// us/rest/api/resources/resources/get-by-id).
// On some providers, it may not be possible to determine the full ID at startup,
// which is why this field cannot be made required. For example, on AWS the
// account ID
// part of the ARN is not available without calling another AWS API
// which may be deemed too slow for a short-running lambda function.
// As an alternative, consider setting `faas.id` as a span attribute instead.
FaaSIDKey = attribute.Key("faas.id")
// The immutable version of the function being executed.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '26', 'pinkfroid-00002'
// Note: Depending on the cloud provider and platform, use:
// * **AWS Lambda:** The [function
// version](https://docs.aws.amazon.com/lambda/latest/dg/configuration-
// versions.html)
// (an integer represented as a decimal string).
// * **Google Cloud Run:** The
// [revision](https://cloud.google.com/run/docs/managing/revisions)
// (i.e., the function name plus the revision suffix).
// * **Google Cloud Functions:** The value of the
// [`K_REVISION` environment
// variable](https://cloud.google.com/functions/docs/env-
// var#runtime_environment_variables_set_automatically).
// * **Azure Functions:** Not applicable. Do not set this attribute.
FaaSVersionKey = attribute.Key("faas.version")
// The execution environment ID as a string, that will be potentially reused for
// other invocations to the same function/function version.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '2021/06/28/[$LATEST]2f399eb14537447da05ab2a2e39309de'
// Note: * **AWS Lambda:** Use the (full) log stream name.
FaaSInstanceKey = attribute.Key("faas.instance")
// The amount of memory available to the serverless function in MiB.
//
// Type: int
// Required: No
// Stability: stable
// Examples: 128
// Note: It's recommended to set this attribute since e.g. too little memory can
// easily stop a Java AWS Lambda function from working correctly. On AWS Lambda,
// the environment variable `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` provides this
// information.
FaaSMaxMemoryKey = attribute.Key("faas.max_memory")
)
// A host is defined as a general computing instance.
const (
// Unique host ID. For Cloud, this must be the instance_id assigned by the cloud
// provider.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry-test'
HostIDKey = attribute.Key("host.id")
// Name of the host. On Unix systems, it may contain what the hostname command
// returns, or the fully qualified hostname, or another name specified by the
// user.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry-test'
HostNameKey = attribute.Key("host.name")
// Type of host. For Cloud, this must be the machine type.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'n1-standard-1'
HostTypeKey = attribute.Key("host.type")
// The CPU architecture the host system is running on.
//
// Type: Enum
// Required: No
// Stability: stable
HostArchKey = attribute.Key("host.arch")
// Name of the VM image or OS install the host was instantiated from.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'infra-ami-eks-worker-node-7d4ec78312', 'CentOS-8-x86_64-1905'
HostImageNameKey = attribute.Key("host.image.name")
// VM image ID. For Cloud, this value is from the provider.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'ami-07b06b442921831e5'
HostImageIDKey = attribute.Key("host.image.id")
// The version string of the VM image as defined in [Version
// Attributes](README.md#version-attributes).
//
// Type: string
// Required: No
// Stability: stable
// Examples: '0.1'
HostImageVersionKey = attribute.Key("host.image.version")
)
var (
// AMD64
HostArchAMD64 = HostArchKey.String("amd64")
// ARM32
HostArchARM32 = HostArchKey.String("arm32")
// ARM64
HostArchARM64 = HostArchKey.String("arm64")
// Itanium
HostArchIA64 = HostArchKey.String("ia64")
// 32-bit PowerPC
HostArchPPC32 = HostArchKey.String("ppc32")
// 64-bit PowerPC
HostArchPPC64 = HostArchKey.String("ppc64")
// IBM z/Architecture
HostArchS390x = HostArchKey.String("s390x")
// 32-bit x86
HostArchX86 = HostArchKey.String("x86")
)
// A Kubernetes Cluster.
const (
// The name of the cluster.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry-cluster'
K8SClusterNameKey = attribute.Key("k8s.cluster.name")
)
// A Kubernetes Node object.
const (
// The name of the Node.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'node-1'
K8SNodeNameKey = attribute.Key("k8s.node.name")
// The UID of the Node.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '1eb3a0c6-0477-4080-a9cb-0cb7db65c6a2'
K8SNodeUIDKey = attribute.Key("k8s.node.uid")
)
// A Kubernetes Namespace.
const (
// The name of the namespace that the pod is running in.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'default'
K8SNamespaceNameKey = attribute.Key("k8s.namespace.name")
)
// A Kubernetes Pod object.
const (
// The UID of the Pod.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff'
K8SPodUIDKey = attribute.Key("k8s.pod.uid")
// The name of the Pod.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry-pod-autoconf'
K8SPodNameKey = attribute.Key("k8s.pod.name")
)
// A container in a [PodTemplate](https://kubernetes.io/docs/concepts/workloads/pods/#pod-templates).
const (
// The name of the Container from Pod specification, must be unique within a Pod.
// Container runtime usually uses different globally unique name
// (`container.name`).
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'redis'
K8SContainerNameKey = attribute.Key("k8s.container.name")
// Number of times the container was restarted. This attribute can be used to
// identify a particular container (running or stopped) within a container spec.
//
// Type: int
// Required: No
// Stability: stable
// Examples: 0, 2
K8SContainerRestartCountKey = attribute.Key("k8s.container.restart_count")
)
// A Kubernetes ReplicaSet object.
const (
// The UID of the ReplicaSet.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff'
K8SReplicaSetUIDKey = attribute.Key("k8s.replicaset.uid")
// The name of the ReplicaSet.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry'
K8SReplicaSetNameKey = attribute.Key("k8s.replicaset.name")
)
// A Kubernetes Deployment object.
const (
// The UID of the Deployment.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff'
K8SDeploymentUIDKey = attribute.Key("k8s.deployment.uid")
// The name of the Deployment.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry'
K8SDeploymentNameKey = attribute.Key("k8s.deployment.name")
)
// A Kubernetes StatefulSet object.
const (
// The UID of the StatefulSet.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff'
K8SStatefulSetUIDKey = attribute.Key("k8s.statefulset.uid")
// The name of the StatefulSet.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry'
K8SStatefulSetNameKey = attribute.Key("k8s.statefulset.name")
)
// A Kubernetes DaemonSet object.
const (
// The UID of the DaemonSet.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff'
K8SDaemonSetUIDKey = attribute.Key("k8s.daemonset.uid")
// The name of the DaemonSet.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry'
K8SDaemonSetNameKey = attribute.Key("k8s.daemonset.name")
)
// A Kubernetes Job object.
const (
// The UID of the Job.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff'
K8SJobUIDKey = attribute.Key("k8s.job.uid")
// The name of the Job.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry'
K8SJobNameKey = attribute.Key("k8s.job.name")
)
// A Kubernetes CronJob object.
const (
// The UID of the CronJob.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '275ecb36-5aa8-4c2a-9c47-d8bb681b9aff'
K8SCronJobUIDKey = attribute.Key("k8s.cronjob.uid")
// The name of the CronJob.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry'
K8SCronJobNameKey = attribute.Key("k8s.cronjob.name")
)
// The operating system (OS) on which the process represented by this resource is running.
const (
// The operating system type.
//
// Type: Enum
// Required: Always
// Stability: stable
OSTypeKey = attribute.Key("os.type")
// Human readable (not intended to be parsed) OS version information, like e.g.
// reported by `ver` or `lsb_release -a` commands.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'Microsoft Windows [Version 10.0.18363.778]', 'Ubuntu 18.04.1 LTS'
OSDescriptionKey = attribute.Key("os.description")
// Human readable operating system name.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'iOS', 'Android', 'Ubuntu'
OSNameKey = attribute.Key("os.name")
// The version string of the operating system as defined in [Version
// Attributes](../../resource/semantic_conventions/README.md#version-attributes).
//
// Type: string
// Required: No
// Stability: stable
// Examples: '14.2.1', '18.04.1'
OSVersionKey = attribute.Key("os.version")
)
var (
// Microsoft Windows
OSTypeWindows = OSTypeKey.String("windows")
// Linux
OSTypeLinux = OSTypeKey.String("linux")
// Apple Darwin
OSTypeDarwin = OSTypeKey.String("darwin")
// FreeBSD
OSTypeFreeBSD = OSTypeKey.String("freebsd")
// NetBSD
OSTypeNetBSD = OSTypeKey.String("netbsd")
// OpenBSD
OSTypeOpenBSD = OSTypeKey.String("openbsd")
// DragonFly BSD
OSTypeDragonflyBSD = OSTypeKey.String("dragonflybsd")
// HP-UX (Hewlett Packard Unix)
OSTypeHPUX = OSTypeKey.String("hpux")
// AIX (Advanced Interactive eXecutive)
OSTypeAIX = OSTypeKey.String("aix")
// Oracle Solaris
OSTypeSolaris = OSTypeKey.String("solaris")
// IBM z/OS
OSTypeZOS = OSTypeKey.String("z_os")
)
// An operating system process.
const (
// Process identifier (PID).
//
// Type: int
// Required: No
// Stability: stable
// Examples: 1234
ProcessPIDKey = attribute.Key("process.pid")
// The name of the process executable. On Linux based systems, can be set to the
// `Name` in `proc/[pid]/status`. On Windows, can be set to the base name of
// `GetProcessImageFileNameW`.
//
// Type: string
// Required: See below
// Stability: stable
// Examples: 'otelcol'
ProcessExecutableNameKey = attribute.Key("process.executable.name")
// The full path to the process executable. On Linux based systems, can be set to
// the target of `proc/[pid]/exe`. On Windows, can be set to the result of
// `GetProcessImageFileNameW`.
//
// Type: string
// Required: See below
// Stability: stable
// Examples: '/usr/bin/cmd/otelcol'
ProcessExecutablePathKey = attribute.Key("process.executable.path")
// The command used to launch the process (i.e. the command name). On Linux based
// systems, can be set to the zeroth string in `proc/[pid]/cmdline`. On Windows,
// can be set to the first parameter extracted from `GetCommandLineW`.
//
// Type: string
// Required: See below
// Stability: stable
// Examples: 'cmd/otelcol'
ProcessCommandKey = attribute.Key("process.command")
// The full command used to launch the process as a single string representing the
// full command. On Windows, can be set to the result of `GetCommandLineW`. Do not
// set this if you have to assemble it just for monitoring; use
// `process.command_args` instead.
//
// Type: string
// Required: See below
// Stability: stable
// Examples: 'C:\\cmd\\otecol --config="my directory\\config.yaml"'
ProcessCommandLineKey = attribute.Key("process.command_line")
// All the command arguments (including the command/executable itself) as received
// by the process. On Linux-based systems (and some other Unixoid systems
// supporting procfs), can be set according to the list of null-delimited strings
// extracted from `proc/[pid]/cmdline`. For libc-based executables, this would be
// the full argv vector passed to `main`.
//
// Type: string[]
// Required: See below
// Stability: stable
// Examples: 'cmd/otecol', '--config=config.yaml'
ProcessCommandArgsKey = attribute.Key("process.command_args")
// The username of the user that owns the process.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'root'
ProcessOwnerKey = attribute.Key("process.owner")
)
// The single (language) runtime instance which is monitored.
const (
// The name of the runtime of this process. For compiled native binaries, this
// SHOULD be the name of the compiler.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'OpenJDK Runtime Environment'
ProcessRuntimeNameKey = attribute.Key("process.runtime.name")
// The version of the runtime of this process, as returned by the runtime without
// modification.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '14.0.2'
ProcessRuntimeVersionKey = attribute.Key("process.runtime.version")
// An additional description about the runtime of the process, for example a
// specific vendor customization of the runtime environment.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'Eclipse OpenJ9 Eclipse OpenJ9 VM openj9-0.21.0'
ProcessRuntimeDescriptionKey = attribute.Key("process.runtime.description")
)
// A service instance.
const (
// Logical name of the service.
//
// Type: string
// Required: Always
// Stability: stable
// Examples: 'shoppingcart'
// Note: MUST be the same for all instances of horizontally scaled services. If
// the value was not specified, SDKs MUST fallback to `unknown_service:`
// concatenated with [`process.executable.name`](process.md#process), e.g.
// `unknown_service:bash`. If `process.executable.name` is not available, the
// value MUST be set to `unknown_service`.
ServiceNameKey = attribute.Key("service.name")
// A namespace for `service.name`.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'Shop'
// Note: A string value having a meaning that helps to distinguish a group of
// services, for example the team name that owns a group of services.
// `service.name` is expected to be unique within the same namespace. If
// `service.namespace` is not specified in the Resource then `service.name` is
// expected to be unique for all services that have no explicit namespace defined
// (so the empty/unspecified namespace is simply one more valid namespace). Zero-
// length namespace string is assumed equal to unspecified namespace.
ServiceNamespaceKey = attribute.Key("service.namespace")
// The string ID of the service instance.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '627cc493-f310-47de-96bd-71410b7dec09'
// Note: MUST be unique for each instance of the same
// `service.namespace,service.name` pair (in other words
// `service.namespace,service.name,service.instance.id` triplet MUST be globally
// unique). The ID helps to distinguish instances of the same service that exist
// at the same time (e.g. instances of a horizontally scaled service). It is
// preferable for the ID to be persistent and stay the same for the lifetime of
// the service instance, however it is acceptable that the ID is ephemeral and
// changes during important lifetime events for the service (e.g. service
// restarts). If the service has no inherent unique ID that can be used as the
// value of this attribute it is recommended to generate a random Version 1 or
// Version 4 RFC 4122 UUID (services aiming for reproducible UUIDs may also use
// Version 5, see RFC 4122 for more recommendations).
ServiceInstanceIDKey = attribute.Key("service.instance.id")
// The version string of the service API or implementation.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '2.0.0'
ServiceVersionKey = attribute.Key("service.version")
)
// The telemetry SDK used to capture data recorded by the instrumentation libraries.
const (
// The name of the telemetry SDK as defined above.
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'opentelemetry'
TelemetrySDKNameKey = attribute.Key("telemetry.sdk.name")
// The language of the telemetry SDK.
//
// Type: Enum
// Required: No
// Stability: stable
TelemetrySDKLanguageKey = attribute.Key("telemetry.sdk.language")
// The version string of the telemetry SDK.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '1.2.3'
TelemetrySDKVersionKey = attribute.Key("telemetry.sdk.version")
// The version string of the auto instrumentation agent, if used.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '1.2.3'
TelemetryAutoVersionKey = attribute.Key("telemetry.auto.version")
)
var (
// cpp
TelemetrySDKLanguageCPP = TelemetrySDKLanguageKey.String("cpp")
// dotnet
TelemetrySDKLanguageDotnet = TelemetrySDKLanguageKey.String("dotnet")
// erlang
TelemetrySDKLanguageErlang = TelemetrySDKLanguageKey.String("erlang")
// go
TelemetrySDKLanguageGo = TelemetrySDKLanguageKey.String("go")
// java
TelemetrySDKLanguageJava = TelemetrySDKLanguageKey.String("java")
// nodejs
TelemetrySDKLanguageNodejs = TelemetrySDKLanguageKey.String("nodejs")
// php
TelemetrySDKLanguagePHP = TelemetrySDKLanguageKey.String("php")
// python
TelemetrySDKLanguagePython = TelemetrySDKLanguageKey.String("python")
// ruby
TelemetrySDKLanguageRuby = TelemetrySDKLanguageKey.String("ruby")
// webjs
TelemetrySDKLanguageWebjs = TelemetrySDKLanguageKey.String("webjs")
// swift
TelemetrySDKLanguageSwift = TelemetrySDKLanguageKey.String("swift")
)
// Resource describing the packaged software running the application code. Web engines are typically executed using process.runtime.
const (
// The name of the web engine.
//
// Type: string
// Required: Always
// Stability: stable
// Examples: 'WildFly'
WebEngineNameKey = attribute.Key("webengine.name")
// The version of the web engine.
//
// Type: string
// Required: No
// Stability: stable
// Examples: '21.0.0'
WebEngineVersionKey = attribute.Key("webengine.version")
// Additional description of the web engine (e.g. detailed version and edition
// information).
//
// Type: string
// Required: No
// Stability: stable
// Examples: 'WildFly Full 21.0.0.Final (WildFly Core 13.0.1.Final) - 2.2.2.Final'
WebEngineDescriptionKey = attribute.Key("webengine.description")
)

View File

@@ -1,9 +0,0 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
package semconv // import "go.opentelemetry.io/otel/semconv/v1.10.0"
// SchemaURL is the schema URL that matches the version of the semantic conventions
// that this package defines. Semconv packages starting from v1.4.0 must declare
// non-empty schema URL in the form https://opentelemetry.io/schemas/<version>
const SchemaURL = "https://opentelemetry.io/schemas/1.10.0"

View File

File diff suppressed because it is too large Load Diff

View File

@@ -6,7 +6,7 @@
// Argon2 was selected as the winner of the Password Hashing Competition and can
// be used to derive cryptographic keys from passwords.
//
// For a detailed specification of Argon2 see [1].
// For a detailed specification of Argon2 see [argon2-specs.pdf].
//
// If you aren't sure which function you need, use Argon2id (IDKey) and
// the parameter recommendations for your scenario.
@@ -17,7 +17,7 @@
// It uses data-independent memory access, which is preferred for password
// hashing and password-based key derivation. Argon2i requires more passes over
// memory than Argon2id to protect from trade-off attacks. The recommended
// parameters (taken from [2]) for non-interactive operations are time=3 and to
// parameters (taken from [RFC 9106 Section 7.3]) for non-interactive operations are time=3 and to
// use the maximum available memory.
//
// # Argon2id
@@ -27,11 +27,11 @@
// half of the first iteration over the memory and data-dependent memory access
// for the rest. Argon2id is side-channel resistant and provides better brute-
// force cost savings due to time-memory tradeoffs than Argon2i. The recommended
// parameters for non-interactive operations (taken from [2]) are time=1 and to
// parameters for non-interactive operations (taken from [RFC 9106 Section 7.3]) are time=1 and to
// use the maximum available memory.
//
// [1] https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
// [2] https://tools.ietf.org/html/draft-irtf-cfrg-argon2-03#section-9.3
// [argon2-specs.pdf]: https://github.com/P-H-C/phc-winner-argon2/blob/master/argon2-specs.pdf
// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
package argon2
import (
@@ -59,7 +59,7 @@ const (
//
// key := argon2.Key([]byte("some password"), salt, 3, 32*1024, 4, 32)
//
// The draft RFC recommends[2] time=3, and memory=32*1024 is a sensible number.
// [RFC 9106 Section 7.3] recommends time=3, and memory=32*1024 as a sensible number.
// If using that amount of memory (32 MB) is not possible in some contexts then
// the time parameter can be increased to compensate.
//
@@ -69,6 +69,8 @@ const (
// adjusted to the number of available CPUs. The cost parameters should be
// increased as memory latency and CPU parallelism increases. Remember to get a
// good random salt.
//
// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
return deriveKey(argon2i, password, salt, nil, nil, time, memory, threads, keyLen)
}
@@ -83,7 +85,7 @@ func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint3
//
// key := argon2.IDKey([]byte("some password"), salt, 1, 64*1024, 4, 32)
//
// The draft RFC recommends[2] time=1, and memory=64*1024 is a sensible number.
// [RFC 9106 Section 7.3] recommends time=1, and memory=64*1024 as a sensible number.
// If using that amount of memory (64 MB) is not possible in some contexts then
// the time parameter can be increased to compensate.
//
@@ -93,6 +95,8 @@ func Key(password, salt []byte, time, memory uint32, threads uint8, keyLen uint3
// adjusted to the numbers of available CPUs. The cost parameters should be
// increased as memory latency and CPU parallelism increases. Remember to get a
// good random salt.
//
// [RFC 9106 Section 7.3]: https://www.rfc-editor.org/rfc/rfc9106.html#section-7.3
func IDKey(password, salt []byte, time, memory uint32, threads uint8, keyLen uint32) []byte {
return deriveKey(argon2id, password, salt, nil, nil, time, memory, threads, keyLen)
}

View File

@@ -29,7 +29,7 @@ loop:
MOVD $NUM_ROUNDS, R21
VLD1 (R11), [V30.S4, V31.S4]
// load contants
// load constants
// VLD4R (R10), [V0.S4, V1.S4, V2.S4, V3.S4]
WORD $0x4D60E940

View File

@@ -56,7 +56,10 @@ func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []
ret, out := sliceForAppend(dst, len(plaintext)+16)
if alias.InexactOverlap(out, plaintext) {
panic("chacha20poly1305: invalid buffer overlap")
panic("chacha20poly1305: invalid buffer overlap of output and input")
}
if alias.AnyOverlap(out, additionalData) {
panic("chacha20poly1305: invalid buffer overlap of output and additional data")
}
chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
return ret
@@ -73,7 +76,10 @@ func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) (
ciphertext = ciphertext[:len(ciphertext)-16]
ret, out := sliceForAppend(dst, len(ciphertext))
if alias.InexactOverlap(out, ciphertext) {
panic("chacha20poly1305: invalid buffer overlap")
panic("chacha20poly1305: invalid buffer overlap of output and input")
}
if alias.AnyOverlap(out, additionalData) {
panic("chacha20poly1305: invalid buffer overlap of output and additional data")
}
if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
for i := range out {

View File

@@ -31,7 +31,10 @@ func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []b
ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
ciphertext, tag := out[:len(plaintext)], out[len(plaintext):]
if alias.InexactOverlap(out, plaintext) {
panic("chacha20poly1305: invalid buffer overlap")
panic("chacha20poly1305: invalid buffer overlap of output and input")
}
if alias.AnyOverlap(out, additionalData) {
panic("chacha20poly1305: invalid buffer overlap of output and additional data")
}
var polyKey [32]byte
@@ -67,7 +70,10 @@ func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []
ret, out := sliceForAppend(dst, len(ciphertext))
if alias.InexactOverlap(out, ciphertext) {
panic("chacha20poly1305: invalid buffer overlap")
panic("chacha20poly1305: invalid buffer overlap of output and input")
}
if alias.AnyOverlap(out, additionalData) {
panic("chacha20poly1305: invalid buffer overlap of output and additional data")
}
if !p.Verify(tag) {
for i := range out {

View File

@@ -1,66 +0,0 @@
// Copyright 2014 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package sha3 implements the SHA-3 fixed-output-length hash functions and
// the SHAKE variable-output-length hash functions defined by FIPS-202.
//
// All types in this package also implement [encoding.BinaryMarshaler],
// [encoding.BinaryAppender] and [encoding.BinaryUnmarshaler] to marshal and
// unmarshal the internal state of the hash.
//
// Both types of hash function use the "sponge" construction and the Keccak
// permutation. For a detailed specification see http://keccak.noekeon.org/
//
// # Guidance
//
// If you aren't sure what function you need, use SHAKE256 with at least 64
// bytes of output. The SHAKE instances are faster than the SHA3 instances;
// the latter have to allocate memory to conform to the hash.Hash interface.
//
// If you need a secret-key MAC (message authentication code), prepend the
// secret key to the input, hash with SHAKE256 and read at least 32 bytes of
// output.
//
// # Security strengths
//
// The SHA3-x (x equals 224, 256, 384, or 512) functions have a security
// strength against preimage attacks of x bits. Since they only produce "x"
// bits of output, their collision-resistance is only "x/2" bits.
//
// The SHAKE-256 and -128 functions have a generic security strength of 256 and
// 128 bits against all attacks, provided that at least 2x bits of their output
// is used. Requesting more than 64 or 32 bytes of output, respectively, does
// not increase the collision-resistance of the SHAKE functions.
//
// # The sponge construction
//
// A sponge builds a pseudo-random function from a public pseudo-random
// permutation, by applying the permutation to a state of "rate + capacity"
// bytes, but hiding "capacity" of the bytes.
//
// A sponge starts out with a zero state. To hash an input using a sponge, up
// to "rate" bytes of the input are XORed into the sponge's state. The sponge
// is then "full" and the permutation is applied to "empty" it. This process is
// repeated until all the input has been "absorbed". The input is then padded.
// The digest is "squeezed" from the sponge in the same way, except that output
// is copied out instead of input being XORed in.
//
// A sponge is parameterized by its generic security strength, which is equal
// to half its capacity; capacity + rate is equal to the permutation's width.
// Since the KeccakF-1600 permutation is 1600 bits (200 bytes) wide, this means
// that the security strength of a sponge instance is equal to (1600 - bitrate) / 2.
//
// # Recommendations
//
// The SHAKE functions are recommended for most new uses. They can produce
// output of arbitrary length. SHAKE256, with an output length of at least
// 64 bytes, provides 256-bit security against all attacks. The Keccak team
// recommends it for most applications upgrading from SHA2-512. (NIST chose a
// much stronger, but much slower, sponge instance for SHA3-512.)
//
// The SHA-3 functions are "drop-in" replacements for the SHA-2 functions.
// They produce output of the same length, with the same security strengths
// against all attacks. This means, in particular, that SHA3-256 only has
// 128-bit collision resistance, because its output length is 32 bytes.
package sha3

View File

@@ -2,127 +2,94 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Package sha3 implements the SHA-3 hash algorithms and the SHAKE extendable
// output functions defined in FIPS 202.
//
// Most of this package is a wrapper around the crypto/sha3 package in the
// standard library. The only exception is the legacy Keccak hash functions.
package sha3
// This file provides functions for creating instances of the SHA-3
// and SHAKE hash functions, as well as utility functions for hashing
// bytes.
import (
"crypto"
"crypto/sha3"
"hash"
)
// New224 creates a new SHA3-224 hash.
// Its generic security strength is 224 bits against preimage attacks,
// and 112 bits against collision attacks.
//
// It is a wrapper for the [sha3.New224] function in the standard library.
//
//go:fix inline
func New224() hash.Hash {
return new224()
return sha3.New224()
}
// New256 creates a new SHA3-256 hash.
// Its generic security strength is 256 bits against preimage attacks,
// and 128 bits against collision attacks.
//
// It is a wrapper for the [sha3.New256] function in the standard library.
//
//go:fix inline
func New256() hash.Hash {
return new256()
return sha3.New256()
}
// New384 creates a new SHA3-384 hash.
// Its generic security strength is 384 bits against preimage attacks,
// and 192 bits against collision attacks.
//
// It is a wrapper for the [sha3.New384] function in the standard library.
//
//go:fix inline
func New384() hash.Hash {
return new384()
return sha3.New384()
}
// New512 creates a new SHA3-512 hash.
// Its generic security strength is 512 bits against preimage attacks,
// and 256 bits against collision attacks.
//
// It is a wrapper for the [sha3.New512] function in the standard library.
//
//go:fix inline
func New512() hash.Hash {
return new512()
}
func init() {
crypto.RegisterHash(crypto.SHA3_224, New224)
crypto.RegisterHash(crypto.SHA3_256, New256)
crypto.RegisterHash(crypto.SHA3_384, New384)
crypto.RegisterHash(crypto.SHA3_512, New512)
}
const (
dsbyteSHA3 = 0b00000110
dsbyteKeccak = 0b00000001
dsbyteShake = 0b00011111
dsbyteCShake = 0b00000100
// rateK[c] is the rate in bytes for Keccak[c] where c is the capacity in
// bits. Given the sponge size is 1600 bits, the rate is 1600 - c bits.
rateK256 = (1600 - 256) / 8
rateK448 = (1600 - 448) / 8
rateK512 = (1600 - 512) / 8
rateK768 = (1600 - 768) / 8
rateK1024 = (1600 - 1024) / 8
)
func new224Generic() *state {
return &state{rate: rateK448, outputLen: 28, dsbyte: dsbyteSHA3}
}
func new256Generic() *state {
return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteSHA3}
}
func new384Generic() *state {
return &state{rate: rateK768, outputLen: 48, dsbyte: dsbyteSHA3}
}
func new512Generic() *state {
return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteSHA3}
}
// NewLegacyKeccak256 creates a new Keccak-256 hash.
//
// Only use this function if you require compatibility with an existing cryptosystem
// that uses non-standard padding. All other users should use New256 instead.
func NewLegacyKeccak256() hash.Hash {
return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteKeccak}
}
// NewLegacyKeccak512 creates a new Keccak-512 hash.
//
// Only use this function if you require compatibility with an existing cryptosystem
// that uses non-standard padding. All other users should use New512 instead.
func NewLegacyKeccak512() hash.Hash {
return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteKeccak}
return sha3.New512()
}
// Sum224 returns the SHA3-224 digest of the data.
func Sum224(data []byte) (digest [28]byte) {
h := New224()
h.Write(data)
h.Sum(digest[:0])
return
//
// It is a wrapper for the [sha3.Sum224] function in the standard library.
//
//go:fix inline
func Sum224(data []byte) [28]byte {
return sha3.Sum224(data)
}
// Sum256 returns the SHA3-256 digest of the data.
func Sum256(data []byte) (digest [32]byte) {
h := New256()
h.Write(data)
h.Sum(digest[:0])
return
//
// It is a wrapper for the [sha3.Sum256] function in the standard library.
//
//go:fix inline
func Sum256(data []byte) [32]byte {
return sha3.Sum256(data)
}
// Sum384 returns the SHA3-384 digest of the data.
func Sum384(data []byte) (digest [48]byte) {
h := New384()
h.Write(data)
h.Sum(digest[:0])
return
//
// It is a wrapper for the [sha3.Sum384] function in the standard library.
//
//go:fix inline
func Sum384(data []byte) [48]byte {
return sha3.Sum384(data)
}
// Sum512 returns the SHA3-512 digest of the data.
func Sum512(data []byte) (digest [64]byte) {
h := New512()
h.Write(data)
h.Sum(digest[:0])
return
//
// It is a wrapper for the [sha3.Sum512] function in the standard library.
//
//go:fix inline
func Sum512(data []byte) [64]byte {
return sha3.Sum512(data)
}

View File

@@ -1,23 +0,0 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !gc || purego || !s390x
package sha3
func new224() *state {
return new224Generic()
}
func new256() *state {
return new256Generic()
}
func new384() *state {
return new384Generic()
}
func new512() *state {
return new512Generic()
}

View File

@@ -1,13 +0,0 @@
// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build amd64 && !purego && gc
package sha3
// This function is implemented in keccakf_amd64.s.
//go:noescape
func keccakF1600(a *[25]uint64)

View File

File diff suppressed because it is too large Load Diff

View File

@@ -4,15 +4,46 @@
package sha3
// This implementation is only used for NewLegacyKeccak256 and
// NewLegacyKeccak512, which are not implemented by crypto/sha3.
// All other functions in this package are wrappers around crypto/sha3.
import (
"crypto/subtle"
"encoding/binary"
"errors"
"hash"
"unsafe"
"golang.org/x/sys/cpu"
)
const (
dsbyteKeccak = 0b00000001
// rateK[c] is the rate in bytes for Keccak[c] where c is the capacity in
// bits. Given the sponge size is 1600 bits, the rate is 1600 - c bits.
rateK256 = (1600 - 256) / 8
rateK512 = (1600 - 512) / 8
rateK1024 = (1600 - 1024) / 8
)
// NewLegacyKeccak256 creates a new Keccak-256 hash.
//
// Only use this function if you require compatibility with an existing cryptosystem
// that uses non-standard padding. All other users should use New256 instead.
func NewLegacyKeccak256() hash.Hash {
return &state{rate: rateK512, outputLen: 32, dsbyte: dsbyteKeccak}
}
// NewLegacyKeccak512 creates a new Keccak-512 hash.
//
// Only use this function if you require compatibility with an existing cryptosystem
// that uses non-standard padding. All other users should use New512 instead.
func NewLegacyKeccak512() hash.Hash {
return &state{rate: rateK1024, outputLen: 64, dsbyte: dsbyteKeccak}
}
// spongeDirection indicates the direction bytes are flowing through the sponge.
type spongeDirection int
@@ -173,12 +204,9 @@ func (d *state) Sum(in []byte) []byte {
}
const (
magicSHA3 = "sha\x08"
magicShake = "sha\x09"
magicCShake = "sha\x0a"
magicKeccak = "sha\x0b"
// magic || rate || main state || n || sponge direction
marshaledSize = len(magicSHA3) + 1 + 200 + 1 + 1
marshaledSize = len(magicKeccak) + 1 + 200 + 1 + 1
)
func (d *state) MarshalBinary() ([]byte, error) {
@@ -187,12 +215,6 @@ func (d *state) MarshalBinary() ([]byte, error) {
func (d *state) AppendBinary(b []byte) ([]byte, error) {
switch d.dsbyte {
case dsbyteSHA3:
b = append(b, magicSHA3...)
case dsbyteShake:
b = append(b, magicShake...)
case dsbyteCShake:
b = append(b, magicCShake...)
case dsbyteKeccak:
b = append(b, magicKeccak...)
default:
@@ -210,12 +232,9 @@ func (d *state) UnmarshalBinary(b []byte) error {
return errors.New("sha3: invalid hash state")
}
magic := string(b[:len(magicSHA3)])
b = b[len(magicSHA3):]
magic := string(b[:len(magicKeccak)])
b = b[len(magicKeccak):]
switch {
case magic == magicSHA3 && d.dsbyte == dsbyteSHA3:
case magic == magicShake && d.dsbyte == dsbyteShake:
case magic == magicCShake && d.dsbyte == dsbyteCShake:
case magic == magicKeccak && d.dsbyte == dsbyteKeccak:
default:
return errors.New("sha3: invalid hash state identifier")

View File

@@ -2,10 +2,12 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !amd64 || purego || !gc
package sha3
// This implementation is only used for NewLegacyKeccak256 and
// NewLegacyKeccak512, which are not implemented by crypto/sha3.
// All other functions in this package are wrappers around crypto/sha3.
import "math/bits"
// rc stores the round constants for use in the ι step.

View File

@@ -1,303 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc && !purego
package sha3
// This file contains code for using the 'compute intermediate
// message digest' (KIMD) and 'compute last message digest' (KLMD)
// instructions to compute SHA-3 and SHAKE hashes on IBM Z.
import (
"hash"
"golang.org/x/sys/cpu"
)
// codes represent 7-bit KIMD/KLMD function codes as defined in
// the Principles of Operation.
type code uint64
const (
// function codes for KIMD/KLMD
sha3_224 code = 32
sha3_256 = 33
sha3_384 = 34
sha3_512 = 35
shake_128 = 36
shake_256 = 37
nopad = 0x100
)
// kimd is a wrapper for the 'compute intermediate message digest' instruction.
// src must be a multiple of the rate for the given function code.
//
//go:noescape
func kimd(function code, chain *[200]byte, src []byte)
// klmd is a wrapper for the 'compute last message digest' instruction.
// src padding is handled by the instruction.
//
//go:noescape
func klmd(function code, chain *[200]byte, dst, src []byte)
type asmState struct {
a [200]byte // 1600 bit state
buf []byte // care must be taken to ensure cap(buf) is a multiple of rate
rate int // equivalent to block size
storage [3072]byte // underlying storage for buf
outputLen int // output length for full security
function code // KIMD/KLMD function code
state spongeDirection // whether the sponge is absorbing or squeezing
}
func newAsmState(function code) *asmState {
var s asmState
s.function = function
switch function {
case sha3_224:
s.rate = 144
s.outputLen = 28
case sha3_256:
s.rate = 136
s.outputLen = 32
case sha3_384:
s.rate = 104
s.outputLen = 48
case sha3_512:
s.rate = 72
s.outputLen = 64
case shake_128:
s.rate = 168
s.outputLen = 32
case shake_256:
s.rate = 136
s.outputLen = 64
default:
panic("sha3: unrecognized function code")
}
// limit s.buf size to a multiple of s.rate
s.resetBuf()
return &s
}
func (s *asmState) clone() *asmState {
c := *s
c.buf = c.storage[:len(s.buf):cap(s.buf)]
return &c
}
// copyIntoBuf copies b into buf. It will panic if there is not enough space to
// store all of b.
func (s *asmState) copyIntoBuf(b []byte) {
bufLen := len(s.buf)
s.buf = s.buf[:len(s.buf)+len(b)]
copy(s.buf[bufLen:], b)
}
// resetBuf points buf at storage, sets the length to 0 and sets cap to be a
// multiple of the rate.
func (s *asmState) resetBuf() {
max := (cap(s.storage) / s.rate) * s.rate
s.buf = s.storage[:0:max]
}
// Write (via the embedded io.Writer interface) adds more data to the running hash.
// It never returns an error.
func (s *asmState) Write(b []byte) (int, error) {
if s.state != spongeAbsorbing {
panic("sha3: Write after Read")
}
length := len(b)
for len(b) > 0 {
if len(s.buf) == 0 && len(b) >= cap(s.buf) {
// Hash the data directly and push any remaining bytes
// into the buffer.
remainder := len(b) % s.rate
kimd(s.function, &s.a, b[:len(b)-remainder])
if remainder != 0 {
s.copyIntoBuf(b[len(b)-remainder:])
}
return length, nil
}
if len(s.buf) == cap(s.buf) {
// flush the buffer
kimd(s.function, &s.a, s.buf)
s.buf = s.buf[:0]
}
// copy as much as we can into the buffer
n := len(b)
if len(b) > cap(s.buf)-len(s.buf) {
n = cap(s.buf) - len(s.buf)
}
s.copyIntoBuf(b[:n])
b = b[n:]
}
return length, nil
}
// Read squeezes an arbitrary number of bytes from the sponge.
func (s *asmState) Read(out []byte) (n int, err error) {
// The 'compute last message digest' instruction only stores the digest
// at the first operand (dst) for SHAKE functions.
if s.function != shake_128 && s.function != shake_256 {
panic("sha3: can only call Read for SHAKE functions")
}
n = len(out)
// need to pad if we were absorbing
if s.state == spongeAbsorbing {
s.state = spongeSqueezing
// write hash directly into out if possible
if len(out)%s.rate == 0 {
klmd(s.function, &s.a, out, s.buf) // len(out) may be 0
s.buf = s.buf[:0]
return
}
// write hash into buffer
max := cap(s.buf)
if max > len(out) {
max = (len(out)/s.rate)*s.rate + s.rate
}
klmd(s.function, &s.a, s.buf[:max], s.buf)
s.buf = s.buf[:max]
}
for len(out) > 0 {
// flush the buffer
if len(s.buf) != 0 {
c := copy(out, s.buf)
out = out[c:]
s.buf = s.buf[c:]
continue
}
// write hash directly into out if possible
if len(out)%s.rate == 0 {
klmd(s.function|nopad, &s.a, out, nil)
return
}
// write hash into buffer
s.resetBuf()
if cap(s.buf) > len(out) {
s.buf = s.buf[:(len(out)/s.rate)*s.rate+s.rate]
}
klmd(s.function|nopad, &s.a, s.buf, nil)
}
return
}
// Sum appends the current hash to b and returns the resulting slice.
// It does not change the underlying hash state.
func (s *asmState) Sum(b []byte) []byte {
if s.state != spongeAbsorbing {
panic("sha3: Sum after Read")
}
// Copy the state to preserve the original.
a := s.a
// Hash the buffer. Note that we don't clear it because we
// aren't updating the state.
switch s.function {
case sha3_224, sha3_256, sha3_384, sha3_512:
klmd(s.function, &a, nil, s.buf)
return append(b, a[:s.outputLen]...)
case shake_128, shake_256:
d := make([]byte, s.outputLen, 64)
klmd(s.function, &a, d, s.buf)
return append(b, d[:s.outputLen]...)
default:
panic("sha3: unknown function")
}
}
// Reset resets the Hash to its initial state.
func (s *asmState) Reset() {
for i := range s.a {
s.a[i] = 0
}
s.resetBuf()
s.state = spongeAbsorbing
}
// Size returns the number of bytes Sum will return.
func (s *asmState) Size() int {
return s.outputLen
}
// BlockSize returns the hash's underlying block size.
// The Write method must be able to accept any amount
// of data, but it may operate more efficiently if all writes
// are a multiple of the block size.
func (s *asmState) BlockSize() int {
return s.rate
}
// Clone returns a copy of the ShakeHash in its current state.
func (s *asmState) Clone() ShakeHash {
return s.clone()
}
// new224 returns an assembly implementation of SHA3-224 if available,
// otherwise it returns a generic implementation.
func new224() hash.Hash {
if cpu.S390X.HasSHA3 {
return newAsmState(sha3_224)
}
return new224Generic()
}
// new256 returns an assembly implementation of SHA3-256 if available,
// otherwise it returns a generic implementation.
func new256() hash.Hash {
if cpu.S390X.HasSHA3 {
return newAsmState(sha3_256)
}
return new256Generic()
}
// new384 returns an assembly implementation of SHA3-384 if available,
// otherwise it returns a generic implementation.
func new384() hash.Hash {
if cpu.S390X.HasSHA3 {
return newAsmState(sha3_384)
}
return new384Generic()
}
// new512 returns an assembly implementation of SHA3-512 if available,
// otherwise it returns a generic implementation.
func new512() hash.Hash {
if cpu.S390X.HasSHA3 {
return newAsmState(sha3_512)
}
return new512Generic()
}
// newShake128 returns an assembly implementation of SHAKE-128 if available,
// otherwise it returns a generic implementation.
func newShake128() ShakeHash {
if cpu.S390X.HasSHA3 {
return newAsmState(shake_128)
}
return newShake128Generic()
}
// newShake256 returns an assembly implementation of SHAKE-256 if available,
// otherwise it returns a generic implementation.
func newShake256() ShakeHash {
if cpu.S390X.HasSHA3 {
return newAsmState(shake_256)
}
return newShake256Generic()
}

View File

@@ -1,33 +0,0 @@
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build gc && !purego
#include "textflag.h"
// func kimd(function code, chain *[200]byte, src []byte)
TEXT ·kimd(SB), NOFRAME|NOSPLIT, $0-40
MOVD function+0(FP), R0
MOVD chain+8(FP), R1
LMG src+16(FP), R2, R3 // R2=base, R3=len
continue:
WORD $0xB93E0002 // KIMD --, R2
BVS continue // continue if interrupted
MOVD $0, R0 // reset R0 for pre-go1.8 compilers
RET
// func klmd(function code, chain *[200]byte, dst, src []byte)
TEXT ·klmd(SB), NOFRAME|NOSPLIT, $0-64
// TODO: SHAKE support
MOVD function+0(FP), R0
MOVD chain+8(FP), R1
LMG dst+16(FP), R2, R3 // R2=base, R3=len
LMG src+40(FP), R4, R5 // R4=base, R5=len
continue:
WORD $0xB93F0024 // KLMD R2, R4
BVS continue // continue if interrupted
MOVD $0, R0 // reset R0 for pre-go1.8 compilers
RET

View File

@@ -4,24 +4,10 @@
package sha3
// This file defines the ShakeHash interface, and provides
// functions for creating SHAKE and cSHAKE instances, as well as utility
// functions for hashing bytes to arbitrary-length output.
//
//
// SHAKE implementation is based on FIPS PUB 202 [1]
// cSHAKE implementations is based on NIST SP 800-185 [2]
//
// [1] https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.202.pdf
// [2] https://doi.org/10.6028/NIST.SP.800-185
import (
"bytes"
"encoding/binary"
"errors"
"crypto/sha3"
"hash"
"io"
"math/bits"
)
// ShakeHash defines the interface to hash functions that support
@@ -32,7 +18,7 @@ type ShakeHash interface {
hash.Hash
// Read reads more output from the hash; reading affects the hash's
// state. (ShakeHash.Read is thus very different from Hash.Sum)
// state. (ShakeHash.Read is thus very different from Hash.Sum.)
// It never returns an error, but subsequent calls to Write or Sum
// will panic.
io.Reader
@@ -41,115 +27,18 @@ type ShakeHash interface {
Clone() ShakeHash
}
// cSHAKE specific context
type cshakeState struct {
*state // SHA-3 state context and Read/Write operations
// initBlock is the cSHAKE specific initialization set of bytes. It is initialized
// by newCShake function and stores concatenation of N followed by S, encoded
// by the method specified in 3.3 of [1].
// It is stored here in order for Reset() to be able to put context into
// initial state.
initBlock []byte
}
func bytepad(data []byte, rate int) []byte {
out := make([]byte, 0, 9+len(data)+rate-1)
out = append(out, leftEncode(uint64(rate))...)
out = append(out, data...)
if padlen := rate - len(out)%rate; padlen < rate {
out = append(out, make([]byte, padlen)...)
}
return out
}
func leftEncode(x uint64) []byte {
// Let n be the smallest positive integer for which 2^(8n) > x.
n := (bits.Len64(x) + 7) / 8
if n == 0 {
n = 1
}
// Return n || x with n as a byte and x an n bytes in big-endian order.
b := make([]byte, 9)
binary.BigEndian.PutUint64(b[1:], x)
b = b[9-n-1:]
b[0] = byte(n)
return b
}
func newCShake(N, S []byte, rate, outputLen int, dsbyte byte) ShakeHash {
c := cshakeState{state: &state{rate: rate, outputLen: outputLen, dsbyte: dsbyte}}
c.initBlock = make([]byte, 0, 9+len(N)+9+len(S)) // leftEncode returns max 9 bytes
c.initBlock = append(c.initBlock, leftEncode(uint64(len(N))*8)...)
c.initBlock = append(c.initBlock, N...)
c.initBlock = append(c.initBlock, leftEncode(uint64(len(S))*8)...)
c.initBlock = append(c.initBlock, S...)
c.Write(bytepad(c.initBlock, c.rate))
return &c
}
// Reset resets the hash to initial state.
func (c *cshakeState) Reset() {
c.state.Reset()
c.Write(bytepad(c.initBlock, c.rate))
}
// Clone returns copy of a cSHAKE context within its current state.
func (c *cshakeState) Clone() ShakeHash {
b := make([]byte, len(c.initBlock))
copy(b, c.initBlock)
return &cshakeState{state: c.clone(), initBlock: b}
}
// Clone returns copy of SHAKE context within its current state.
func (c *state) Clone() ShakeHash {
return c.clone()
}
func (c *cshakeState) MarshalBinary() ([]byte, error) {
return c.AppendBinary(make([]byte, 0, marshaledSize+len(c.initBlock)))
}
func (c *cshakeState) AppendBinary(b []byte) ([]byte, error) {
b, err := c.state.AppendBinary(b)
if err != nil {
return nil, err
}
b = append(b, c.initBlock...)
return b, nil
}
func (c *cshakeState) UnmarshalBinary(b []byte) error {
if len(b) <= marshaledSize {
return errors.New("sha3: invalid hash state")
}
if err := c.state.UnmarshalBinary(b[:marshaledSize]); err != nil {
return err
}
c.initBlock = bytes.Clone(b[marshaledSize:])
return nil
}
// NewShake128 creates a new SHAKE128 variable-output-length ShakeHash.
// Its generic security strength is 128 bits against all attacks if at
// least 32 bytes of its output are used.
func NewShake128() ShakeHash {
return newShake128()
return &shakeWrapper{sha3.NewSHAKE128(), 32, false, sha3.NewSHAKE128}
}
// NewShake256 creates a new SHAKE256 variable-output-length ShakeHash.
// Its generic security strength is 256 bits against all attacks if
// at least 64 bytes of its output are used.
func NewShake256() ShakeHash {
return newShake256()
}
func newShake128Generic() *state {
return &state{rate: rateK256, outputLen: 32, dsbyte: dsbyteShake}
}
func newShake256Generic() *state {
return &state{rate: rateK512, outputLen: 64, dsbyte: dsbyteShake}
return &shakeWrapper{sha3.NewSHAKE256(), 64, false, sha3.NewSHAKE256}
}
// NewCShake128 creates a new instance of cSHAKE128 variable-output-length ShakeHash,
@@ -159,10 +48,9 @@ func newShake256Generic() *state {
// computations on same input with different S yield unrelated outputs.
// When N and S are both empty, this is equivalent to NewShake128.
func NewCShake128(N, S []byte) ShakeHash {
if len(N) == 0 && len(S) == 0 {
return NewShake128()
}
return newCShake(N, S, rateK256, 32, dsbyteCShake)
return &shakeWrapper{sha3.NewCSHAKE128(N, S), 32, false, func() *sha3.SHAKE {
return sha3.NewCSHAKE128(N, S)
}}
}
// NewCShake256 creates a new instance of cSHAKE256 variable-output-length ShakeHash,
@@ -172,10 +60,9 @@ func NewCShake128(N, S []byte) ShakeHash {
// computations on same input with different S yield unrelated outputs.
// When N and S are both empty, this is equivalent to NewShake256.
func NewCShake256(N, S []byte) ShakeHash {
if len(N) == 0 && len(S) == 0 {
return NewShake256()
}
return newCShake(N, S, rateK512, 64, dsbyteCShake)
return &shakeWrapper{sha3.NewCSHAKE256(N, S), 64, false, func() *sha3.SHAKE {
return sha3.NewCSHAKE256(N, S)
}}
}
// ShakeSum128 writes an arbitrary-length digest of data into hash.
@@ -191,3 +78,42 @@ func ShakeSum256(hash, data []byte) {
h.Write(data)
h.Read(hash)
}
// shakeWrapper adds the Size, Sum, and Clone methods to a sha3.SHAKE
// to implement the ShakeHash interface.
type shakeWrapper struct {
*sha3.SHAKE
outputLen int
squeezing bool
newSHAKE func() *sha3.SHAKE
}
func (w *shakeWrapper) Read(p []byte) (n int, err error) {
w.squeezing = true
return w.SHAKE.Read(p)
}
func (w *shakeWrapper) Clone() ShakeHash {
s := w.newSHAKE()
b, err := w.MarshalBinary()
if err != nil {
panic(err) // unreachable
}
if err := s.UnmarshalBinary(b); err != nil {
panic(err) // unreachable
}
return &shakeWrapper{s, w.outputLen, w.squeezing, w.newSHAKE}
}
func (w *shakeWrapper) Size() int { return w.outputLen }
func (w *shakeWrapper) Sum(b []byte) []byte {
if w.squeezing {
panic("sha3: Sum after Read")
}
out := make([]byte, w.outputLen)
// Clone the state so that we don't affect future Write calls.
s := w.Clone()
s.Read(out)
return append(b, out...)
}

View File

@@ -1,15 +0,0 @@
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build !gc || purego || !s390x
package sha3
func newShake128() *state {
return newShake128Generic()
}
func newShake256() *state {
return newShake256Generic()
}

View File

@@ -112,7 +112,7 @@ func (r *keyring) Unlock(passphrase []byte) error {
}
// expireKeysLocked removes expired keys from the keyring. If a key was added
// with a lifetimesecs contraint and seconds >= lifetimesecs seconds have
// with a lifetimesecs constraint and seconds >= lifetimesecs seconds have
// elapsed, it is removed. The caller *must* be holding the keyring mutex.
func (r *keyring) expireKeysLocked() {
for _, k := range r.keys {

View File

@@ -792,7 +792,7 @@ func marshalString(to []byte, s []byte) []byte {
return to[len(s):]
}
var bigIntType = reflect.TypeOf((*big.Int)(nil))
var bigIntType = reflect.TypeFor[*big.Int]()
// Decode a packet into its corresponding message.
func decode(packet []byte) (interface{}, error) {

25
vendor/modules.txt vendored
View File

@@ -754,7 +754,7 @@ github.com/google/go-tpm/tpmutil/tbs
# github.com/google/pprof v0.0.0-20250403155104-27863c87afa6
## explicit; go 1.23
github.com/google/pprof/profile
# github.com/google/renameio/v2 v2.0.0
# github.com/google/renameio/v2 v2.0.1
## explicit; go 1.13
github.com/google/renameio/v2
# github.com/google/uuid v1.6.0
@@ -1355,7 +1355,7 @@ github.com/opencloud-eu/icap-client
# github.com/opencloud-eu/libre-graph-api-go v1.0.8-0.20250724122329-41ba6b191e76
## explicit; go 1.18
github.com/opencloud-eu/libre-graph-api-go
# github.com/opencloud-eu/reva/v2 v2.39.3-0.20251113164418-9fd6b6864c10
# github.com/opencloud-eu/reva/v2 v2.39.3-0.20251120114614-1b573c55f789
## explicit; go 1.24.1
github.com/opencloud-eu/reva/v2/cmd/revad/internal/grace
github.com/opencloud-eu/reva/v2/cmd/revad/runtime
@@ -1772,8 +1772,8 @@ github.com/orcaman/concurrent-map
# github.com/oxtoacart/bpool v0.0.0-20190530202638-03653db5a59c
## explicit; go 1.12
github.com/oxtoacart/bpool
# github.com/pablodz/inotifywaitgo v0.0.9
## explicit; go 1.23.1
# github.com/pablodz/inotifywaitgo v0.0.9 => github.com/opencloud-eu/inotifywaitgo v0.0.0-20251111171128-a390bae3c5e9
## explicit; go 1.24.1
github.com/pablodz/inotifywaitgo/inotifywaitgo
# github.com/patrickmn/go-cache v2.1.0+incompatible
## explicit
@@ -1891,6 +1891,18 @@ github.com/russross/blackfriday/v2
## explicit
github.com/rwcarlsen/goexif/exif
github.com/rwcarlsen/goexif/tiff
# github.com/samber/lo v1.51.0
## explicit; go 1.18
github.com/samber/lo
github.com/samber/lo/internal/constraints
github.com/samber/lo/internal/rand
github.com/samber/lo/mutable
# github.com/samber/slog-common v0.19.0
## explicit; go 1.21
github.com/samber/slog-common
# github.com/samber/slog-zerolog/v2 v2.9.0
## explicit; go 1.21
github.com/samber/slog-zerolog/v2
# github.com/segmentio/asm v1.2.0
## explicit; go 1.18
github.com/segmentio/asm/base64
@@ -2292,7 +2304,6 @@ go.opentelemetry.io/otel/internal/global
go.opentelemetry.io/otel/propagation
go.opentelemetry.io/otel/semconv/internal
go.opentelemetry.io/otel/semconv/internal/v4
go.opentelemetry.io/otel/semconv/v1.10.0
go.opentelemetry.io/otel/semconv/v1.20.0
go.opentelemetry.io/otel/semconv/v1.20.0/httpconv
go.opentelemetry.io/otel/semconv/v1.21.0
@@ -2366,7 +2377,7 @@ go.yaml.in/yaml/v2
# go.yaml.in/yaml/v3 v3.0.4
## explicit; go 1.16
go.yaml.in/yaml/v3
# golang.org/x/crypto v0.43.0
# golang.org/x/crypto v0.44.0
## explicit; go 1.24.0
golang.org/x/crypto/argon2
golang.org/x/crypto/bcrypt
@@ -2466,7 +2477,7 @@ golang.org/x/sys/windows/svc/mgr
# golang.org/x/term v0.37.0
## explicit; go 1.24.0
golang.org/x/term
# golang.org/x/text v0.30.0
# golang.org/x/text v0.31.0
## explicit; go 1.24.0
golang.org/x/text/cases
golang.org/x/text/encoding