update deps
This commit is contained in:
parent
301201c8d2
commit
3ef819de2d
311 changed files with 143330 additions and 69305 deletions
14
.github/workflows/workflow.yml
vendored
14
.github/workflows/workflow.yml
vendored
|
|
@ -18,9 +18,9 @@ jobs:
|
|||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-go@v5
|
||||
- uses: extractions/setup-just@v1
|
||||
- uses: extractions/setup-just@e33e0265a09d6d736e2ee1e0eb685ef1de4669ff # v3
|
||||
- name: lint
|
||||
uses: golangci/golangci-lint-action@v6
|
||||
uses: golangci/golangci-lint-action@4afd733a84b1f43292c63897423277bb7f4313a9 # v8.0.0
|
||||
with:
|
||||
version: latest
|
||||
- name: test
|
||||
|
|
@ -31,16 +31,16 @@ jobs:
|
|||
runs-on: self-hosted
|
||||
steps:
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v1
|
||||
uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3.11.1
|
||||
- name: Login to ghcr.io
|
||||
uses: docker/login-action@v3
|
||||
uses: docker/login-action@74a5d142397b4f367a81961eba4e8cd7edddf772 # v3.4.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
uses: docker/metadata-action@902fa8ec7d6ecbf8d84d538b9b233a880e428804 # v5.7.0
|
||||
with:
|
||||
images: |
|
||||
ghcr.io/${{ github.repository }}
|
||||
|
|
@ -49,7 +49,7 @@ jobs:
|
|||
type=raw,value=latest,enable=${{ github.ref_name == 'main' }}
|
||||
type=semver,pattern={{raw}}
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v6
|
||||
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6.18.0
|
||||
with:
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
|
|
@ -66,7 +66,7 @@ jobs:
|
|||
fetch-depth: 0
|
||||
- uses: actions/setup-go@v5
|
||||
- name: Release
|
||||
uses: goreleaser/goreleaser-action@v6
|
||||
uses: goreleaser/goreleaser-action@9c156ee8a17a598857849441385a2041ef570552 # v6.3.0
|
||||
with:
|
||||
version: latest
|
||||
args: release --clean
|
||||
|
|
|
|||
196
.golangci.yml
196
.golangci.yml
|
|
@ -1,73 +1,16 @@
|
|||
version: "2"
|
||||
run:
|
||||
concurrency: 4
|
||||
timeout: 30m
|
||||
modules-download-mode: readonly
|
||||
issues-exit-code: 1
|
||||
tests: true
|
||||
build-tags: []
|
||||
modules-download-mode: readonly
|
||||
|
||||
output:
|
||||
formats:
|
||||
- format: colored-line-number
|
||||
print-issued-lines: true
|
||||
print-linter-name: true
|
||||
sort-results: true
|
||||
|
||||
linters-settings:
|
||||
decorder:
|
||||
dec-order:
|
||||
- const
|
||||
- var
|
||||
- type
|
||||
- func
|
||||
dogsled:
|
||||
max-blank-identifiers: 3
|
||||
errcheck:
|
||||
check-type-assertions: true
|
||||
check-blank: true
|
||||
errchkjson:
|
||||
report-no-exported: true
|
||||
exhaustive:
|
||||
check:
|
||||
- switch
|
||||
- map
|
||||
default-signifies-exhaustive: true
|
||||
gocognit:
|
||||
min-complexity: 15
|
||||
nestif:
|
||||
min-complexity: 5
|
||||
gocritic:
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- style
|
||||
- performance
|
||||
gofmt:
|
||||
simplify: true
|
||||
rewrite-rules:
|
||||
- pattern: 'interface{}'
|
||||
replacement: 'any'
|
||||
- pattern: 'a[b:len(a)]'
|
||||
replacement: 'a[b:]'
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
grouper:
|
||||
const-require-single-const: true
|
||||
import-require-single-import: true
|
||||
var-require-single-var: true
|
||||
misspell:
|
||||
locale: US
|
||||
usestdlibvars:
|
||||
time-month: true
|
||||
time-layout: true
|
||||
crypto-hash: true
|
||||
default-rpc-path: true
|
||||
sql-isolation-level: true
|
||||
tls-signature-scheme: true
|
||||
constant-kind: true
|
||||
unparam:
|
||||
check-exported: true
|
||||
text:
|
||||
path: stdout
|
||||
print-linter-name: true
|
||||
print-issued-lines: true
|
||||
linters:
|
||||
disable-all: false
|
||||
enable:
|
||||
- asasalint
|
||||
- asciicheck
|
||||
|
|
@ -80,7 +23,6 @@ linters:
|
|||
- dupl
|
||||
- dupword
|
||||
- durationcheck
|
||||
- errcheck
|
||||
- errchkjson
|
||||
- errname
|
||||
- errorlint
|
||||
|
|
@ -89,14 +31,8 @@ linters:
|
|||
- gocognit
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goimports
|
||||
- gosec
|
||||
- gosimple
|
||||
- gosmopolitan
|
||||
- govet
|
||||
- ineffassign
|
||||
- makezero
|
||||
- mirror
|
||||
- misspell
|
||||
|
|
@ -106,36 +42,106 @@ linters:
|
|||
- predeclared
|
||||
- revive
|
||||
- sqlclosecheck
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- usestdlibvars
|
||||
- wastedassign
|
||||
fast: false
|
||||
|
||||
|
||||
settings:
|
||||
decorder:
|
||||
dec-order:
|
||||
- const
|
||||
- var
|
||||
- type
|
||||
- func
|
||||
dogsled:
|
||||
max-blank-identifiers: 3
|
||||
errcheck:
|
||||
check-type-assertions: true
|
||||
check-blank: true
|
||||
errchkjson:
|
||||
report-no-exported: true
|
||||
exhaustive:
|
||||
check:
|
||||
- switch
|
||||
- map
|
||||
default-signifies-exhaustive: true
|
||||
gocognit:
|
||||
min-complexity: 15
|
||||
gocritic:
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- style
|
||||
- performance
|
||||
grouper:
|
||||
const-require-single-const: true
|
||||
import-require-single-import: true
|
||||
var-require-single-var: true
|
||||
misspell:
|
||||
locale: US
|
||||
nestif:
|
||||
min-complexity: 5
|
||||
unparam:
|
||||
check-exported: true
|
||||
usestdlibvars:
|
||||
time-month: true
|
||||
time-layout: true
|
||||
crypto-hash: true
|
||||
default-rpc-path: true
|
||||
sql-isolation-level: true
|
||||
tls-signature-scheme: true
|
||||
constant-kind: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- linters:
|
||||
- dupl
|
||||
- errcheck
|
||||
- gocognit
|
||||
- gocyclo
|
||||
- gosec
|
||||
path: _test\.go
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: 'SA9003:'
|
||||
- linters:
|
||||
- lll
|
||||
source: '^//go:generate '
|
||||
- linters:
|
||||
- revive
|
||||
text: returns unexported type
|
||||
paths:
|
||||
- mocks
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
issues:
|
||||
exclude-dirs-use-default: true
|
||||
exclude-dirs:
|
||||
- mocks
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gocyclo
|
||||
- gocognit
|
||||
- errcheck
|
||||
- dupl
|
||||
- gosec
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "SA9003:"
|
||||
- linters:
|
||||
- lll
|
||||
source: "^//go:generate "
|
||||
- linters:
|
||||
- revive
|
||||
text: "returns unexported type"
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
new: false
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goimports
|
||||
settings:
|
||||
gofmt:
|
||||
simplify: true
|
||||
rewrite-rules:
|
||||
- pattern: interface{}
|
||||
replacement: any
|
||||
- pattern: a[b:len(a)]
|
||||
replacement: a[b:]
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- mocks
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
|
|
|||
47
go.mod
47
go.mod
|
|
@ -1,26 +1,24 @@
|
|||
module github.com/etkecc/postmoogle
|
||||
|
||||
go 1.23.0
|
||||
|
||||
toolchain go1.23.5
|
||||
go 1.24.3
|
||||
|
||||
require (
|
||||
github.com/archdx/zerolog-sentry v1.8.5
|
||||
github.com/emersion/go-msgauth v0.7.0
|
||||
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6
|
||||
github.com/emersion/go-smtp v0.21.3
|
||||
github.com/emersion/go-smtp v0.22.0
|
||||
github.com/etkecc/go-env v1.2.1
|
||||
github.com/etkecc/go-fswatcher v1.0.1
|
||||
github.com/etkecc/go-healthchecks/v2 v2.2.2
|
||||
github.com/etkecc/go-kit v1.6.0
|
||||
github.com/etkecc/go-linkpearl v0.0.0-20250416104026-a65bda9f5f37
|
||||
github.com/etkecc/go-kit v1.7.0
|
||||
github.com/etkecc/go-linkpearl v0.0.0-20250617213914-419fd498ee39
|
||||
github.com/etkecc/go-mxidwc v1.0.1
|
||||
github.com/etkecc/go-secgen v1.4.0
|
||||
github.com/etkecc/go-validator/v2 v2.2.4
|
||||
github.com/fsnotify/fsnotify v1.9.0
|
||||
github.com/gabriel-vasile/mimetype v1.4.9
|
||||
github.com/getsentry/sentry-go v0.32.0
|
||||
github.com/jhillyerd/enmime v1.3.0
|
||||
github.com/getsentry/sentry-go v0.34.0
|
||||
github.com/jhillyerd/enmime/v2 v2.2.0
|
||||
github.com/kvannotten/mailstrip v0.0.0-20200711213611-0002f5c0467e
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/mcnijman/go-emailaddress v1.1.1
|
||||
|
|
@ -28,9 +26,9 @@ require (
|
|||
github.com/raja/argon2pw v1.0.2-0.20210910183755-a391af63bd39
|
||||
github.com/rs/zerolog v1.34.0
|
||||
github.com/swaggo/swag v1.16.3
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0
|
||||
maunium.net/go/mautrix v0.23.3
|
||||
modernc.org/sqlite v1.37.0
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
|
||||
maunium.net/go/mautrix v0.24.1
|
||||
modernc.org/sqlite v1.38.0
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
@ -43,6 +41,7 @@ require (
|
|||
github.com/cention-sany/utf7 v0.0.0-20170124080048-26cad61bd60a // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/etkecc/go-trysmtp v1.1.5 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.19.6 // indirect
|
||||
github.com/go-openapi/spec v0.20.4 // indirect
|
||||
|
|
@ -50,7 +49,7 @@ require (
|
|||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 // indirect
|
||||
github.com/inbucket/html2text v1.0.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
|
|
@ -59,8 +58,10 @@ require (
|
|||
github.com/mattn/go-sqlite3 v1.14.28 // indirect
|
||||
github.com/mikesmitty/edkey v0.0.0-20170222072505-3356ea4e686a // indirect
|
||||
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20250319124200-ccd6737f222a // indirect
|
||||
github.com/olekukonko/errors v1.1.0 // indirect
|
||||
github.com/olekukonko/ll v0.0.9 // indirect
|
||||
github.com/olekukonko/tablewriter v1.0.7 // indirect
|
||||
github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
|
|
@ -69,15 +70,15 @@ require (
|
|||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/yuin/goldmark v1.7.10 // indirect
|
||||
go.mau.fi/util v0.8.6 // indirect
|
||||
golang.org/x/crypto v0.37.0 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/sys v0.32.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/tools v0.32.0 // indirect
|
||||
github.com/yuin/goldmark v1.7.12 // indirect
|
||||
go.mau.fi/util v0.8.8 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
golang.org/x/tools v0.34.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
modernc.org/libc v1.63.0 // indirect
|
||||
modernc.org/libc v1.66.0 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.10.0 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
)
|
||||
|
|
|
|||
118
go.sum
118
go.sum
|
|
@ -25,21 +25,20 @@ github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkp
|
|||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||
github.com/emersion/go-msgauth v0.7.0 h1:vj2hMn6KhFtW41kshIBTXvp6KgYSqpA/ZN9Pv4g1INc=
|
||||
github.com/emersion/go-msgauth v0.7.0/go.mod h1:mmS9I6HkSovrNgq0HNXTeu8l3sRAAuQ9RMvbM4KU7Ck=
|
||||
github.com/emersion/go-sasl v0.0.0-20200509203442-7bfe0ed36a21/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6 h1:oP4q0fw+fOSWn3DfFi4EXdT+B+gTtzx8GC9xsc26Znk=
|
||||
github.com/emersion/go-sasl v0.0.0-20241020182733-b788ff22d5a6/go.mod h1:iL2twTeMvZnrg54ZoPDNfJaJaqy0xIQFuBdrLsmspwQ=
|
||||
github.com/emersion/go-smtp v0.21.3 h1:7uVwagE8iPYE48WhNsng3RRpCUpFvNl39JGNSIyGVMY=
|
||||
github.com/emersion/go-smtp v0.21.3/go.mod h1:qm27SGYgoIPRot6ubfQ/GpiPy/g3PaZAVRxiO/sDUgQ=
|
||||
github.com/emersion/go-smtp v0.22.0 h1:/d3HWxkZZ4riB+0kzfoODh9X+xyCrLEezMnAAa1LEMU=
|
||||
github.com/emersion/go-smtp v0.22.0/go.mod h1:ZtRRkbTyp2XTHCA+BmyTFTrj8xY4I+b4McvHxCU2gsQ=
|
||||
github.com/etkecc/go-env v1.2.1 h1:b/mIa8D1d9hc3rI8h5bEtBHsnKkBZ6UbmYPog3QIPTU=
|
||||
github.com/etkecc/go-env v1.2.1/go.mod h1:yTs1DWEsllAZYA417r4V+OmMuYinGXtBzKkLGNvCv5c=
|
||||
github.com/etkecc/go-fswatcher v1.0.1 h1:n9hqtjzTS3ETb9hcZe1EqYA1lkkhlzZlztu3hXwFDQc=
|
||||
github.com/etkecc/go-fswatcher v1.0.1/go.mod h1:O5TODJ9z6Qb7X+snqHbB+F0Pah6G497Wdg0SFE/UYpE=
|
||||
github.com/etkecc/go-healthchecks/v2 v2.2.2 h1:YV7e+Ga8JY3aZX4Qf6Q1Ca+DnqbT5Drjli3PufB4J1g=
|
||||
github.com/etkecc/go-healthchecks/v2 v2.2.2/go.mod h1:IowWGN4F6By6z0eh63+639OscylgtdvT9ITjfN6hnZA=
|
||||
github.com/etkecc/go-kit v1.6.0 h1:pNbIONvOuRaWuP0F4JLq2ASBxnsCEq5CFY3KeKdhaVY=
|
||||
github.com/etkecc/go-kit v1.6.0/go.mod h1:yikghi8YaYbTjRXNtx82g0LFv90YqZi2vLf5Chw0ysg=
|
||||
github.com/etkecc/go-linkpearl v0.0.0-20250416104026-a65bda9f5f37 h1:ZWUy35kMjyTXTU7cwHyyV1ttrOIBZlg/+z8zmbevj98=
|
||||
github.com/etkecc/go-linkpearl v0.0.0-20250416104026-a65bda9f5f37/go.mod h1:iXvKPwSFgTG9DyEMN9UMuUNeZrZFYlGoBsZZNbiTMdQ=
|
||||
github.com/etkecc/go-kit v1.7.0 h1:jxJ+tWfWwYqgNgfdbZ2va/M0AHyDdKwc8lYGXrFuWsc=
|
||||
github.com/etkecc/go-kit v1.7.0/go.mod h1:yikghi8YaYbTjRXNtx82g0LFv90YqZi2vLf5Chw0ysg=
|
||||
github.com/etkecc/go-linkpearl v0.0.0-20250617213914-419fd498ee39 h1:wZ+Iv47qoBy3jzzVgzS52KkQWcddUuDsO5s59J2q+1g=
|
||||
github.com/etkecc/go-linkpearl v0.0.0-20250617213914-419fd498ee39/go.mod h1:v4QqBXgDZ4Hz235SqyaHde1FL+dNpdnjX4tTR4kxcaI=
|
||||
github.com/etkecc/go-mxidwc v1.0.1 h1:t4Kq3FxSlQjt1i7RpzE5q3cOWjJ0vrTzzGZRSpgh8mg=
|
||||
github.com/etkecc/go-mxidwc v1.0.1/go.mod h1:WFlntcH4mdual/gNi6X7a6rSJERNuLjdrwM3K/tucQA=
|
||||
github.com/etkecc/go-secgen v1.4.0 h1:PapLpen3aIqG2LDu+U6KWDb4SMW2UGuvetpJjd86yUc=
|
||||
|
|
@ -48,12 +47,14 @@ github.com/etkecc/go-trysmtp v1.1.5 h1:MQ4l4bE9nWUqRKoJOalGHGLkb/hRmzbcBMlClufEO
|
|||
github.com/etkecc/go-trysmtp v1.1.5/go.mod h1:J4zQu+kgM37xQY148wUz0Dm6y+YhtzMghAv/AaXt0sQ=
|
||||
github.com/etkecc/go-validator/v2 v2.2.4 h1:E4sE4FpYPVP6VRvnndni+8qUXbeSuHxkniFsi6uCRFQ=
|
||||
github.com/etkecc/go-validator/v2 v2.2.4/go.mod h1:5VX2VICgMALoFBiGc02p/6RjQ8ifRxZ0KOuLAH5fTe0=
|
||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
|
||||
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY=
|
||||
github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok=
|
||||
github.com/getsentry/sentry-go v0.32.0 h1:YKs+//QmwE3DcYtfKRH8/KyOOF/I6Qnx7qYGNHCGmCY=
|
||||
github.com/getsentry/sentry-go v0.32.0/go.mod h1:CYNcMMz73YigoHljQRG+qPF+eMq8gG72XcGN/p71BAY=
|
||||
github.com/getsentry/sentry-go v0.34.0 h1:1FCHBVp8TfSc8L10zqSwXUZNiOSF+10qw4czjarTiY4=
|
||||
github.com/getsentry/sentry-go v0.34.0/go.mod h1:C55omcY9ChRQIUcVcGcs+Zdy4ZpQGvNJ7JYHIoSWOtE=
|
||||
github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA=
|
||||
github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
|
|
@ -66,8 +67,8 @@ github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7
|
|||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM=
|
||||
github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=
|
||||
github.com/go-test/deep v1.1.0 h1:WOcxcdHcvdgThNXjw0t76K42FXTU7HpNQWHpA2HHNlg=
|
||||
github.com/go-test/deep v1.1.0/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
|
||||
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f h1:3BSP1Tbs2djlpprl7wCLuiqMaUh5SJkkzI2gDs+FgLs=
|
||||
github.com/gogs/chardet v0.0.0-20211120154057-b7413eaefb8f/go.mod h1:Pcatq5tYkCW2Q6yrR2VRHlbHpZ/R4/7qyL1TCF7vl14=
|
||||
|
|
@ -79,10 +80,10 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
|||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=
|
||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056 h1:iCHtR9CQyktQ5+f3dMVZfwD2KWJUgm7M0gdL9NGr8KA=
|
||||
github.com/jaytaylor/html2text v0.0.0-20230321000545-74c2419ad056/go.mod h1:CVKlgaMiht+LXvHG173ujK6JUhZXKb2u/BQtjPDIvyk=
|
||||
github.com/jhillyerd/enmime v1.3.0 h1:LV5kzfLidiOr8qRGIpYYmUZCnhrPbcFAnAFUnWn99rw=
|
||||
github.com/jhillyerd/enmime v1.3.0/go.mod h1:6c6jg5HdRRV2FtvVL69LjiX1M8oE0xDX9VEhV3oy4gs=
|
||||
github.com/inbucket/html2text v1.0.0 h1:N5kza++4uBBDJ2Z3KUnTRyPNoBcW+YfOgNiNmNB+sgs=
|
||||
github.com/inbucket/html2text v1.0.0/go.mod h1:5TrhXQKGU+LXurODaSm55Y9eXoPBRnYiOz4x2XfUoJU=
|
||||
github.com/jhillyerd/enmime/v2 v2.2.0 h1:Pe35MB96eZK5Q0XjlvPftOgWypQpd1gcbfJKAt7rsB8=
|
||||
github.com/jhillyerd/enmime/v2 v2.2.0/go.mod h1:SOBXlCemjhiV2DvHhAKnJiWrtJGS/Ffuw4Iy7NjBTaI=
|
||||
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
|
||||
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
|
|
@ -108,7 +109,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
|
|||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A=
|
||||
|
|
@ -122,10 +122,14 @@ github.com/mileusna/crontab v1.2.0/go.mod h1:dbns64w/u3tUnGZGf8pAa76ZqOfeBX4olW4
|
|||
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/petermattis/goid v0.0.0-20250319124200-ccd6737f222a h1:S+AGcmAESQ0pXCUNnRH7V+bOUIgkSX5qVt2cNKCrm0Q=
|
||||
github.com/petermattis/goid v0.0.0-20250319124200-ccd6737f222a/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
|
||||
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
|
||||
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
|
||||
github.com/olekukonko/ll v0.0.9/go.mod h1:En+sEW0JNETl26+K8eZ6/W4UQ7CYSrrgg/EdIYT2H8g=
|
||||
github.com/olekukonko/tablewriter v1.0.7 h1:HCC2e3MM+2g72M81ZcJU11uciw6z/p82aEnm4/ySDGw=
|
||||
github.com/olekukonko/tablewriter v1.0.7/go.mod h1:H428M+HzoUXC6JU2Abj9IT9ooRmdq9CxuDmKMtrOCMs=
|
||||
github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb h1:3PrKuO92dUTMrQ9dx0YNejC6U/Si6jqKmyQ9vWjwqR4=
|
||||
github.com/petermattis/goid v0.0.0-20250508124226-395b08cebbdb/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4=
|
||||
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
|
||||
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
|
|
@ -163,41 +167,41 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
|
|||
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||
github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY=
|
||||
github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28=
|
||||
github.com/yuin/goldmark v1.7.10 h1:S+LrtBjRmqMac2UdtB6yyCEJm+UILZ2fefI4p7o0QpI=
|
||||
github.com/yuin/goldmark v1.7.10/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
go.mau.fi/util v0.8.6 h1:AEK13rfgtiZJL2YsNK+W4ihhYCuukcRom8WPP/w/L54=
|
||||
go.mau.fi/util v0.8.6/go.mod h1:uNB3UTXFbkpp7xL1M/WvQks90B/L4gvbLpbS0603KOE=
|
||||
github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY=
|
||||
github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
|
||||
go.mau.fi/util v0.8.8 h1:OnuEEc/sIJFhnq4kFggiImUpcmnmL/xpvQMRu5Fiy5c=
|
||||
go.mau.fi/util v0.8.8/go.mod h1:Y/kS3loxTEhy8Vill513EtPXr+CRDdae+Xj2BXXMy/c=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE=
|
||||
golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
|
||||
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20180911220305-26e67e76b6c3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610=
|
||||
golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
|
||||
golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.32.0 h1:Q7N1vhpkQv7ybVzLFtTjvQya2ewbwNDZzUgfXGqtMWU=
|
||||
golang.org/x/tools v0.32.0/go.mod h1:ZxrU41P/wAbZD8EDa6dDCa6XfpkhJ7HFMjHJXfBDu8s=
|
||||
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
@ -210,28 +214,30 @@ gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C
|
|||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
maunium.net/go/mautrix v0.23.3 h1:U+fzdcLhFKLUm5gf2+Q0hEUqWkwDMRfvE+paUH9ogSk=
|
||||
maunium.net/go/mautrix v0.23.3/go.mod h1:LX+3evXVKSvh/b43BVC3rkvN2qV7b0bkIV4fY7Snn/4=
|
||||
modernc.org/cc/v4 v4.26.0 h1:QMYvbVduUGH0rrO+5mqF/PSPPRZNpRtg2CLELy7vUpA=
|
||||
modernc.org/cc/v4 v4.26.0/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.26.0 h1:gVzXaDzGeBYJ2uXTOpR8FR7OlksDOe9jxnjhIKCsiTc=
|
||||
modernc.org/ccgo/v4 v4.26.0/go.mod h1:Sem8f7TFUtVXkG2fiaChQtyyfkqhJBg/zjEJBkmuAVY=
|
||||
modernc.org/fileutil v1.3.0 h1:gQ5SIzK3H9kdfai/5x41oQiKValumqNTDXMvKo62HvE=
|
||||
modernc.org/fileutil v1.3.0/go.mod h1:XatxS8fZi3pS8/hKG2GH/ArUogfxjpEKs3Ku3aK4JyQ=
|
||||
maunium.net/go/mautrix v0.24.1 h1:09/xi4qTeA03g1n/DPmmqAlT8Cx4QrgwiPlmLVzA9AU=
|
||||
maunium.net/go/mautrix v0.24.1/go.mod h1:Xy6o+pXmbqmgWsUWh15EQ1eozjC+k/VT/7kloByv9PI=
|
||||
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
|
||||
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
||||
modernc.org/fileutil v1.3.3 h1:3qaU+7f7xxTUmvU1pJTZiDLAIoJVdUSSauJNHg9yXoA=
|
||||
modernc.org/fileutil v1.3.3/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||
modernc.org/libc v1.63.0 h1:wKzb61wOGCzgahQBORb1b0dZonh8Ufzl/7r4Yf1D5YA=
|
||||
modernc.org/libc v1.63.0/go.mod h1:wDzH1mgz1wUIEwottFt++POjGRO9sgyQKrpXaz3x89E=
|
||||
modernc.org/goabi0 v0.0.3 h1:y81b9r3asCh6Xtse6Nz85aYGB0cG3M3U6222yap1KWI=
|
||||
modernc.org/goabi0 v0.0.3/go.mod h1:CEFRnnJhKvWT1c1JTI3Avm+tgOWbkOu5oPA8eH8LnMI=
|
||||
modernc.org/libc v1.66.0 h1:eoFuDb1ozurUY5WSWlgvxHp0FuL+AncMwNjFqGYMJPQ=
|
||||
modernc.org/libc v1.66.0/go.mod h1:AiZxInURfEJx516LqEaFcrC+X38rt9G7+8ojIXQKHbo=
|
||||
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||
modernc.org/memory v1.10.0 h1:fzumd51yQ1DxcOxSO+S6X7+QTuVU+n8/Aj7swYjFfC4=
|
||||
modernc.org/memory v1.10.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||
modernc.org/sqlite v1.37.0 h1:s1TMe7T3Q3ovQiK2Ouz4Jwh7dw4ZDqbebSDTlSJdfjI=
|
||||
modernc.org/sqlite v1.37.0/go.mod h1:5YiWv+YviqGMuGw4V+PNplcyaJ5v+vQd7TQOgkACoJM=
|
||||
modernc.org/sqlite v1.38.0 h1:+4OrfPQ8pxHKuWG4md1JpR/EYAh3Md7TdejuuzE7EUI=
|
||||
modernc.org/sqlite v1.38.0/go.mod h1:1Bj+yES4SVvBZ4cBOpVZ6QgesMCKpJZDq0nxYzOpmNE=
|
||||
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import (
|
|||
|
||||
"github.com/emersion/go-msgauth/dkim"
|
||||
"github.com/etkecc/go-linkpearl"
|
||||
"github.com/jhillyerd/enmime"
|
||||
"github.com/jhillyerd/enmime/v2"
|
||||
"github.com/kvannotten/mailstrip"
|
||||
"maunium.net/go/mautrix/event"
|
||||
"maunium.net/go/mautrix/format"
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import (
|
|||
"github.com/etkecc/go-validator/v2"
|
||||
"github.com/etkecc/postmoogle/internal/email"
|
||||
"github.com/etkecc/postmoogle/internal/utils"
|
||||
"github.com/jhillyerd/enmime"
|
||||
"github.com/jhillyerd/enmime/v2"
|
||||
"github.com/rs/zerolog"
|
||||
"maunium.net/go/mautrix/id"
|
||||
)
|
||||
|
|
|
|||
1
vendor/github.com/emersion/go-smtp/README.md
generated
vendored
1
vendor/github.com/emersion/go-smtp/README.md
generated
vendored
|
|
@ -1,7 +1,6 @@
|
|||
# go-smtp
|
||||
|
||||
[](https://pkg.go.dev/github.com/emersion/go-smtp)
|
||||
[](https://builds.sr.ht/~emersion/go-smtp/commits?)
|
||||
|
||||
An ESMTP client and server library written in Go.
|
||||
|
||||
|
|
|
|||
200
vendor/github.com/emersion/go-smtp/client.go
generated
vendored
200
vendor/github.com/emersion/go-smtp/client.go
generated
vendored
|
|
@ -12,6 +12,7 @@ import (
|
|||
"io"
|
||||
"net"
|
||||
"net/textproto"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
|
@ -81,7 +82,7 @@ func DialTLS(addr string, tlsConfig *tls.Config) (*Client, error) {
|
|||
return client, nil
|
||||
}
|
||||
|
||||
// DialStartTLS retruns a new Client connected to an SMTP server via STARTTLS
|
||||
// DialStartTLS returns a new Client connected to an SMTP server via STARTTLS
|
||||
// at addr. The addr must include a port, as in "mail.example.com:smtp".
|
||||
//
|
||||
// A nil tlsConfig is equivalent to a zero tls.Config.
|
||||
|
|
@ -530,6 +531,9 @@ func (c *Client) Rcpt(to string, opts *RcptOptions) error {
|
|||
fmt.Fprintf(&sb, " ORCPT=%s;%s", string(opts.OriginalRecipientType), enc)
|
||||
}
|
||||
}
|
||||
if _, ok := c.ext["RRVS"]; ok && opts != nil && !opts.RequireRecipientValidSince.IsZero() {
|
||||
sb.WriteString(fmt.Sprintf(" RRVS=%s", opts.RequireRecipientValidSince.Format(time.RFC3339)))
|
||||
}
|
||||
if _, _, err := c.cmd(25, "%s", sb.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
@ -537,85 +541,152 @@ func (c *Client) Rcpt(to string, opts *RcptOptions) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
type dataCloser struct {
|
||||
c *Client
|
||||
io.WriteCloser
|
||||
statusCb func(rcpt string, status *SMTPError)
|
||||
closed bool
|
||||
// DataCommand is a pending DATA command. DataCommand is an io.WriteCloser.
|
||||
// See Client.Data.
|
||||
type DataCommand struct {
|
||||
client *Client
|
||||
wc io.WriteCloser
|
||||
|
||||
closeErr error
|
||||
}
|
||||
|
||||
func (d *dataCloser) Close() error {
|
||||
if d.closed {
|
||||
return fmt.Errorf("smtp: data writer closed twice")
|
||||
var _ io.WriteCloser = (*DataCommand)(nil)
|
||||
|
||||
// Write implements io.Writer.
|
||||
func (cmd *DataCommand) Write(b []byte) (int, error) {
|
||||
return cmd.wc.Write(b)
|
||||
}
|
||||
|
||||
// Close implements io.Closer.
|
||||
func (cmd *DataCommand) Close() error {
|
||||
var err error
|
||||
if cmd.client.lmtp {
|
||||
_, err = cmd.CloseWithLMTPResponse()
|
||||
} else {
|
||||
_, err = cmd.CloseWithResponse()
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// CloseWithResponse is equivalent to Close, but also returns the server
|
||||
// response. It cannot be called when the LMTP protocol is used.
|
||||
//
|
||||
// If server returns an error, it will be of type *SMTPError.
|
||||
func (cmd *DataCommand) CloseWithResponse() (*DataResponse, error) {
|
||||
if cmd.client.lmtp {
|
||||
return nil, errors.New("smtp: CloseWithResponse used with an LMTP client")
|
||||
}
|
||||
|
||||
if err := d.WriteCloser.Close(); err != nil {
|
||||
if err := cmd.close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd.client.conn.SetDeadline(time.Now().Add(cmd.client.SubmissionTimeout))
|
||||
defer cmd.client.conn.SetDeadline(time.Time{})
|
||||
|
||||
_, msg, err := cmd.client.readResponse(250)
|
||||
if err != nil {
|
||||
cmd.closeErr = err
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &DataResponse{StatusText: msg}, nil
|
||||
}
|
||||
|
||||
// CloseWithLMTPResponse is equivalent to Close, but also returns per-recipient
|
||||
// server responses. It can only be called when the LMTP protocol is used.
|
||||
//
|
||||
// If server returns an error, it will be of type LMTPDataError.
|
||||
func (cmd *DataCommand) CloseWithLMTPResponse() (map[string]*DataResponse, error) {
|
||||
if !cmd.client.lmtp {
|
||||
return nil, errors.New("smtp: CloseWithLMTPResponse used without an LMTP client")
|
||||
}
|
||||
|
||||
if err := cmd.close(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
cmd.client.conn.SetDeadline(time.Now().Add(cmd.client.SubmissionTimeout))
|
||||
defer cmd.client.conn.SetDeadline(time.Time{})
|
||||
|
||||
resp := make(map[string]*DataResponse, len(cmd.client.rcpts))
|
||||
lmtpErr := make(LMTPDataError, len(cmd.client.rcpts))
|
||||
for i := 0; i < len(cmd.client.rcpts); i++ {
|
||||
rcpt := cmd.client.rcpts[i]
|
||||
_, msg, err := cmd.client.readResponse(250)
|
||||
if err != nil {
|
||||
if smtpErr, ok := err.(*SMTPError); ok {
|
||||
lmtpErr[rcpt] = smtpErr
|
||||
} else {
|
||||
if len(lmtpErr) > 0 {
|
||||
return resp, errors.Join(err, lmtpErr)
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
} else {
|
||||
resp[rcpt] = &DataResponse{StatusText: msg}
|
||||
}
|
||||
}
|
||||
|
||||
if len(lmtpErr) > 0 {
|
||||
return resp, lmtpErr
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (cmd *DataCommand) close() error {
|
||||
if cmd.closeErr != nil {
|
||||
return cmd.closeErr
|
||||
}
|
||||
|
||||
if err := cmd.wc.Close(); err != nil {
|
||||
cmd.closeErr = err
|
||||
return err
|
||||
}
|
||||
|
||||
d.c.conn.SetDeadline(time.Now().Add(d.c.SubmissionTimeout))
|
||||
defer d.c.conn.SetDeadline(time.Time{})
|
||||
|
||||
expectedResponses := len(d.c.rcpts)
|
||||
if d.c.lmtp {
|
||||
for expectedResponses > 0 {
|
||||
rcpt := d.c.rcpts[len(d.c.rcpts)-expectedResponses]
|
||||
if _, _, err := d.c.readResponse(250); err != nil {
|
||||
if smtpErr, ok := err.(*SMTPError); ok {
|
||||
if d.statusCb != nil {
|
||||
d.statusCb(rcpt, smtpErr)
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
} else if d.statusCb != nil {
|
||||
d.statusCb(rcpt, nil)
|
||||
}
|
||||
expectedResponses--
|
||||
}
|
||||
} else {
|
||||
_, _, err := d.c.readResponse(250)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
d.closed = true
|
||||
cmd.closeErr = errors.New("smtp: data writer closed twice")
|
||||
return nil
|
||||
}
|
||||
|
||||
// DataResponse is the response returned by a DATA command. See
|
||||
// DataCommand.CloseWithResponse.
|
||||
type DataResponse struct {
|
||||
// StatusText is the status text returned by the server. It may contain
|
||||
// tracking information.
|
||||
StatusText string
|
||||
}
|
||||
|
||||
// LMTPDataError is a collection of errors returned by an LMTP server for a
|
||||
// DATA command. It holds per-recipient errors.
|
||||
type LMTPDataError map[string]*SMTPError
|
||||
|
||||
// Error implements error.
|
||||
func (lmtpErr LMTPDataError) Error() string {
|
||||
return errors.Join(lmtpErr.Unwrap()...).Error()
|
||||
}
|
||||
|
||||
// Unwrap returns all per-recipient errors returned by the server.
|
||||
func (lmtpErr LMTPDataError) Unwrap() []error {
|
||||
l := make([]error, 0, len(lmtpErr))
|
||||
for rcpt, smtpErr := range lmtpErr {
|
||||
l = append(l, fmt.Errorf("<%v>: %w", rcpt, smtpErr))
|
||||
}
|
||||
sort.Slice(l, func(i, j int) bool {
|
||||
return l[i].Error() < l[j].Error()
|
||||
})
|
||||
return l
|
||||
}
|
||||
|
||||
// Data issues a DATA command to the server and returns a writer that
|
||||
// can be used to write the mail headers and body. The caller should
|
||||
// close the writer before calling any more methods on c. A call to
|
||||
// Data must be preceded by one or more calls to Rcpt.
|
||||
//
|
||||
// If server returns an error, it will be of type *SMTPError.
|
||||
func (c *Client) Data() (io.WriteCloser, error) {
|
||||
func (c *Client) Data() (*DataCommand, error) {
|
||||
_, _, err := c.cmd(354, "DATA")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dataCloser{c: c, WriteCloser: c.text.DotWriter()}, nil
|
||||
}
|
||||
|
||||
// LMTPData is the LMTP-specific version of the Data method. It accepts a callback
|
||||
// that will be called for each status response received from the server.
|
||||
//
|
||||
// Status callback will receive a SMTPError argument for each negative server
|
||||
// reply and nil for each positive reply. I/O errors will not be reported using
|
||||
// callback and instead will be returned by the Close method of io.WriteCloser.
|
||||
// Callback will be called for each successfull Rcpt call done before in the
|
||||
// same order.
|
||||
func (c *Client) LMTPData(statusCb func(rcpt string, status *SMTPError)) (io.WriteCloser, error) {
|
||||
if !c.lmtp {
|
||||
return nil, errors.New("smtp: not a LMTP client")
|
||||
}
|
||||
|
||||
_, _, err := c.cmd(354, "DATA")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &dataCloser{c: c, WriteCloser: c.text.DotWriter(), statusCb: statusCb}, nil
|
||||
return &DataCommand{client: c, wc: c.text.DotWriter()}, nil
|
||||
}
|
||||
|
||||
// SendMail will use an existing connection to send an email from
|
||||
|
|
@ -784,6 +855,11 @@ func (c *Client) Reset() error {
|
|||
if _, _, err := c.cmd(250, "RSET"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// allow custom HELLO again
|
||||
c.didHello = false
|
||||
c.helloError = nil
|
||||
|
||||
c.rcpts = nil
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
46
vendor/github.com/emersion/go-smtp/conn.go
generated
vendored
46
vendor/github.com/emersion/go-smtp/conn.go
generated
vendored
|
|
@ -229,14 +229,21 @@ func (c *Conn) handleGreet(enhanced bool, arg string) {
|
|||
// NewSession can access it via Conn.Hostname.
|
||||
c.helo = domain
|
||||
|
||||
sess, err := c.server.Backend.NewSession(c)
|
||||
if err != nil {
|
||||
c.helo = ""
|
||||
c.writeError(451, EnhancedCode{4, 0, 0}, err)
|
||||
return
|
||||
}
|
||||
// RFC 5321: "An EHLO command MAY be issued by a client later in the session"
|
||||
if c.session != nil {
|
||||
// RFC 5321: "... the SMTP server MUST clear all buffers
|
||||
// and reset the state exactly as if a RSET command has been issued."
|
||||
c.reset()
|
||||
} else {
|
||||
sess, err := c.server.Backend.NewSession(c)
|
||||
if err != nil {
|
||||
c.helo = ""
|
||||
c.writeError(451, EnhancedCode{4, 0, 0}, err)
|
||||
return
|
||||
}
|
||||
|
||||
c.setSession(sess)
|
||||
c.setSession(sess)
|
||||
}
|
||||
|
||||
if !enhanced {
|
||||
c.writeResponse(250, EnhancedCode{2, 0, 0}, fmt.Sprintf("Hello %s", domain))
|
||||
|
|
@ -284,6 +291,9 @@ func (c *Conn) handleGreet(enhanced bool, arg string) {
|
|||
if c.server.MaxRecipients > 0 {
|
||||
caps = append(caps, fmt.Sprintf("LIMITS RCPTMAX=%v", c.server.MaxRecipients))
|
||||
}
|
||||
if c.server.EnableRRVS {
|
||||
caps = append(caps, "RRVS")
|
||||
}
|
||||
|
||||
args := []string{"Hello " + domain}
|
||||
args = append(args, caps...)
|
||||
|
|
@ -709,6 +719,18 @@ func (c *Conn) handleRcpt(arg string) {
|
|||
}
|
||||
opts.OriginalRecipientType = aType
|
||||
opts.OriginalRecipient = aAddr
|
||||
case "RRVS":
|
||||
if !c.server.EnableRRVS {
|
||||
c.writeResponse(504, EnhancedCode{5, 5, 4}, "RRVS is not implemented")
|
||||
return
|
||||
}
|
||||
value, _, _ = strings.Cut(value, ";") // discard the no-support action
|
||||
rrvsTime, err := time.Parse(time.RFC3339, value)
|
||||
if err != nil {
|
||||
c.writeResponse(501, EnhancedCode{5, 5, 4}, "Malformed RRVS parameter value")
|
||||
return
|
||||
}
|
||||
opts.RequireRecipientValidSince = rrvsTime
|
||||
default:
|
||||
c.writeResponse(500, EnhancedCode{5, 5, 4}, "Unknown RCPT TO argument")
|
||||
return
|
||||
|
|
@ -1229,13 +1251,17 @@ func (c *Conn) writeResponse(code int, enhCode EnhancedCode, text ...string) {
|
|||
}
|
||||
}
|
||||
|
||||
for i := 0; i < len(text)-1; i++ {
|
||||
// transform each single line with \n, into separate lines
|
||||
text = strings.Split(strings.Join(text, "\n"), "\n")
|
||||
|
||||
lastLineIndex := len(text) - 1
|
||||
for i := 0; i < lastLineIndex; i++ {
|
||||
c.text.PrintfLine("%d-%v", code, text[i])
|
||||
}
|
||||
if enhCode == NoEnhancedCode {
|
||||
c.text.PrintfLine("%d %v", code, text[len(text)-1])
|
||||
c.text.PrintfLine("%d %v", code, text[lastLineIndex])
|
||||
} else {
|
||||
c.text.PrintfLine("%d %v.%v.%v %v", code, enhCode[0], enhCode[1], enhCode[2], text[len(text)-1])
|
||||
c.text.PrintfLine("%d %v.%v.%v %v", code, enhCode[0], enhCode[1], enhCode[2], text[lastLineIndex])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
4
vendor/github.com/emersion/go-smtp/server.go
generated
vendored
4
vendor/github.com/emersion/go-smtp/server.go
generated
vendored
|
|
@ -57,6 +57,10 @@ type Server struct {
|
|||
// Should be used only if backend supports it.
|
||||
EnableDSN bool
|
||||
|
||||
// Advertise RRVS (RFC 7293) capability.
|
||||
// Should be used only if backend supports it.
|
||||
EnableRRVS bool
|
||||
|
||||
// The server backend.
|
||||
Backend Backend
|
||||
|
||||
|
|
|
|||
6
vendor/github.com/emersion/go-smtp/smtp.go
generated
vendored
6
vendor/github.com/emersion/go-smtp/smtp.go
generated
vendored
|
|
@ -17,6 +17,8 @@
|
|||
// Additional extensions may be handled by other packages.
|
||||
package smtp
|
||||
|
||||
import "time"
|
||||
|
||||
type BodyType string
|
||||
|
||||
const (
|
||||
|
|
@ -91,4 +93,8 @@ type RcptOptions struct {
|
|||
// Original recipient set by client.
|
||||
OriginalRecipientType DSNAddressType
|
||||
OriginalRecipient string
|
||||
|
||||
// Time value of the RRVS= argument
|
||||
// Left as the zero time if unset.
|
||||
RequireRecipientValidSince time.Time
|
||||
}
|
||||
|
|
|
|||
196
vendor/github.com/etkecc/go-kit/.golangci.yml
generated
vendored
196
vendor/github.com/etkecc/go-kit/.golangci.yml
generated
vendored
|
|
@ -1,73 +1,16 @@
|
|||
version: "2"
|
||||
run:
|
||||
concurrency: 4
|
||||
timeout: 30m
|
||||
modules-download-mode: readonly
|
||||
issues-exit-code: 1
|
||||
tests: true
|
||||
build-tags: []
|
||||
modules-download-mode: readonly
|
||||
|
||||
output:
|
||||
formats:
|
||||
- format: colored-line-number
|
||||
print-issued-lines: true
|
||||
print-linter-name: true
|
||||
sort-results: true
|
||||
|
||||
linters-settings:
|
||||
decorder:
|
||||
dec-order:
|
||||
- const
|
||||
- var
|
||||
- type
|
||||
- func
|
||||
dogsled:
|
||||
max-blank-identifiers: 3
|
||||
errcheck:
|
||||
check-type-assertions: true
|
||||
check-blank: true
|
||||
errchkjson:
|
||||
report-no-exported: true
|
||||
exhaustive:
|
||||
check:
|
||||
- switch
|
||||
- map
|
||||
default-signifies-exhaustive: true
|
||||
gocognit:
|
||||
min-complexity: 15
|
||||
nestif:
|
||||
min-complexity: 5
|
||||
gocritic:
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- style
|
||||
- performance
|
||||
gofmt:
|
||||
simplify: true
|
||||
rewrite-rules:
|
||||
- pattern: 'interface{}'
|
||||
replacement: 'any'
|
||||
- pattern: 'a[b:len(a)]'
|
||||
replacement: 'a[b:]'
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
grouper:
|
||||
const-require-single-const: true
|
||||
import-require-single-import: true
|
||||
var-require-single-var: true
|
||||
misspell:
|
||||
locale: US
|
||||
usestdlibvars:
|
||||
time-month: true
|
||||
time-layout: true
|
||||
crypto-hash: true
|
||||
default-rpc-path: true
|
||||
sql-isolation-level: true
|
||||
tls-signature-scheme: true
|
||||
constant-kind: true
|
||||
unparam:
|
||||
check-exported: true
|
||||
text:
|
||||
path: stdout
|
||||
print-linter-name: true
|
||||
print-issued-lines: true
|
||||
linters:
|
||||
disable-all: false
|
||||
enable:
|
||||
- asasalint
|
||||
- asciicheck
|
||||
|
|
@ -80,7 +23,6 @@ linters:
|
|||
- dupl
|
||||
- dupword
|
||||
- durationcheck
|
||||
- errcheck
|
||||
- errchkjson
|
||||
- errname
|
||||
- errorlint
|
||||
|
|
@ -89,14 +31,8 @@ linters:
|
|||
- gocognit
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goimports
|
||||
- gosec
|
||||
- gosimple
|
||||
- gosmopolitan
|
||||
- govet
|
||||
- ineffassign
|
||||
- makezero
|
||||
- mirror
|
||||
- misspell
|
||||
|
|
@ -106,36 +42,106 @@ linters:
|
|||
- predeclared
|
||||
- revive
|
||||
- sqlclosecheck
|
||||
- staticcheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- usestdlibvars
|
||||
- wastedassign
|
||||
fast: false
|
||||
|
||||
|
||||
settings:
|
||||
decorder:
|
||||
dec-order:
|
||||
- const
|
||||
- var
|
||||
- type
|
||||
- func
|
||||
dogsled:
|
||||
max-blank-identifiers: 3
|
||||
errcheck:
|
||||
check-type-assertions: true
|
||||
check-blank: true
|
||||
errchkjson:
|
||||
report-no-exported: true
|
||||
exhaustive:
|
||||
check:
|
||||
- switch
|
||||
- map
|
||||
default-signifies-exhaustive: true
|
||||
gocognit:
|
||||
min-complexity: 15
|
||||
gocritic:
|
||||
enabled-tags:
|
||||
- diagnostic
|
||||
- style
|
||||
- performance
|
||||
grouper:
|
||||
const-require-single-const: true
|
||||
import-require-single-import: true
|
||||
var-require-single-var: true
|
||||
misspell:
|
||||
locale: US
|
||||
nestif:
|
||||
min-complexity: 5
|
||||
unparam:
|
||||
check-exported: true
|
||||
usestdlibvars:
|
||||
time-month: true
|
||||
time-layout: true
|
||||
crypto-hash: true
|
||||
default-rpc-path: true
|
||||
sql-isolation-level: true
|
||||
tls-signature-scheme: true
|
||||
constant-kind: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- linters:
|
||||
- dupl
|
||||
- errcheck
|
||||
- gocognit
|
||||
- gocyclo
|
||||
- gosec
|
||||
path: _test\.go
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: 'SA9003:'
|
||||
- linters:
|
||||
- lll
|
||||
source: '^//go:generate '
|
||||
- linters:
|
||||
- revive
|
||||
text: returns unexported type
|
||||
paths:
|
||||
- mocks
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
issues:
|
||||
exclude-dirs-use-default: true
|
||||
exclude-dirs:
|
||||
- mocks
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gocyclo
|
||||
- gocognit
|
||||
- errcheck
|
||||
- dupl
|
||||
- gosec
|
||||
- linters:
|
||||
- staticcheck
|
||||
text: "SA9003:"
|
||||
- linters:
|
||||
- lll
|
||||
source: "^//go:generate "
|
||||
- linters:
|
||||
- revive
|
||||
text: "returns unexported type"
|
||||
max-issues-per-linter: 0
|
||||
max-same-issues: 0
|
||||
new: false
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- gofumpt
|
||||
- goimports
|
||||
settings:
|
||||
gofmt:
|
||||
simplify: true
|
||||
rewrite-rules:
|
||||
- pattern: interface{}
|
||||
replacement: any
|
||||
- pattern: a[b:len(a)]
|
||||
replacement: a[b:]
|
||||
gofumpt:
|
||||
extra-rules: true
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- mocks
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
|
|
|||
34
vendor/github.com/etkecc/go-kit/ip.go
generated
vendored
Normal file
34
vendor/github.com/etkecc/go-kit/ip.go
generated
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package kit
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// AnonymizeIP drops the last octet of the IPv4 and IPv6 address to anonymize it
|
||||
func AnonymizeIP(ip string) string {
|
||||
if ip == "" {
|
||||
return ""
|
||||
}
|
||||
parsedIP := net.ParseIP(ip)
|
||||
if parsedIP == nil {
|
||||
return ip // not an ip
|
||||
}
|
||||
|
||||
// IPv4
|
||||
if parsedIP.To4() != nil {
|
||||
ipParts := strings.Split(parsedIP.String(), ".")
|
||||
if len(ipParts) == 4 {
|
||||
ipParts[3] = "0"
|
||||
return strings.Join(ipParts, ".")
|
||||
}
|
||||
}
|
||||
|
||||
// IPv6
|
||||
ipParts := strings.Split(parsedIP.String(), ":")
|
||||
if len(ipParts) > 0 {
|
||||
ipParts[len(ipParts)-1] = "0"
|
||||
return strings.Join(ipParts, ":")
|
||||
}
|
||||
return ip // not an ip
|
||||
}
|
||||
11
vendor/github.com/etkecc/go-kit/strings.go
generated
vendored
11
vendor/github.com/etkecc/go-kit/strings.go
generated
vendored
|
|
@ -63,6 +63,11 @@ func StringToInt(value string, optionalDefaultValue ...int) int {
|
|||
defaultValue = optionalDefaultValue[0]
|
||||
}
|
||||
|
||||
value = strings.TrimSpace(value)
|
||||
if value == "" {
|
||||
return defaultValue
|
||||
}
|
||||
|
||||
vInt, err := strconv.Atoi(value)
|
||||
if err != nil {
|
||||
return defaultValue
|
||||
|
|
@ -80,7 +85,7 @@ func StringToSlice(value string, optionalDefaultValue ...string) []string {
|
|||
|
||||
value = strings.TrimSpace(value)
|
||||
if idx := strings.Index(value, ","); idx == -1 {
|
||||
value = defaultValue
|
||||
return []string{defaultValue}
|
||||
}
|
||||
|
||||
parts := strings.Split(value, ",")
|
||||
|
|
@ -93,6 +98,10 @@ func StringToSlice(value string, optionalDefaultValue ...string) []string {
|
|||
|
||||
// SliceToString converts slice of strings into single string (using strings.Join) with optional hook
|
||||
func SliceToString(slice []string, delimiter string, hook func(string) string) string {
|
||||
if len(slice) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
adjusted := make([]string, 0, len(slice))
|
||||
for _, item := range slice {
|
||||
if hook != nil {
|
||||
|
|
|
|||
4
vendor/github.com/etkecc/go-linkpearl/linkpearl.go
generated
vendored
4
vendor/github.com/etkecc/go-linkpearl/linkpearl.go
generated
vendored
|
|
@ -159,7 +159,7 @@ func (l *Linkpearl) Start(ctx context.Context, optionalStatusMsg ...string) erro
|
|||
|
||||
err := l.api.SetPresence(ctx, mautrix.ReqPresence{Presence: event.PresenceOnline, StatusMsg: statusMsg})
|
||||
if err != nil {
|
||||
l.log.Error().Err(err).Msg("cannot set presence")
|
||||
l.log.Warn().Err(err).Msg("cannot set presence on start")
|
||||
}
|
||||
defer l.Stop(ctx)
|
||||
|
||||
|
|
@ -171,7 +171,7 @@ func (l *Linkpearl) Start(ctx context.Context, optionalStatusMsg ...string) erro
|
|||
func (l *Linkpearl) Stop(ctx context.Context) {
|
||||
l.log.Debug().Msg("stopping the client")
|
||||
if err := l.api.SetPresence(ctx, mautrix.ReqPresence{Presence: event.PresenceOffline}); err != nil {
|
||||
l.log.Error().Err(err).Msg("cannot set presence")
|
||||
l.log.Warn().Err(err).Msg("cannot set presence")
|
||||
}
|
||||
l.api.StopSync()
|
||||
if err := l.ch.Close(); err != nil {
|
||||
|
|
|
|||
20
vendor/github.com/fatih/color/LICENSE.md
generated
vendored
Normal file
20
vendor/github.com/fatih/color/LICENSE.md
generated
vendored
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Fatih Arslan
|
||||
|
||||
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.
|
||||
189
vendor/github.com/fatih/color/README.md
generated
vendored
Normal file
189
vendor/github.com/fatih/color/README.md
generated
vendored
Normal file
|
|
@ -0,0 +1,189 @@
|
|||
# color [](https://github.com/fatih/color/actions) [](https://pkg.go.dev/github.com/fatih/color)
|
||||
|
||||
Color lets you use colorized outputs in terms of [ANSI Escape
|
||||
Codes](http://en.wikipedia.org/wiki/ANSI_escape_code#Colors) in Go (Golang). It
|
||||
has support for Windows too! The API can be used in several ways, pick one that
|
||||
suits you.
|
||||
|
||||

|
||||
|
||||
## Install
|
||||
|
||||
```
|
||||
go get github.com/fatih/color
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Standard colors
|
||||
|
||||
```go
|
||||
// Print with default helper functions
|
||||
color.Cyan("Prints text in cyan.")
|
||||
|
||||
// A newline will be appended automatically
|
||||
color.Blue("Prints %s in blue.", "text")
|
||||
|
||||
// These are using the default foreground colors
|
||||
color.Red("We have red")
|
||||
color.Magenta("And many others ..")
|
||||
|
||||
```
|
||||
|
||||
### RGB colors
|
||||
|
||||
If your terminal supports 24-bit colors, you can use RGB color codes.
|
||||
|
||||
```go
|
||||
color.RGB(255, 128, 0).Println("foreground orange")
|
||||
color.RGB(230, 42, 42).Println("foreground red")
|
||||
|
||||
color.BgRGB(255, 128, 0).Println("background orange")
|
||||
color.BgRGB(230, 42, 42).Println("background red")
|
||||
```
|
||||
|
||||
### Mix and reuse colors
|
||||
|
||||
```go
|
||||
// Create a new color object
|
||||
c := color.New(color.FgCyan).Add(color.Underline)
|
||||
c.Println("Prints cyan text with an underline.")
|
||||
|
||||
// Or just add them to New()
|
||||
d := color.New(color.FgCyan, color.Bold)
|
||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||
|
||||
// Mix up foreground and background colors, create new mixes!
|
||||
red := color.New(color.FgRed)
|
||||
|
||||
boldRed := red.Add(color.Bold)
|
||||
boldRed.Println("This will print text in bold red.")
|
||||
|
||||
whiteBackground := red.Add(color.BgWhite)
|
||||
whiteBackground.Println("Red text with white background.")
|
||||
|
||||
// Mix with RGB color codes
|
||||
color.RGB(255, 128, 0).AddBgRGB(0, 0, 0).Println("orange with black background")
|
||||
|
||||
color.BgRGB(255, 128, 0).AddRGB(255, 255, 255).Println("orange background with white foreground")
|
||||
```
|
||||
|
||||
### Use your own output (io.Writer)
|
||||
|
||||
```go
|
||||
// Use your own io.Writer output
|
||||
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||
|
||||
blue := color.New(color.FgBlue)
|
||||
blue.Fprint(writer, "This will print text in blue.")
|
||||
```
|
||||
|
||||
### Custom print functions (PrintFunc)
|
||||
|
||||
```go
|
||||
// Create a custom print function for convenience
|
||||
red := color.New(color.FgRed).PrintfFunc()
|
||||
red("Warning")
|
||||
red("Error: %s", err)
|
||||
|
||||
// Mix up multiple attributes
|
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||
notice("Don't forget this...")
|
||||
```
|
||||
|
||||
### Custom fprint functions (FprintFunc)
|
||||
|
||||
```go
|
||||
blue := color.New(color.FgBlue).FprintfFunc()
|
||||
blue(myWriter, "important notice: %s", stars)
|
||||
|
||||
// Mix up with multiple attributes
|
||||
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||
success(myWriter, "Don't forget this...")
|
||||
```
|
||||
|
||||
### Insert into noncolor strings (SprintFunc)
|
||||
|
||||
```go
|
||||
// Create SprintXxx functions to mix strings with other non-colorized strings:
|
||||
yellow := color.New(color.FgYellow).SprintFunc()
|
||||
red := color.New(color.FgRed).SprintFunc()
|
||||
fmt.Printf("This is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||
|
||||
info := color.New(color.FgWhite, color.BgGreen).SprintFunc()
|
||||
fmt.Printf("This %s rocks!\n", info("package"))
|
||||
|
||||
// Use helper functions
|
||||
fmt.Println("This", color.RedString("warning"), "should be not neglected.")
|
||||
fmt.Printf("%v %v\n", color.GreenString("Info:"), "an important message.")
|
||||
|
||||
// Windows supported too! Just don't forget to change the output to color.Output
|
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||
```
|
||||
|
||||
### Plug into existing code
|
||||
|
||||
```go
|
||||
// Use handy standard colors
|
||||
color.Set(color.FgYellow)
|
||||
|
||||
fmt.Println("Existing text will now be in yellow")
|
||||
fmt.Printf("This one %s\n", "too")
|
||||
|
||||
color.Unset() // Don't forget to unset
|
||||
|
||||
// You can mix up parameters
|
||||
color.Set(color.FgMagenta, color.Bold)
|
||||
defer color.Unset() // Use it in your function
|
||||
|
||||
fmt.Println("All text will now be bold magenta.")
|
||||
```
|
||||
|
||||
### Disable/Enable color
|
||||
|
||||
There might be a case where you want to explicitly disable/enable color output. the
|
||||
`go-isatty` package will automatically disable color output for non-tty output streams
|
||||
(for example if the output were piped directly to `less`).
|
||||
|
||||
The `color` package also disables color output if the [`NO_COLOR`](https://no-color.org) environment
|
||||
variable is set to a non-empty string.
|
||||
|
||||
`Color` has support to disable/enable colors programmatically both globally and
|
||||
for single color definitions. For example suppose you have a CLI app and a
|
||||
`-no-color` bool flag. You can easily disable the color output with:
|
||||
|
||||
```go
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||
|
||||
if *flagNoColor {
|
||||
color.NoColor = true // disables colorized output
|
||||
}
|
||||
```
|
||||
|
||||
It also has support for single color definitions (local). You can
|
||||
disable/enable color output on the fly:
|
||||
|
||||
```go
|
||||
c := color.New(color.FgCyan)
|
||||
c.Println("Prints cyan text")
|
||||
|
||||
c.DisableColor()
|
||||
c.Println("This is printed without any color")
|
||||
|
||||
c.EnableColor()
|
||||
c.Println("This prints again cyan...")
|
||||
```
|
||||
|
||||
## GitHub Actions
|
||||
|
||||
To output color in GitHub Actions (or other CI systems that support ANSI colors), make sure to set `color.NoColor = false` so that it bypasses the check for non-tty output streams.
|
||||
|
||||
|
||||
## Credits
|
||||
|
||||
* [Fatih Arslan](https://github.com/fatih)
|
||||
* Windows support via @mattn: [colorable](https://github.com/mattn/go-colorable)
|
||||
|
||||
## License
|
||||
|
||||
The MIT License (MIT) - see [`LICENSE.md`](https://github.com/fatih/color/blob/master/LICENSE.md) for more details
|
||||
685
vendor/github.com/fatih/color/color.go
generated
vendored
Normal file
685
vendor/github.com/fatih/color/color.go
generated
vendored
Normal file
|
|
@ -0,0 +1,685 @@
|
|||
package color
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
var (
|
||||
// NoColor defines if the output is colorized or not. It's dynamically set to
|
||||
// false or true based on the stdout's file descriptor referring to a terminal
|
||||
// or not. It's also set to true if the NO_COLOR environment variable is
|
||||
// set (regardless of its value). This is a global option and affects all
|
||||
// colors. For more control over each color block use the methods
|
||||
// DisableColor() individually.
|
||||
NoColor = noColorIsSet() || os.Getenv("TERM") == "dumb" ||
|
||||
(!isatty.IsTerminal(os.Stdout.Fd()) && !isatty.IsCygwinTerminal(os.Stdout.Fd()))
|
||||
|
||||
// Output defines the standard output of the print functions. By default,
|
||||
// os.Stdout is used.
|
||||
Output = colorable.NewColorableStdout()
|
||||
|
||||
// Error defines a color supporting writer for os.Stderr.
|
||||
Error = colorable.NewColorableStderr()
|
||||
|
||||
// colorsCache is used to reduce the count of created Color objects and
|
||||
// allows to reuse already created objects with required Attribute.
|
||||
colorsCache = make(map[Attribute]*Color)
|
||||
colorsCacheMu sync.Mutex // protects colorsCache
|
||||
)
|
||||
|
||||
// noColorIsSet returns true if the environment variable NO_COLOR is set to a non-empty string.
|
||||
func noColorIsSet() bool {
|
||||
return os.Getenv("NO_COLOR") != ""
|
||||
}
|
||||
|
||||
// Color defines a custom color object which is defined by SGR parameters.
|
||||
type Color struct {
|
||||
params []Attribute
|
||||
noColor *bool
|
||||
}
|
||||
|
||||
// Attribute defines a single SGR Code
|
||||
type Attribute int
|
||||
|
||||
const escape = "\x1b"
|
||||
|
||||
// Base attributes
|
||||
const (
|
||||
Reset Attribute = iota
|
||||
Bold
|
||||
Faint
|
||||
Italic
|
||||
Underline
|
||||
BlinkSlow
|
||||
BlinkRapid
|
||||
ReverseVideo
|
||||
Concealed
|
||||
CrossedOut
|
||||
)
|
||||
|
||||
const (
|
||||
ResetBold Attribute = iota + 22
|
||||
ResetItalic
|
||||
ResetUnderline
|
||||
ResetBlinking
|
||||
_
|
||||
ResetReversed
|
||||
ResetConcealed
|
||||
ResetCrossedOut
|
||||
)
|
||||
|
||||
var mapResetAttributes map[Attribute]Attribute = map[Attribute]Attribute{
|
||||
Bold: ResetBold,
|
||||
Faint: ResetBold,
|
||||
Italic: ResetItalic,
|
||||
Underline: ResetUnderline,
|
||||
BlinkSlow: ResetBlinking,
|
||||
BlinkRapid: ResetBlinking,
|
||||
ReverseVideo: ResetReversed,
|
||||
Concealed: ResetConcealed,
|
||||
CrossedOut: ResetCrossedOut,
|
||||
}
|
||||
|
||||
// Foreground text colors
|
||||
const (
|
||||
FgBlack Attribute = iota + 30
|
||||
FgRed
|
||||
FgGreen
|
||||
FgYellow
|
||||
FgBlue
|
||||
FgMagenta
|
||||
FgCyan
|
||||
FgWhite
|
||||
|
||||
// used internally for 256 and 24-bit coloring
|
||||
foreground
|
||||
)
|
||||
|
||||
// Foreground Hi-Intensity text colors
|
||||
const (
|
||||
FgHiBlack Attribute = iota + 90
|
||||
FgHiRed
|
||||
FgHiGreen
|
||||
FgHiYellow
|
||||
FgHiBlue
|
||||
FgHiMagenta
|
||||
FgHiCyan
|
||||
FgHiWhite
|
||||
)
|
||||
|
||||
// Background text colors
|
||||
const (
|
||||
BgBlack Attribute = iota + 40
|
||||
BgRed
|
||||
BgGreen
|
||||
BgYellow
|
||||
BgBlue
|
||||
BgMagenta
|
||||
BgCyan
|
||||
BgWhite
|
||||
|
||||
// used internally for 256 and 24-bit coloring
|
||||
background
|
||||
)
|
||||
|
||||
// Background Hi-Intensity text colors
|
||||
const (
|
||||
BgHiBlack Attribute = iota + 100
|
||||
BgHiRed
|
||||
BgHiGreen
|
||||
BgHiYellow
|
||||
BgHiBlue
|
||||
BgHiMagenta
|
||||
BgHiCyan
|
||||
BgHiWhite
|
||||
)
|
||||
|
||||
// New returns a newly created color object.
|
||||
func New(value ...Attribute) *Color {
|
||||
c := &Color{
|
||||
params: make([]Attribute, 0),
|
||||
}
|
||||
|
||||
if noColorIsSet() {
|
||||
c.noColor = boolPtr(true)
|
||||
}
|
||||
|
||||
c.Add(value...)
|
||||
return c
|
||||
}
|
||||
|
||||
// RGB returns a new foreground color in 24-bit RGB.
|
||||
func RGB(r, g, b int) *Color {
|
||||
return New(foreground, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||
}
|
||||
|
||||
// BgRGB returns a new background color in 24-bit RGB.
|
||||
func BgRGB(r, g, b int) *Color {
|
||||
return New(background, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||
}
|
||||
|
||||
// AddRGB is used to chain foreground RGB SGR parameters. Use as many as parameters to combine
|
||||
// and create custom color objects. Example: .Add(34, 0, 12).Add(255, 128, 0).
|
||||
func (c *Color) AddRGB(r, g, b int) *Color {
|
||||
c.params = append(c.params, foreground, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||
return c
|
||||
}
|
||||
|
||||
// AddRGB is used to chain background RGB SGR parameters. Use as many as parameters to combine
|
||||
// and create custom color objects. Example: .Add(34, 0, 12).Add(255, 128, 0).
|
||||
func (c *Color) AddBgRGB(r, g, b int) *Color {
|
||||
c.params = append(c.params, background, 2, Attribute(r), Attribute(g), Attribute(b))
|
||||
return c
|
||||
}
|
||||
|
||||
// Set sets the given parameters immediately. It will change the color of
|
||||
// output with the given SGR parameters until color.Unset() is called.
|
||||
func Set(p ...Attribute) *Color {
|
||||
c := New(p...)
|
||||
c.Set()
|
||||
return c
|
||||
}
|
||||
|
||||
// Unset resets all escape attributes and clears the output. Usually should
|
||||
// be called after Set().
|
||||
func Unset() {
|
||||
if NoColor {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(Output, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Set sets the SGR sequence.
|
||||
func (c *Color) Set() *Color {
|
||||
if c.isNoColorSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
fmt.Fprint(Output, c.format())
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *Color) unset() {
|
||||
if c.isNoColorSet() {
|
||||
return
|
||||
}
|
||||
|
||||
Unset()
|
||||
}
|
||||
|
||||
// SetWriter is used to set the SGR sequence with the given io.Writer. This is
|
||||
// a low-level function, and users should use the higher-level functions, such
|
||||
// as color.Fprint, color.Print, etc.
|
||||
func (c *Color) SetWriter(w io.Writer) *Color {
|
||||
if c.isNoColorSet() {
|
||||
return c
|
||||
}
|
||||
|
||||
fmt.Fprint(w, c.format())
|
||||
return c
|
||||
}
|
||||
|
||||
// UnsetWriter resets all escape attributes and clears the output with the give
|
||||
// io.Writer. Usually should be called after SetWriter().
|
||||
func (c *Color) UnsetWriter(w io.Writer) {
|
||||
if c.isNoColorSet() {
|
||||
return
|
||||
}
|
||||
|
||||
if NoColor {
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Fprintf(w, "%s[%dm", escape, Reset)
|
||||
}
|
||||
|
||||
// Add is used to chain SGR parameters. Use as many as parameters to combine
|
||||
// and create custom color objects. Example: Add(color.FgRed, color.Underline).
|
||||
func (c *Color) Add(value ...Attribute) *Color {
|
||||
c.params = append(c.params, value...)
|
||||
return c
|
||||
}
|
||||
|
||||
// Fprint formats using the default formats for its operands and writes to w.
|
||||
// Spaces are added between operands when neither is a string.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprint(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
c.SetWriter(w)
|
||||
defer c.UnsetWriter(w)
|
||||
|
||||
return fmt.Fprint(w, a...)
|
||||
}
|
||||
|
||||
// Print formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are added between operands when neither is a
|
||||
// string. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Print(a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprint(Output, a...)
|
||||
}
|
||||
|
||||
// Fprintf formats according to a format specifier and writes to w.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintf(w io.Writer, format string, a ...interface{}) (n int, err error) {
|
||||
c.SetWriter(w)
|
||||
defer c.UnsetWriter(w)
|
||||
|
||||
return fmt.Fprintf(w, format, a...)
|
||||
}
|
||||
|
||||
// Printf formats according to a format specifier and writes to standard output.
|
||||
// It returns the number of bytes written and any write error encountered.
|
||||
// This is the standard fmt.Printf() method wrapped with the given color.
|
||||
func (c *Color) Printf(format string, a ...interface{}) (n int, err error) {
|
||||
c.Set()
|
||||
defer c.unset()
|
||||
|
||||
return fmt.Fprintf(Output, format, a...)
|
||||
}
|
||||
|
||||
// Fprintln formats using the default formats for its operands and writes to w.
|
||||
// Spaces are always added between operands and a newline is appended.
|
||||
// On Windows, users should wrap w with colorable.NewColorable() if w is of
|
||||
// type *os.File.
|
||||
func (c *Color) Fprintln(w io.Writer, a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(w, c.wrap(sprintln(a...)))
|
||||
}
|
||||
|
||||
// Println formats using the default formats for its operands and writes to
|
||||
// standard output. Spaces are always added between operands and a newline is
|
||||
// appended. It returns the number of bytes written and any write error
|
||||
// encountered. This is the standard fmt.Print() method wrapped with the given
|
||||
// color.
|
||||
func (c *Color) Println(a ...interface{}) (n int, err error) {
|
||||
return fmt.Fprintln(Output, c.wrap(sprintln(a...)))
|
||||
}
|
||||
|
||||
// Sprint is just like Print, but returns a string instead of printing it.
|
||||
func (c *Color) Sprint(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
|
||||
// Sprintln is just like Println, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintln(a ...interface{}) string {
|
||||
return c.wrap(sprintln(a...)) + "\n"
|
||||
}
|
||||
|
||||
// Sprintf is just like Printf, but returns a string instead of printing it.
|
||||
func (c *Color) Sprintf(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
|
||||
// FprintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprint().
|
||||
func (c *Color) FprintFunc() func(w io.Writer, a ...interface{}) {
|
||||
return func(w io.Writer, a ...interface{}) {
|
||||
c.Fprint(w, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Print().
|
||||
func (c *Color) PrintFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) {
|
||||
c.Print(a...)
|
||||
}
|
||||
}
|
||||
|
||||
// FprintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprintf().
|
||||
func (c *Color) FprintfFunc() func(w io.Writer, format string, a ...interface{}) {
|
||||
return func(w io.Writer, format string, a ...interface{}) {
|
||||
c.Fprintf(w, format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintfFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Printf().
|
||||
func (c *Color) PrintfFunc() func(format string, a ...interface{}) {
|
||||
return func(format string, a ...interface{}) {
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// FprintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Fprintln().
|
||||
func (c *Color) FprintlnFunc() func(w io.Writer, a ...interface{}) {
|
||||
return func(w io.Writer, a ...interface{}) {
|
||||
c.Fprintln(w, a...)
|
||||
}
|
||||
}
|
||||
|
||||
// PrintlnFunc returns a new function that prints the passed arguments as
|
||||
// colorized with color.Println().
|
||||
func (c *Color) PrintlnFunc() func(a ...interface{}) {
|
||||
return func(a ...interface{}) {
|
||||
c.Println(a...)
|
||||
}
|
||||
}
|
||||
|
||||
// SprintFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprint(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output, example:
|
||||
//
|
||||
// put := New(FgYellow).SprintFunc()
|
||||
// fmt.Fprintf(color.Output, "This is a %s", put("warning"))
|
||||
func (c *Color) SprintFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprint(a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintfFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintf(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintfFunc() func(format string, a ...interface{}) string {
|
||||
return func(format string, a ...interface{}) string {
|
||||
return c.wrap(fmt.Sprintf(format, a...))
|
||||
}
|
||||
}
|
||||
|
||||
// SprintlnFunc returns a new function that returns colorized strings for the
|
||||
// given arguments with fmt.Sprintln(). Useful to put into or mix into other
|
||||
// string. Windows users should use this in conjunction with color.Output.
|
||||
func (c *Color) SprintlnFunc() func(a ...interface{}) string {
|
||||
return func(a ...interface{}) string {
|
||||
return c.wrap(sprintln(a...)) + "\n"
|
||||
}
|
||||
}
|
||||
|
||||
// sequence returns a formatted SGR sequence to be plugged into a "\x1b[...m"
|
||||
// an example output might be: "1;36" -> bold cyan
|
||||
func (c *Color) sequence() string {
|
||||
format := make([]string, len(c.params))
|
||||
for i, v := range c.params {
|
||||
format[i] = strconv.Itoa(int(v))
|
||||
}
|
||||
|
||||
return strings.Join(format, ";")
|
||||
}
|
||||
|
||||
// wrap wraps the s string with the colors attributes. The string is ready to
|
||||
// be printed.
|
||||
func (c *Color) wrap(s string) string {
|
||||
if c.isNoColorSet() {
|
||||
return s
|
||||
}
|
||||
|
||||
return c.format() + s + c.unformat()
|
||||
}
|
||||
|
||||
func (c *Color) format() string {
|
||||
return fmt.Sprintf("%s[%sm", escape, c.sequence())
|
||||
}
|
||||
|
||||
func (c *Color) unformat() string {
|
||||
//return fmt.Sprintf("%s[%dm", escape, Reset)
|
||||
//for each element in sequence let's use the specific reset escape, or the generic one if not found
|
||||
format := make([]string, len(c.params))
|
||||
for i, v := range c.params {
|
||||
format[i] = strconv.Itoa(int(Reset))
|
||||
ra, ok := mapResetAttributes[v]
|
||||
if ok {
|
||||
format[i] = strconv.Itoa(int(ra))
|
||||
}
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s[%sm", escape, strings.Join(format, ";"))
|
||||
}
|
||||
|
||||
// DisableColor disables the color output. Useful to not change any existing
|
||||
// code and still being able to output. Can be used for flags like
|
||||
// "--no-color". To enable back use EnableColor() method.
|
||||
func (c *Color) DisableColor() {
|
||||
c.noColor = boolPtr(true)
|
||||
}
|
||||
|
||||
// EnableColor enables the color output. Use it in conjunction with
|
||||
// DisableColor(). Otherwise, this method has no side effects.
|
||||
func (c *Color) EnableColor() {
|
||||
c.noColor = boolPtr(false)
|
||||
}
|
||||
|
||||
func (c *Color) isNoColorSet() bool {
|
||||
// check first if we have user set action
|
||||
if c.noColor != nil {
|
||||
return *c.noColor
|
||||
}
|
||||
|
||||
// if not return the global option, which is disabled by default
|
||||
return NoColor
|
||||
}
|
||||
|
||||
// Equals returns a boolean value indicating whether two colors are equal.
|
||||
func (c *Color) Equals(c2 *Color) bool {
|
||||
if c == nil && c2 == nil {
|
||||
return true
|
||||
}
|
||||
if c == nil || c2 == nil {
|
||||
return false
|
||||
}
|
||||
if len(c.params) != len(c2.params) {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, attr := range c.params {
|
||||
if !c2.attrExists(attr) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
func (c *Color) attrExists(a Attribute) bool {
|
||||
for _, attr := range c.params {
|
||||
if attr == a {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func boolPtr(v bool) *bool {
|
||||
return &v
|
||||
}
|
||||
|
||||
func getCachedColor(p Attribute) *Color {
|
||||
colorsCacheMu.Lock()
|
||||
defer colorsCacheMu.Unlock()
|
||||
|
||||
c, ok := colorsCache[p]
|
||||
if !ok {
|
||||
c = New(p)
|
||||
colorsCache[p] = c
|
||||
}
|
||||
|
||||
return c
|
||||
}
|
||||
|
||||
func colorPrint(format string, p Attribute, a ...interface{}) {
|
||||
c := getCachedColor(p)
|
||||
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
|
||||
if len(a) == 0 {
|
||||
c.Print(format)
|
||||
} else {
|
||||
c.Printf(format, a...)
|
||||
}
|
||||
}
|
||||
|
||||
func colorString(format string, p Attribute, a ...interface{}) string {
|
||||
c := getCachedColor(p)
|
||||
|
||||
if len(a) == 0 {
|
||||
return c.SprintFunc()(format)
|
||||
}
|
||||
|
||||
return c.SprintfFunc()(format, a...)
|
||||
}
|
||||
|
||||
// Black is a convenient helper function to print with black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Black(format string, a ...interface{}) { colorPrint(format, FgBlack, a...) }
|
||||
|
||||
// Red is a convenient helper function to print with red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Red(format string, a ...interface{}) { colorPrint(format, FgRed, a...) }
|
||||
|
||||
// Green is a convenient helper function to print with green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Green(format string, a ...interface{}) { colorPrint(format, FgGreen, a...) }
|
||||
|
||||
// Yellow is a convenient helper function to print with yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Yellow(format string, a ...interface{}) { colorPrint(format, FgYellow, a...) }
|
||||
|
||||
// Blue is a convenient helper function to print with blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Blue(format string, a ...interface{}) { colorPrint(format, FgBlue, a...) }
|
||||
|
||||
// Magenta is a convenient helper function to print with magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func Magenta(format string, a ...interface{}) { colorPrint(format, FgMagenta, a...) }
|
||||
|
||||
// Cyan is a convenient helper function to print with cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func Cyan(format string, a ...interface{}) { colorPrint(format, FgCyan, a...) }
|
||||
|
||||
// White is a convenient helper function to print with white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func White(format string, a ...interface{}) { colorPrint(format, FgWhite, a...) }
|
||||
|
||||
// BlackString is a convenient helper function to return a string with black
|
||||
// foreground.
|
||||
func BlackString(format string, a ...interface{}) string { return colorString(format, FgBlack, a...) }
|
||||
|
||||
// RedString is a convenient helper function to return a string with red
|
||||
// foreground.
|
||||
func RedString(format string, a ...interface{}) string { return colorString(format, FgRed, a...) }
|
||||
|
||||
// GreenString is a convenient helper function to return a string with green
|
||||
// foreground.
|
||||
func GreenString(format string, a ...interface{}) string { return colorString(format, FgGreen, a...) }
|
||||
|
||||
// YellowString is a convenient helper function to return a string with yellow
|
||||
// foreground.
|
||||
func YellowString(format string, a ...interface{}) string { return colorString(format, FgYellow, a...) }
|
||||
|
||||
// BlueString is a convenient helper function to return a string with blue
|
||||
// foreground.
|
||||
func BlueString(format string, a ...interface{}) string { return colorString(format, FgBlue, a...) }
|
||||
|
||||
// MagentaString is a convenient helper function to return a string with magenta
|
||||
// foreground.
|
||||
func MagentaString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgMagenta, a...)
|
||||
}
|
||||
|
||||
// CyanString is a convenient helper function to return a string with cyan
|
||||
// foreground.
|
||||
func CyanString(format string, a ...interface{}) string { return colorString(format, FgCyan, a...) }
|
||||
|
||||
// WhiteString is a convenient helper function to return a string with white
|
||||
// foreground.
|
||||
func WhiteString(format string, a ...interface{}) string { return colorString(format, FgWhite, a...) }
|
||||
|
||||
// HiBlack is a convenient helper function to print with hi-intensity black foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiBlack(format string, a ...interface{}) { colorPrint(format, FgHiBlack, a...) }
|
||||
|
||||
// HiRed is a convenient helper function to print with hi-intensity red foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiRed(format string, a ...interface{}) { colorPrint(format, FgHiRed, a...) }
|
||||
|
||||
// HiGreen is a convenient helper function to print with hi-intensity green foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiGreen(format string, a ...interface{}) { colorPrint(format, FgHiGreen, a...) }
|
||||
|
||||
// HiYellow is a convenient helper function to print with hi-intensity yellow foreground.
|
||||
// A newline is appended to format by default.
|
||||
func HiYellow(format string, a ...interface{}) { colorPrint(format, FgHiYellow, a...) }
|
||||
|
||||
// HiBlue is a convenient helper function to print with hi-intensity blue foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiBlue(format string, a ...interface{}) { colorPrint(format, FgHiBlue, a...) }
|
||||
|
||||
// HiMagenta is a convenient helper function to print with hi-intensity magenta foreground.
|
||||
// A newline is appended to format by default.
|
||||
func HiMagenta(format string, a ...interface{}) { colorPrint(format, FgHiMagenta, a...) }
|
||||
|
||||
// HiCyan is a convenient helper function to print with hi-intensity cyan foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiCyan(format string, a ...interface{}) { colorPrint(format, FgHiCyan, a...) }
|
||||
|
||||
// HiWhite is a convenient helper function to print with hi-intensity white foreground. A
|
||||
// newline is appended to format by default.
|
||||
func HiWhite(format string, a ...interface{}) { colorPrint(format, FgHiWhite, a...) }
|
||||
|
||||
// HiBlackString is a convenient helper function to return a string with hi-intensity black
|
||||
// foreground.
|
||||
func HiBlackString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiBlack, a...)
|
||||
}
|
||||
|
||||
// HiRedString is a convenient helper function to return a string with hi-intensity red
|
||||
// foreground.
|
||||
func HiRedString(format string, a ...interface{}) string { return colorString(format, FgHiRed, a...) }
|
||||
|
||||
// HiGreenString is a convenient helper function to return a string with hi-intensity green
|
||||
// foreground.
|
||||
func HiGreenString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiGreen, a...)
|
||||
}
|
||||
|
||||
// HiYellowString is a convenient helper function to return a string with hi-intensity yellow
|
||||
// foreground.
|
||||
func HiYellowString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiYellow, a...)
|
||||
}
|
||||
|
||||
// HiBlueString is a convenient helper function to return a string with hi-intensity blue
|
||||
// foreground.
|
||||
func HiBlueString(format string, a ...interface{}) string { return colorString(format, FgHiBlue, a...) }
|
||||
|
||||
// HiMagentaString is a convenient helper function to return a string with hi-intensity magenta
|
||||
// foreground.
|
||||
func HiMagentaString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiMagenta, a...)
|
||||
}
|
||||
|
||||
// HiCyanString is a convenient helper function to return a string with hi-intensity cyan
|
||||
// foreground.
|
||||
func HiCyanString(format string, a ...interface{}) string { return colorString(format, FgHiCyan, a...) }
|
||||
|
||||
// HiWhiteString is a convenient helper function to return a string with hi-intensity white
|
||||
// foreground.
|
||||
func HiWhiteString(format string, a ...interface{}) string {
|
||||
return colorString(format, FgHiWhite, a...)
|
||||
}
|
||||
|
||||
// sprintln is a helper function to format a string with fmt.Sprintln and trim the trailing newline.
|
||||
func sprintln(a ...interface{}) string {
|
||||
return strings.TrimSuffix(fmt.Sprintln(a...), "\n")
|
||||
}
|
||||
19
vendor/github.com/fatih/color/color_windows.go
generated
vendored
Normal file
19
vendor/github.com/fatih/color/color_windows.go
generated
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
package color
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Opt-in for ansi color support for current process.
|
||||
// https://learn.microsoft.com/en-us/windows/console/console-virtual-terminal-sequences#output-sequences
|
||||
var outMode uint32
|
||||
out := windows.Handle(os.Stdout.Fd())
|
||||
if err := windows.GetConsoleMode(out, &outMode); err != nil {
|
||||
return
|
||||
}
|
||||
outMode |= windows.ENABLE_PROCESSED_OUTPUT | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING
|
||||
_ = windows.SetConsoleMode(out, outMode)
|
||||
}
|
||||
134
vendor/github.com/fatih/color/doc.go
generated
vendored
Normal file
134
vendor/github.com/fatih/color/doc.go
generated
vendored
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
/*
|
||||
Package color is an ANSI color package to output colorized or SGR defined
|
||||
output to the standard output. The API can be used in several way, pick one
|
||||
that suits you.
|
||||
|
||||
Use simple and default helper functions with predefined foreground colors:
|
||||
|
||||
color.Cyan("Prints text in cyan.")
|
||||
|
||||
// a newline will be appended automatically
|
||||
color.Blue("Prints %s in blue.", "text")
|
||||
|
||||
// More default foreground colors..
|
||||
color.Red("We have red")
|
||||
color.Yellow("Yellow color too!")
|
||||
color.Magenta("And many others ..")
|
||||
|
||||
// Hi-intensity colors
|
||||
color.HiGreen("Bright green color.")
|
||||
color.HiBlack("Bright black means gray..")
|
||||
color.HiWhite("Shiny white color!")
|
||||
|
||||
However, there are times when custom color mixes are required. Below are some
|
||||
examples to create custom color objects and use the print functions of each
|
||||
separate color object.
|
||||
|
||||
// Create a new color object
|
||||
c := color.New(color.FgCyan).Add(color.Underline)
|
||||
c.Println("Prints cyan text with an underline.")
|
||||
|
||||
// Or just add them to New()
|
||||
d := color.New(color.FgCyan, color.Bold)
|
||||
d.Printf("This prints bold cyan %s\n", "too!.")
|
||||
|
||||
|
||||
// Mix up foreground and background colors, create new mixes!
|
||||
red := color.New(color.FgRed)
|
||||
|
||||
boldRed := red.Add(color.Bold)
|
||||
boldRed.Println("This will print text in bold red.")
|
||||
|
||||
whiteBackground := red.Add(color.BgWhite)
|
||||
whiteBackground.Println("Red text with White background.")
|
||||
|
||||
// Use your own io.Writer output
|
||||
color.New(color.FgBlue).Fprintln(myWriter, "blue color!")
|
||||
|
||||
blue := color.New(color.FgBlue)
|
||||
blue.Fprint(myWriter, "This will print text in blue.")
|
||||
|
||||
You can create PrintXxx functions to simplify even more:
|
||||
|
||||
// Create a custom print function for convenient
|
||||
red := color.New(color.FgRed).PrintfFunc()
|
||||
red("warning")
|
||||
red("error: %s", err)
|
||||
|
||||
// Mix up multiple attributes
|
||||
notice := color.New(color.Bold, color.FgGreen).PrintlnFunc()
|
||||
notice("don't forget this...")
|
||||
|
||||
You can also FprintXxx functions to pass your own io.Writer:
|
||||
|
||||
blue := color.New(FgBlue).FprintfFunc()
|
||||
blue(myWriter, "important notice: %s", stars)
|
||||
|
||||
// Mix up with multiple attributes
|
||||
success := color.New(color.Bold, color.FgGreen).FprintlnFunc()
|
||||
success(myWriter, don't forget this...")
|
||||
|
||||
Or create SprintXxx functions to mix strings with other non-colorized strings:
|
||||
|
||||
yellow := New(FgYellow).SprintFunc()
|
||||
red := New(FgRed).SprintFunc()
|
||||
|
||||
fmt.Printf("this is a %s and this is %s.\n", yellow("warning"), red("error"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Printf("this %s rocks!\n", info("package"))
|
||||
|
||||
Windows support is enabled by default. All Print functions work as intended.
|
||||
However, only for color.SprintXXX functions, user should use fmt.FprintXXX and
|
||||
set the output to color.Output:
|
||||
|
||||
fmt.Fprintf(color.Output, "Windows support: %s", color.GreenString("PASS"))
|
||||
|
||||
info := New(FgWhite, BgGreen).SprintFunc()
|
||||
fmt.Fprintf(color.Output, "this %s rocks!\n", info("package"))
|
||||
|
||||
Using with existing code is possible. Just use the Set() method to set the
|
||||
standard output to the given parameters. That way a rewrite of an existing
|
||||
code is not required.
|
||||
|
||||
// Use handy standard colors.
|
||||
color.Set(color.FgYellow)
|
||||
|
||||
fmt.Println("Existing text will be now in Yellow")
|
||||
fmt.Printf("This one %s\n", "too")
|
||||
|
||||
color.Unset() // don't forget to unset
|
||||
|
||||
// You can mix up parameters
|
||||
color.Set(color.FgMagenta, color.Bold)
|
||||
defer color.Unset() // use it in your function
|
||||
|
||||
fmt.Println("All text will be now bold magenta.")
|
||||
|
||||
There might be a case where you want to disable color output (for example to
|
||||
pipe the standard output of your app to somewhere else). `Color` has support to
|
||||
disable colors both globally and for single color definition. For example
|
||||
suppose you have a CLI app and a `--no-color` bool flag. You can easily disable
|
||||
the color output with:
|
||||
|
||||
var flagNoColor = flag.Bool("no-color", false, "Disable color output")
|
||||
|
||||
if *flagNoColor {
|
||||
color.NoColor = true // disables colorized output
|
||||
}
|
||||
|
||||
You can also disable the color by setting the NO_COLOR environment variable to any value.
|
||||
|
||||
It also has support for single color definitions (local). You can
|
||||
disable/enable color output on the fly:
|
||||
|
||||
c := color.New(color.FgCyan)
|
||||
c.Println("Prints cyan text")
|
||||
|
||||
c.DisableColor()
|
||||
c.Println("This is printed without any color")
|
||||
|
||||
c.EnableColor()
|
||||
c.Println("This prints again cyan...")
|
||||
*/
|
||||
package color
|
||||
2
vendor/github.com/getsentry/sentry-go/.codecov.yml
generated
vendored
2
vendor/github.com/getsentry/sentry-go/.codecov.yml
generated
vendored
|
|
@ -11,3 +11,5 @@ coverage:
|
|||
default:
|
||||
# Do not fail the commit status if the coverage was reduced up to this value
|
||||
threshold: 0.5%
|
||||
ignore:
|
||||
- "log_fallback.go"
|
||||
|
|
|
|||
62
vendor/github.com/getsentry/sentry-go/.golangci.yml
generated
vendored
62
vendor/github.com/getsentry/sentry-go/.golangci.yml
generated
vendored
|
|
@ -1,20 +1,17 @@
|
|||
version: "2"
|
||||
linters:
|
||||
disable-all: true
|
||||
default: none
|
||||
enable:
|
||||
- bodyclose
|
||||
- dogsled
|
||||
- dupl
|
||||
- errcheck
|
||||
- exportloopref
|
||||
- gochecknoinits
|
||||
- goconst
|
||||
- gocritic
|
||||
- gocyclo
|
||||
- godot
|
||||
- gofmt
|
||||
- goimports
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
|
|
@ -22,25 +19,44 @@ linters:
|
|||
- prealloc
|
||||
- revive
|
||||
- staticcheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unparam
|
||||
- unused
|
||||
- whitespace
|
||||
issues:
|
||||
exclude-rules:
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- goconst
|
||||
- prealloc
|
||||
- path: _test\.go
|
||||
text: "G306:"
|
||||
linters:
|
||||
- gosec
|
||||
- path: errors_test\.go
|
||||
linters:
|
||||
- unused
|
||||
- path: http/example_test\.go
|
||||
linters:
|
||||
- errcheck
|
||||
- bodyclose
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
rules:
|
||||
- linters:
|
||||
- goconst
|
||||
- prealloc
|
||||
path: _test\.go
|
||||
- linters:
|
||||
- gosec
|
||||
path: _test\.go
|
||||
text: 'G306:'
|
||||
- linters:
|
||||
- unused
|
||||
path: errors_test\.go
|
||||
- linters:
|
||||
- bodyclose
|
||||
- errcheck
|
||||
path: http/example_test\.go
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
|
|
|||
133
vendor/github.com/getsentry/sentry-go/CHANGELOG.md
generated
vendored
133
vendor/github.com/getsentry/sentry-go/CHANGELOG.md
generated
vendored
|
|
@ -1,5 +1,138 @@
|
|||
# Changelog
|
||||
|
||||
## 0.34.0
|
||||
|
||||
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.34.0.
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Logrus structured logging support replaces the `sentrylogrus.Hook` signature from a `*Hook` to an interface.
|
||||
|
||||
```go
|
||||
var hook *sentrylogrus.Hook
|
||||
hook = sentrylogrus.New(
|
||||
// ... your setup
|
||||
)
|
||||
|
||||
// should change the definition to
|
||||
var hook sentrylogrus.Hook
|
||||
hook = sentrylogrus.New(
|
||||
// ... your setup
|
||||
)
|
||||
```
|
||||
|
||||
### Features
|
||||
|
||||
- Structured logging support for [slog](https://pkg.go.dev/log/slog). ([#1033](https://github.com/getsentry/sentry-go/pull/1033))
|
||||
|
||||
```go
|
||||
ctx := context.Background()
|
||||
handler := sentryslog.Option{
|
||||
EventLevel: []slog.Level{slog.LevelError, sentryslog.LevelFatal}, // Only Error and Fatal as events
|
||||
LogLevel: []slog.Level{slog.LevelWarn, slog.LevelInfo}, // Only Warn and Info as logs
|
||||
}.NewSentryHandler(ctx)
|
||||
logger := slog.New(handler)
|
||||
logger.Info("hello"))
|
||||
```
|
||||
|
||||
- Structured logging support for [logrus](https://github.com/sirupsen/logrus). ([#1036](https://github.com/getsentry/sentry-go/pull/1036))
|
||||
```go
|
||||
logHook, _ := sentrylogrus.NewLogHook(
|
||||
[]logrus.Level{logrus.InfoLevel, logrus.WarnLevel},
|
||||
sentry.ClientOptions{
|
||||
Dsn: "your-dsn",
|
||||
EnableLogs: true, // Required for log entries
|
||||
})
|
||||
defer logHook.Flush(5 * time.Secod)
|
||||
logrus.RegisterExitHandler(func() {
|
||||
logHook.Flush(5 * time.Second)
|
||||
})
|
||||
|
||||
logger := logrus.New()
|
||||
logger.AddHook(logHook)
|
||||
logger.Infof("hello")
|
||||
```
|
||||
|
||||
- Add support for flushing events with context using `FlushWithContext()`. ([#935](https://github.com/getsentry/sentry-go/pull/935))
|
||||
|
||||
```go
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
if !sentry.FlushWithContext(ctx) {
|
||||
// Handle timeout or cancellation
|
||||
}
|
||||
```
|
||||
|
||||
- Add support for custom fingerprints in slog integration. ([#1039](https://github.com/getsentry/sentry-go/pull/1039))
|
||||
|
||||
### Deprecations
|
||||
|
||||
- Slog structured logging support replaces `Level` option with `EventLevel` and `LogLevel` options, for specifying fine-grained levels for capturing events and logs.
|
||||
|
||||
```go
|
||||
handler := sentryslog.Option{
|
||||
EventLevel: []slog.Level{slog.LevelWarn, slog.LevelError, sentryslog.LevelFatal},
|
||||
LogLevel: []slog.Level{slog.LevelDebug, slog.LevelInfo, slog.LevelWarn, slog.LevelError, sentryslog.LevelFatal},
|
||||
}.NewSentryHandler(ctx)
|
||||
```
|
||||
|
||||
- Logrus structured logging support replaces `New` and `NewFromClient` functions to `NewEventHook`, `NewEventHookFromClient`, to match the newly added `NewLogHook` functions, and specify the hook type being created each time.
|
||||
|
||||
```go
|
||||
logHook, err := sentrylogrus.NewLogHook(
|
||||
[]logrus.Level{logrus.InfoLevel},
|
||||
sentry.ClientOptions{})
|
||||
eventHook, err := sentrylogrus.NewEventHook([]logrus.Level{
|
||||
logrus.ErrorLevel,
|
||||
logrus.FatalLevel,
|
||||
logrus.PanicLevel,
|
||||
}, sentry.ClientOptions{})
|
||||
```
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Fix issue where `ContinueTrace()` would panic when `sentry-trace` header does not exist. ([#1026](https://github.com/getsentry/sentry-go/pull/1026))
|
||||
- Fix incorrect log level signature in structured logging. ([#1034](https://github.com/getsentry/sentry-go/pull/1034))
|
||||
- Remove `sentry.origin` attribute from Sentry logger to prevent confusion in spans. ([#1038](https://github.com/getsentry/sentry-go/pull/1038))
|
||||
- Don't gate user information behind `SendDefaultPII` flag for logs. ([#1032](https://github.com/getsentry/sentry-go/pull/1032))
|
||||
|
||||
### Misc
|
||||
|
||||
- Add more sensitive HTTP headers to the default list of headers that are scrubbed by default. ([#1008](https://github.com/getsentry/sentry-go/pull/1008))
|
||||
|
||||
## 0.33.0
|
||||
|
||||
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.33.0.
|
||||
|
||||
|
||||
### Breaking Changes
|
||||
|
||||
- Rename the internal `Logger` to `DebugLogger`. This feature was only used when you set `Debug: True` in your `sentry.Init()` call. If you haven't used the Logger directly, no changes are necessary. ([#1012](https://github.com/getsentry/sentry-go/issues/1012))
|
||||
|
||||
### Features
|
||||
|
||||
- Add support for [Structured Logging](https://docs.sentry.io/product/explore/logs/). ([#1010](https://github.com/getsentry/sentry-go/issues/1010))
|
||||
|
||||
```go
|
||||
logger := sentry.NewLogger(ctx)
|
||||
logger.Info(ctx, "Hello, Logs!")
|
||||
```
|
||||
|
||||
You can learn more about Sentry Logs on our [docs](https://docs.sentry.io/product/explore/logs/) and the [examples](https://github.com/getsentry/sentry-go/blob/master/_examples/logs/main.go).
|
||||
|
||||
- Add new attributes APIs, which are currently only exposed on logs. ([#1007](https://github.com/getsentry/sentry-go/issues/1007))
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
- Do not push a new scope on `StartSpan`. ([#1013](https://github.com/getsentry/sentry-go/issues/1013))
|
||||
- Fix an issue where the propagated smapling decision wasn't used. ([#995](https://github.com/getsentry/sentry-go/issues/995))
|
||||
- [Otel] Prefer `httpRoute` over `httpTarget` for span descriptions. ([#1002](https://github.com/getsentry/sentry-go/issues/1002))
|
||||
|
||||
### Misc
|
||||
|
||||
- Update `github.com/stretchr/testify` to v1.8.4. ([#988](https://github.com/getsentry/sentry-go/issues/988))
|
||||
|
||||
## 0.32.0
|
||||
|
||||
The Sentry SDK team is happy to announce the immediate availability of Sentry Go SDK v0.32.0.
|
||||
|
|
|
|||
36
vendor/github.com/getsentry/sentry-go/attribute/builder.go
generated
vendored
Normal file
36
vendor/github.com/getsentry/sentry-go/attribute/builder.go
generated
vendored
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
package attribute
|
||||
|
||||
type Builder struct {
|
||||
Key string
|
||||
Value Value
|
||||
}
|
||||
|
||||
// String returns a Builder for a string value.
|
||||
func String(key, value string) Builder {
|
||||
return Builder{key, StringValue(value)}
|
||||
}
|
||||
|
||||
// Int64 returns a Builder for an int64.
|
||||
func Int64(key string, value int64) Builder {
|
||||
return Builder{key, Int64Value(value)}
|
||||
}
|
||||
|
||||
// Int returns a Builder for an int64.
|
||||
func Int(key string, value int) Builder {
|
||||
return Builder{key, IntValue(value)}
|
||||
}
|
||||
|
||||
// Float64 returns a Builder for a float64.
|
||||
func Float64(key string, v float64) Builder {
|
||||
return Builder{key, Float64Value(v)}
|
||||
}
|
||||
|
||||
// Bool returns a Builder for a boolean.
|
||||
func Bool(key string, v bool) Builder {
|
||||
return Builder{key, BoolValue(v)}
|
||||
}
|
||||
|
||||
// Valid checks for valid key and type.
|
||||
func (b *Builder) Valid() bool {
|
||||
return len(b.Key) > 0 && b.Value.Type() != INVALID
|
||||
}
|
||||
49
vendor/github.com/getsentry/sentry-go/attribute/rawhelpers.go
generated
vendored
Normal file
49
vendor/github.com/getsentry/sentry-go/attribute/rawhelpers.go
generated
vendored
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
// Copied from https://github.com/open-telemetry/opentelemetry-go/blob/cc43e01c27892252aac9a8f20da28cdde957a289/attribute/rawhelpers.go
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package attribute
|
||||
|
||||
import (
|
||||
"math"
|
||||
)
|
||||
|
||||
func boolToRaw(b bool) uint64 { // b is not a control flag.
|
||||
if b {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func rawToBool(r uint64) bool {
|
||||
return r != 0
|
||||
}
|
||||
|
||||
func int64ToRaw(i int64) uint64 {
|
||||
// Assumes original was a valid int64 (overflow not checked).
|
||||
return uint64(i) // nolint: gosec
|
||||
}
|
||||
|
||||
func rawToInt64(r uint64) int64 {
|
||||
// Assumes original was a valid int64 (overflow not checked).
|
||||
return int64(r) // nolint: gosec
|
||||
}
|
||||
|
||||
func float64ToRaw(f float64) uint64 {
|
||||
return math.Float64bits(f)
|
||||
}
|
||||
|
||||
func rawToFloat64(r uint64) float64 {
|
||||
return math.Float64frombits(r)
|
||||
}
|
||||
170
vendor/github.com/getsentry/sentry-go/attribute/value.go
generated
vendored
Normal file
170
vendor/github.com/getsentry/sentry-go/attribute/value.go
generated
vendored
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
// Adapted from https://github.com/open-telemetry/opentelemetry-go/blob/cc43e01c27892252aac9a8f20da28cdde957a289/attribute/value.go
|
||||
//
|
||||
// Copyright The OpenTelemetry Authors
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package attribute
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
// Type describes the type of the data Value holds.
|
||||
type Type int // redefines builtin Type.
|
||||
|
||||
// Value represents the value part in key-value pairs.
|
||||
type Value struct {
|
||||
vtype Type
|
||||
numeric uint64
|
||||
stringly string
|
||||
}
|
||||
|
||||
const (
|
||||
// INVALID is used for a Value with no value set.
|
||||
INVALID Type = iota
|
||||
// BOOL is a boolean Type Value.
|
||||
BOOL
|
||||
// INT64 is a 64-bit signed integral Type Value.
|
||||
INT64
|
||||
// FLOAT64 is a 64-bit floating point Type Value.
|
||||
FLOAT64
|
||||
// STRING is a string Type Value.
|
||||
STRING
|
||||
)
|
||||
|
||||
// BoolValue creates a BOOL Value.
|
||||
func BoolValue(v bool) Value {
|
||||
return Value{
|
||||
vtype: BOOL,
|
||||
numeric: boolToRaw(v),
|
||||
}
|
||||
}
|
||||
|
||||
// IntValue creates an INT64 Value.
|
||||
func IntValue(v int) Value {
|
||||
return Int64Value(int64(v))
|
||||
}
|
||||
|
||||
// Int64Value creates an INT64 Value.
|
||||
func Int64Value(v int64) Value {
|
||||
return Value{
|
||||
vtype: INT64,
|
||||
numeric: int64ToRaw(v),
|
||||
}
|
||||
}
|
||||
|
||||
// Float64Value creates a FLOAT64 Value.
|
||||
func Float64Value(v float64) Value {
|
||||
return Value{
|
||||
vtype: FLOAT64,
|
||||
numeric: float64ToRaw(v),
|
||||
}
|
||||
}
|
||||
|
||||
// StringValue creates a STRING Value.
|
||||
func StringValue(v string) Value {
|
||||
return Value{
|
||||
vtype: STRING,
|
||||
stringly: v,
|
||||
}
|
||||
}
|
||||
|
||||
// Type returns a type of the Value.
|
||||
func (v Value) Type() Type {
|
||||
return v.vtype
|
||||
}
|
||||
|
||||
// AsBool returns the bool value. Make sure that the Value's type is
|
||||
// BOOL.
|
||||
func (v Value) AsBool() bool {
|
||||
return rawToBool(v.numeric)
|
||||
}
|
||||
|
||||
// AsInt64 returns the int64 value. Make sure that the Value's type is
|
||||
// INT64.
|
||||
func (v Value) AsInt64() int64 {
|
||||
return rawToInt64(v.numeric)
|
||||
}
|
||||
|
||||
// AsFloat64 returns the float64 value. Make sure that the Value's
|
||||
// type is FLOAT64.
|
||||
func (v Value) AsFloat64() float64 {
|
||||
return rawToFloat64(v.numeric)
|
||||
}
|
||||
|
||||
// AsString returns the string value. Make sure that the Value's type
|
||||
// is STRING.
|
||||
func (v Value) AsString() string {
|
||||
return v.stringly
|
||||
}
|
||||
|
||||
type unknownValueType struct{}
|
||||
|
||||
// AsInterface returns Value's data as interface{}.
|
||||
func (v Value) AsInterface() interface{} {
|
||||
switch v.Type() {
|
||||
case BOOL:
|
||||
return v.AsBool()
|
||||
case INT64:
|
||||
return v.AsInt64()
|
||||
case FLOAT64:
|
||||
return v.AsFloat64()
|
||||
case STRING:
|
||||
return v.stringly
|
||||
}
|
||||
return unknownValueType{}
|
||||
}
|
||||
|
||||
// String returns a string representation of Value's data.
|
||||
func (v Value) String() string {
|
||||
switch v.Type() {
|
||||
case BOOL:
|
||||
return strconv.FormatBool(v.AsBool())
|
||||
case INT64:
|
||||
return strconv.FormatInt(v.AsInt64(), 10)
|
||||
case FLOAT64:
|
||||
return fmt.Sprint(v.AsFloat64())
|
||||
case STRING:
|
||||
return v.stringly
|
||||
default:
|
||||
return "unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON returns the JSON encoding of the Value.
|
||||
func (v Value) MarshalJSON() ([]byte, error) {
|
||||
var jsonVal struct {
|
||||
Type string
|
||||
Value interface{}
|
||||
}
|
||||
jsonVal.Type = v.Type().String()
|
||||
jsonVal.Value = v.AsInterface()
|
||||
return json.Marshal(jsonVal)
|
||||
}
|
||||
|
||||
func (t Type) String() string {
|
||||
switch t {
|
||||
case BOOL:
|
||||
return "bool"
|
||||
case INT64:
|
||||
return "int64"
|
||||
case FLOAT64:
|
||||
return "float64"
|
||||
case STRING:
|
||||
return "string"
|
||||
}
|
||||
return "invalid"
|
||||
}
|
||||
94
vendor/github.com/getsentry/sentry-go/batch_logger.go
generated
vendored
Normal file
94
vendor/github.com/getsentry/sentry-go/batch_logger.go
generated
vendored
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
package sentry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
batchSize = 100
|
||||
batchTimeout = 5 * time.Second
|
||||
)
|
||||
|
||||
type BatchLogger struct {
|
||||
client *Client
|
||||
logCh chan Log
|
||||
cancel context.CancelFunc
|
||||
wg sync.WaitGroup
|
||||
startOnce sync.Once
|
||||
}
|
||||
|
||||
func NewBatchLogger(client *Client) *BatchLogger {
|
||||
return &BatchLogger{
|
||||
client: client,
|
||||
logCh: make(chan Log, batchSize),
|
||||
}
|
||||
}
|
||||
|
||||
func (l *BatchLogger) Start() {
|
||||
l.startOnce.Do(func() {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
l.cancel = cancel
|
||||
l.wg.Add(1)
|
||||
go l.run(ctx)
|
||||
})
|
||||
}
|
||||
|
||||
func (l *BatchLogger) Flush() {
|
||||
if l.cancel != nil {
|
||||
l.cancel()
|
||||
l.wg.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *BatchLogger) run(ctx context.Context) {
|
||||
defer l.wg.Done()
|
||||
var logs []Log
|
||||
timer := time.NewTimer(batchTimeout)
|
||||
|
||||
for {
|
||||
select {
|
||||
case log := <-l.logCh:
|
||||
logs = append(logs, log)
|
||||
if len(logs) >= batchSize {
|
||||
l.processEvent(logs)
|
||||
logs = nil
|
||||
if !timer.Stop() {
|
||||
<-timer.C
|
||||
}
|
||||
timer.Reset(batchTimeout)
|
||||
}
|
||||
case <-timer.C:
|
||||
if len(logs) > 0 {
|
||||
l.processEvent(logs)
|
||||
logs = nil
|
||||
}
|
||||
timer.Reset(batchTimeout)
|
||||
case <-ctx.Done():
|
||||
// Drain remaining logs from channel
|
||||
drain:
|
||||
for {
|
||||
select {
|
||||
case log := <-l.logCh:
|
||||
logs = append(logs, log)
|
||||
default:
|
||||
break drain
|
||||
}
|
||||
}
|
||||
|
||||
if len(logs) > 0 {
|
||||
l.processEvent(logs)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *BatchLogger) processEvent(logs []Log) {
|
||||
event := NewEvent()
|
||||
event.Timestamp = time.Now()
|
||||
event.Type = logEvent.Type
|
||||
event.Logs = logs
|
||||
l.client.CaptureEvent(event, nil, nil)
|
||||
}
|
||||
89
vendor/github.com/getsentry/sentry-go/client.go
generated
vendored
89
vendor/github.com/getsentry/sentry-go/client.go
generated
vendored
|
|
@ -77,9 +77,9 @@ type usageError struct {
|
|||
error
|
||||
}
|
||||
|
||||
// Logger is an instance of log.Logger that is use to provide debug information about running Sentry Client
|
||||
// can be enabled by either using Logger.SetOutput directly or with Debug client option.
|
||||
var Logger = log.New(io.Discard, "[Sentry] ", log.LstdFlags)
|
||||
// DebugLogger is an instance of log.Logger that is used to provide debug information about running Sentry Client
|
||||
// can be enabled by either using DebugLogger.SetOutput directly or with Debug client option.
|
||||
var DebugLogger = log.New(io.Discard, "[Sentry] ", log.LstdFlags)
|
||||
|
||||
// EventProcessor is a function that processes an event.
|
||||
// Event processors are used to change an event before it is sent to Sentry.
|
||||
|
|
@ -144,8 +144,11 @@ type ClientOptions struct {
|
|||
// By default, no such data is sent.
|
||||
SendDefaultPII bool
|
||||
// BeforeSend is called before error events are sent to Sentry.
|
||||
// Use it to mutate the event or return nil to discard the event.
|
||||
// You can use it to mutate the event or return nil to discard it.
|
||||
BeforeSend func(event *Event, hint *EventHint) *Event
|
||||
// BeforeSendLong is called before log events are sent to Sentry.
|
||||
// You can use it to mutate the log event or return nil to discard it.
|
||||
BeforeSendLog func(event *Log) *Log
|
||||
// BeforeSendTransaction is called before transaction events are sent to Sentry.
|
||||
// Use it to mutate the transaction or return nil to discard the transaction.
|
||||
BeforeSendTransaction func(event *Event, hint *EventHint) *Event
|
||||
|
|
@ -223,6 +226,8 @@ type ClientOptions struct {
|
|||
MaxErrorDepth int
|
||||
// Default event tags. These are overridden by tags set on a scope.
|
||||
Tags map[string]string
|
||||
// EnableLogs controls when logs should be emitted.
|
||||
EnableLogs bool
|
||||
}
|
||||
|
||||
// Client is the underlying processor that is used by the main API and Hub
|
||||
|
|
@ -237,7 +242,8 @@ type Client struct {
|
|||
sdkVersion string
|
||||
// Transport is read-only. Replacing the transport of an existing client is
|
||||
// not supported, create a new client instead.
|
||||
Transport Transport
|
||||
Transport Transport
|
||||
batchLogger *BatchLogger
|
||||
}
|
||||
|
||||
// NewClient creates and returns an instance of Client configured using
|
||||
|
|
@ -275,7 +281,7 @@ func NewClient(options ClientOptions) (*Client, error) {
|
|||
if debugWriter == nil {
|
||||
debugWriter = os.Stderr
|
||||
}
|
||||
Logger.SetOutput(debugWriter)
|
||||
DebugLogger.SetOutput(debugWriter)
|
||||
}
|
||||
|
||||
if options.Dsn == "" {
|
||||
|
|
@ -337,6 +343,11 @@ func NewClient(options ClientOptions) (*Client, error) {
|
|||
sdkVersion: SDKVersion,
|
||||
}
|
||||
|
||||
if options.EnableLogs {
|
||||
client.batchLogger = NewBatchLogger(&client)
|
||||
client.batchLogger.Start()
|
||||
}
|
||||
|
||||
client.setupTransport()
|
||||
client.setupIntegrations()
|
||||
|
||||
|
|
@ -351,15 +362,7 @@ func (client *Client) setupTransport() {
|
|||
if opts.Dsn == "" {
|
||||
transport = new(noopTransport)
|
||||
} else {
|
||||
httpTransport := NewHTTPTransport()
|
||||
// When tracing is enabled, use larger buffer to
|
||||
// accommodate more concurrent events.
|
||||
// TODO(tracing): consider using separate buffers per
|
||||
// event type.
|
||||
if opts.EnableTracing {
|
||||
httpTransport.BufferSize = 1000
|
||||
}
|
||||
transport = httpTransport
|
||||
transport = NewHTTPTransport()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -383,12 +386,12 @@ func (client *Client) setupIntegrations() {
|
|||
|
||||
for _, integration := range integrations {
|
||||
if client.integrationAlreadyInstalled(integration.Name()) {
|
||||
Logger.Printf("Integration %s is already installed\n", integration.Name())
|
||||
DebugLogger.Printf("Integration %s is already installed\n", integration.Name())
|
||||
continue
|
||||
}
|
||||
client.integrations = append(client.integrations, integration)
|
||||
integration.SetupOnce(client)
|
||||
Logger.Printf("Integration installed: %s\n", integration.Name())
|
||||
DebugLogger.Printf("Integration installed: %s\n", integration.Name())
|
||||
}
|
||||
|
||||
sort.Slice(client.integrations, func(i, j int) bool {
|
||||
|
|
@ -507,9 +510,31 @@ func (client *Client) RecoverWithContext(
|
|||
// the network synchronously, configure it to use the HTTPSyncTransport in the
|
||||
// call to Init.
|
||||
func (client *Client) Flush(timeout time.Duration) bool {
|
||||
if client.batchLogger != nil {
|
||||
client.batchLogger.Flush()
|
||||
}
|
||||
return client.Transport.Flush(timeout)
|
||||
}
|
||||
|
||||
// FlushWithContext waits until the underlying Transport sends any buffered events
|
||||
// to the Sentry server, blocking for at most the duration specified by the context.
|
||||
// It returns false if the context is canceled before the events are sent. In such a case,
|
||||
// some events may not be delivered.
|
||||
//
|
||||
// FlushWithContext should be called before terminating the program to ensure no
|
||||
// events are unintentionally dropped.
|
||||
//
|
||||
// Avoid calling FlushWithContext indiscriminately after each call to CaptureEvent,
|
||||
// CaptureException, or CaptureMessage. To send events synchronously over the network,
|
||||
// configure the SDK to use HTTPSyncTransport during initialization with Init.
|
||||
|
||||
func (client *Client) FlushWithContext(ctx context.Context) bool {
|
||||
if client.batchLogger != nil {
|
||||
client.batchLogger.Flush()
|
||||
}
|
||||
return client.Transport.FlushWithContext(ctx)
|
||||
}
|
||||
|
||||
// Close clean up underlying Transport resources.
|
||||
//
|
||||
// Close should be called after Flush and before terminating the program
|
||||
|
|
@ -605,7 +630,7 @@ func (client *Client) processEvent(event *Event, hint *EventHint, scope EventMod
|
|||
// options.TracesSampler when they are started. Other events
|
||||
// (errors, messages) are sampled here. Does not apply to check-ins.
|
||||
if event.Type != transactionType && event.Type != checkInType && !sample(client.options.SampleRate) {
|
||||
Logger.Println("Event dropped due to SampleRate hit.")
|
||||
DebugLogger.Println("Event dropped due to SampleRate hit.")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
@ -617,17 +642,21 @@ func (client *Client) processEvent(event *Event, hint *EventHint, scope EventMod
|
|||
if hint == nil {
|
||||
hint = &EventHint{}
|
||||
}
|
||||
if event.Type == transactionType && client.options.BeforeSendTransaction != nil {
|
||||
// Transaction events
|
||||
if event = client.options.BeforeSendTransaction(event, hint); event == nil {
|
||||
Logger.Println("Transaction dropped due to BeforeSendTransaction callback.")
|
||||
return nil
|
||||
switch event.Type {
|
||||
case transactionType:
|
||||
if client.options.BeforeSendTransaction != nil {
|
||||
if event = client.options.BeforeSendTransaction(event, hint); event == nil {
|
||||
DebugLogger.Println("Transaction dropped due to BeforeSendTransaction callback.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
} else if event.Type != transactionType && event.Type != checkInType && client.options.BeforeSend != nil {
|
||||
// All other events
|
||||
if event = client.options.BeforeSend(event, hint); event == nil {
|
||||
Logger.Println("Event dropped due to BeforeSend callback.")
|
||||
return nil
|
||||
case checkInType: // not a default case, since we shouldn't apply BeforeSend on check-in events
|
||||
default:
|
||||
if client.options.BeforeSend != nil {
|
||||
if event = client.options.BeforeSend(event, hint); event == nil {
|
||||
DebugLogger.Println("Event dropped due to BeforeSend callback.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -692,7 +721,7 @@ func (client *Client) prepareEvent(event *Event, hint *EventHint, scope EventMod
|
|||
id := event.EventID
|
||||
event = processor(event, hint)
|
||||
if event == nil {
|
||||
Logger.Printf("Event dropped by one of the Client EventProcessors: %s\n", id)
|
||||
DebugLogger.Printf("Event dropped by one of the Client EventProcessors: %s\n", id)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
@ -701,7 +730,7 @@ func (client *Client) prepareEvent(event *Event, hint *EventHint, scope EventMod
|
|||
id := event.EventID
|
||||
event = processor(event, hint)
|
||||
if event == nil {
|
||||
Logger.Printf("Event dropped by one of the Global EventProcessors: %s\n", id)
|
||||
DebugLogger.Printf("Event dropped by one of the Global EventProcessors: %s\n", id)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
24
vendor/github.com/getsentry/sentry-go/hub.go
generated
vendored
24
vendor/github.com/getsentry/sentry-go/hub.go
generated
vendored
|
|
@ -308,7 +308,7 @@ func (hub *Hub) AddBreadcrumb(breadcrumb *Breadcrumb, hint *BreadcrumbHint) {
|
|||
hint = &BreadcrumbHint{}
|
||||
}
|
||||
if breadcrumb = client.options.BeforeBreadcrumb(breadcrumb, hint); breadcrumb == nil {
|
||||
Logger.Println("breadcrumb dropped due to BeforeBreadcrumb callback.")
|
||||
DebugLogger.Println("breadcrumb dropped due to BeforeBreadcrumb callback.")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -365,6 +365,28 @@ func (hub *Hub) Flush(timeout time.Duration) bool {
|
|||
return client.Flush(timeout)
|
||||
}
|
||||
|
||||
// FlushWithContext waits until the underlying Transport sends any buffered events
|
||||
// to the Sentry server, blocking for at most the duration specified by the context.
|
||||
// It returns false if the context is canceled before the events are sent. In such a case,
|
||||
// some events may not be delivered.
|
||||
//
|
||||
// FlushWithContext should be called before terminating the program to ensure no
|
||||
// events are unintentionally dropped.
|
||||
//
|
||||
// Avoid calling FlushWithContext indiscriminately after each call to CaptureEvent,
|
||||
// CaptureException, or CaptureMessage. To send events synchronously over the network,
|
||||
// configure the SDK to use HTTPSyncTransport during initialization with Init.
|
||||
|
||||
func (hub *Hub) FlushWithContext(ctx context.Context) bool {
|
||||
client := hub.Client()
|
||||
|
||||
if client == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
return client.FlushWithContext(ctx)
|
||||
}
|
||||
|
||||
// GetTraceparent returns the current Sentry traceparent string, to be used as a HTTP header value
|
||||
// or HTML meta tag value.
|
||||
// This function is context aware, as in it either returns the traceparent based
|
||||
|
|
|
|||
6
vendor/github.com/getsentry/sentry-go/integrations.go
generated
vendored
6
vendor/github.com/getsentry/sentry-go/integrations.go
generated
vendored
|
|
@ -32,7 +32,7 @@ func (mi *modulesIntegration) processor(event *Event, _ *EventHint) *Event {
|
|||
mi.once.Do(func() {
|
||||
info, ok := debug.ReadBuildInfo()
|
||||
if !ok {
|
||||
Logger.Print("The Modules integration is not available in binaries built without module support.")
|
||||
DebugLogger.Print("The Modules integration is not available in binaries built without module support.")
|
||||
return
|
||||
}
|
||||
mi.modules = extractModules(info)
|
||||
|
|
@ -141,7 +141,7 @@ func (iei *ignoreErrorsIntegration) processor(event *Event, _ *EventHint) *Event
|
|||
for _, suspect := range suspects {
|
||||
for _, pattern := range iei.ignoreErrors {
|
||||
if pattern.Match([]byte(suspect)) || strings.Contains(suspect, pattern.String()) {
|
||||
Logger.Printf("Event dropped due to being matched by `IgnoreErrors` option."+
|
||||
DebugLogger.Printf("Event dropped due to being matched by `IgnoreErrors` option."+
|
||||
"| Value matched: %s | Filter used: %s", suspect, pattern)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -203,7 +203,7 @@ func (iei *ignoreTransactionsIntegration) processor(event *Event, _ *EventHint)
|
|||
|
||||
for _, pattern := range iei.ignoreTransactions {
|
||||
if pattern.Match([]byte(suspect)) || strings.Contains(suspect, pattern.String()) {
|
||||
Logger.Printf("Transaction dropped due to being matched by `IgnoreTransactions` option."+
|
||||
DebugLogger.Printf("Transaction dropped due to being matched by `IgnoreTransactions` option."+
|
||||
"| Value matched: %s | Filter used: %s", suspect, pattern)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
115
vendor/github.com/getsentry/sentry-go/interfaces.go
generated
vendored
115
vendor/github.com/getsentry/sentry-go/interfaces.go
generated
vendored
|
|
@ -11,17 +11,22 @@ import (
|
|||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go/attribute"
|
||||
)
|
||||
|
||||
// eventType is the type of an error event.
|
||||
const eventType = "event"
|
||||
|
||||
// transactionType is the type of a transaction event.
|
||||
const transactionType = "transaction"
|
||||
|
||||
// checkInType is the type of a check in event.
|
||||
const checkInType = "check_in"
|
||||
|
||||
var logEvent = struct {
|
||||
Type string
|
||||
ContentType string
|
||||
}{
|
||||
"log",
|
||||
"application/vnd.sentry.items.log+json",
|
||||
}
|
||||
|
||||
// Level marks the severity of the event.
|
||||
type Level string
|
||||
|
||||
|
|
@ -34,7 +39,7 @@ const (
|
|||
LevelFatal Level = "fatal"
|
||||
)
|
||||
|
||||
// SdkInfo contains all metadata about about the SDK being used.
|
||||
// SdkInfo contains all metadata about the SDK.
|
||||
type SdkInfo struct {
|
||||
Name string `json:"name,omitempty"`
|
||||
Version string `json:"version,omitempty"`
|
||||
|
|
@ -96,6 +101,60 @@ func (b *Breadcrumb) MarshalJSON() ([]byte, error) {
|
|||
return json.Marshal((*breadcrumb)(b))
|
||||
}
|
||||
|
||||
type Logger interface {
|
||||
// Write implements the io.Writer interface. Currently, the [sentry.Hub] is
|
||||
// context aware, in order to get the correct trace correlation. Using this
|
||||
// might result in incorrect span association on logs. If you need to use
|
||||
// Write it is recommended to create a NewLogger so that the associated context
|
||||
// is passed correctly.
|
||||
Write(p []byte) (n int, err error)
|
||||
// Trace emits a [LogLevelTrace] log to Sentry.
|
||||
// Arguments are handled in the manner of [fmt.Print].
|
||||
Trace(ctx context.Context, v ...interface{})
|
||||
// Debug emits a [LogLevelDebug] log to Sentry.
|
||||
// Arguments are handled in the manner of [fmt.Print].
|
||||
Debug(ctx context.Context, v ...interface{})
|
||||
// Info emits a [LogLevelInfo] log to Sentry.
|
||||
// Arguments are handled in the manner of [fmt.Print].
|
||||
Info(ctx context.Context, v ...interface{})
|
||||
// Warn emits a [LogLevelWarn] log to Sentry.
|
||||
// Arguments are handled in the manner of [fmt.Print].
|
||||
Warn(ctx context.Context, v ...interface{})
|
||||
// Error emits a [LogLevelError] log to Sentry.
|
||||
// Arguments are handled in the manner of [fmt.Print].
|
||||
Error(ctx context.Context, v ...interface{})
|
||||
// Fatal emits a [LogLevelFatal] log to Sentry followed by a call to [os.Exit](1).
|
||||
// Arguments are handled in the manner of [fmt.Print].
|
||||
Fatal(ctx context.Context, v ...interface{})
|
||||
// Panic emits a [LogLevelFatal] log to Sentry followed by a call to panic().
|
||||
// Arguments are handled in the manner of [fmt.Print].
|
||||
Panic(ctx context.Context, v ...interface{})
|
||||
|
||||
// Tracef emits a [LogLevelTrace] log to Sentry.
|
||||
// Arguments are handled in the manner of [fmt.Printf].
|
||||
Tracef(ctx context.Context, format string, v ...interface{})
|
||||
// Debugf emits a [LogLevelDebug] log to Sentry.
|
||||
// Arguments are handled in the manner of [fmt.Printf].
|
||||
Debugf(ctx context.Context, format string, v ...interface{})
|
||||
// Infof emits a [LogLevelInfo] log to Sentry.
|
||||
// Arguments are handled in the manner of [fmt.Printf].
|
||||
Infof(ctx context.Context, format string, v ...interface{})
|
||||
// Warnf emits a [LogLevelWarn] log to Sentry.
|
||||
// Arguments are handled in the manner of [fmt.Printf].
|
||||
Warnf(ctx context.Context, format string, v ...interface{})
|
||||
// Errorf emits a [LogLevelError] log to Sentry.
|
||||
// Arguments are handled in the manner of [fmt.Printf].
|
||||
Errorf(ctx context.Context, format string, v ...interface{})
|
||||
// Fatalf emits a [LogLevelFatal] log to Sentry followed by a call to [os.Exit](1).
|
||||
// Arguments are handled in the manner of [fmt.Printf].
|
||||
Fatalf(ctx context.Context, format string, v ...interface{})
|
||||
// Panicf emits a [LogLevelFatal] log to Sentry followed by a call to panic().
|
||||
// Arguments are handled in the manner of [fmt.Printf].
|
||||
Panicf(ctx context.Context, format string, v ...interface{})
|
||||
// SetAttributes allows attaching parameters to the log message using the attribute API.
|
||||
SetAttributes(...attribute.Builder)
|
||||
}
|
||||
|
||||
// Attachment allows associating files with your events to aid in investigation.
|
||||
// An event may contain one or more attachments.
|
||||
type Attachment struct {
|
||||
|
|
@ -155,11 +214,36 @@ type Request struct {
|
|||
}
|
||||
|
||||
var sensitiveHeaders = map[string]struct{}{
|
||||
"_csrf": {},
|
||||
"_csrf_token": {},
|
||||
"_session": {},
|
||||
"_xsrf": {},
|
||||
"Api-Key": {},
|
||||
"Apikey": {},
|
||||
"Auth": {},
|
||||
"Authorization": {},
|
||||
"Proxy-Authorization": {},
|
||||
"Cookie": {},
|
||||
"Credentials": {},
|
||||
"Csrf": {},
|
||||
"Csrf-Token": {},
|
||||
"Csrftoken": {},
|
||||
"Ip-Address": {},
|
||||
"Passwd": {},
|
||||
"Password": {},
|
||||
"Private-Key": {},
|
||||
"Privatekey": {},
|
||||
"Proxy-Authorization": {},
|
||||
"Remote-Addr": {},
|
||||
"Secret": {},
|
||||
"Session": {},
|
||||
"Sessionid": {},
|
||||
"Token": {},
|
||||
"User-Session": {},
|
||||
"X-Api-Key": {},
|
||||
"X-Csrftoken": {},
|
||||
"X-Forwarded-For": {},
|
||||
"X-Real-Ip": {},
|
||||
"XSRF-TOKEN": {},
|
||||
}
|
||||
|
||||
// NewRequest returns a new Sentry Request from the given http.Request.
|
||||
|
|
@ -325,6 +409,9 @@ type Event struct {
|
|||
CheckIn *CheckIn `json:"check_in,omitempty"`
|
||||
MonitorConfig *MonitorConfig `json:"monitor_config,omitempty"`
|
||||
|
||||
// The fields below are only relevant for logs
|
||||
Logs []Log `json:"items,omitempty"`
|
||||
|
||||
// The fields below are not part of the final JSON payload.
|
||||
|
||||
sdkMetaData SDKMetaData
|
||||
|
|
@ -547,3 +634,17 @@ type EventHint struct {
|
|||
Request *http.Request
|
||||
Response *http.Response
|
||||
}
|
||||
|
||||
type Log struct {
|
||||
Timestamp time.Time `json:"timestamp,omitempty"`
|
||||
TraceID TraceID `json:"trace_id,omitempty"`
|
||||
Level LogLevel `json:"level"`
|
||||
Severity int `json:"severity_number,omitempty"`
|
||||
Body string `json:"body,omitempty"`
|
||||
Attributes map[string]Attribute `json:"attributes,omitempty"`
|
||||
}
|
||||
|
||||
type Attribute struct {
|
||||
Value any `json:"value"`
|
||||
Type string `json:"type"`
|
||||
}
|
||||
|
|
|
|||
230
vendor/github.com/getsentry/sentry-go/log.go
generated
vendored
Normal file
230
vendor/github.com/getsentry/sentry-go/log.go
generated
vendored
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
package sentry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/getsentry/sentry-go/attribute"
|
||||
)
|
||||
|
||||
type LogLevel string
|
||||
|
||||
const (
|
||||
LogLevelTrace LogLevel = "trace"
|
||||
LogLevelDebug LogLevel = "debug"
|
||||
LogLevelInfo LogLevel = "info"
|
||||
LogLevelWarn LogLevel = "warn"
|
||||
LogLevelError LogLevel = "error"
|
||||
LogLevelFatal LogLevel = "fatal"
|
||||
)
|
||||
|
||||
const (
|
||||
LogSeverityTrace int = 1
|
||||
LogSeverityDebug int = 5
|
||||
LogSeverityInfo int = 9
|
||||
LogSeverityWarning int = 13
|
||||
LogSeverityError int = 17
|
||||
LogSeverityFatal int = 21
|
||||
)
|
||||
|
||||
var mapTypesToStr = map[attribute.Type]string{
|
||||
attribute.INVALID: "",
|
||||
attribute.BOOL: "boolean",
|
||||
attribute.INT64: "integer",
|
||||
attribute.FLOAT64: "double",
|
||||
attribute.STRING: "string",
|
||||
}
|
||||
|
||||
type sentryLogger struct {
|
||||
client *Client
|
||||
attributes map[string]Attribute
|
||||
}
|
||||
|
||||
// NewLogger returns a Logger that emits logs to Sentry. If logging is turned off, all logs get discarded.
|
||||
func NewLogger(ctx context.Context) Logger {
|
||||
var hub *Hub
|
||||
hub = GetHubFromContext(ctx)
|
||||
if hub == nil {
|
||||
hub = CurrentHub()
|
||||
}
|
||||
|
||||
client := hub.Client()
|
||||
if client != nil && client.batchLogger != nil {
|
||||
return &sentryLogger{client, make(map[string]Attribute)}
|
||||
}
|
||||
|
||||
DebugLogger.Println("fallback to noopLogger: enableLogs disabled")
|
||||
return &noopLogger{} // fallback: does nothing
|
||||
}
|
||||
|
||||
func (l *sentryLogger) Write(p []byte) (int, error) {
|
||||
// Avoid sending double newlines to Sentry
|
||||
msg := strings.TrimRight(string(p), "\n")
|
||||
l.log(context.Background(), LogLevelInfo, LogSeverityInfo, msg)
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (l *sentryLogger) log(ctx context.Context, level LogLevel, severity int, message string, args ...interface{}) {
|
||||
if message == "" {
|
||||
return
|
||||
}
|
||||
hub := GetHubFromContext(ctx)
|
||||
if hub == nil {
|
||||
hub = CurrentHub()
|
||||
}
|
||||
|
||||
var traceID TraceID
|
||||
var spanID SpanID
|
||||
|
||||
span := hub.Scope().span
|
||||
if span != nil {
|
||||
traceID = span.TraceID
|
||||
spanID = span.SpanID
|
||||
} else {
|
||||
traceID = hub.Scope().propagationContext.TraceID
|
||||
}
|
||||
|
||||
attrs := map[string]Attribute{}
|
||||
if len(args) > 0 {
|
||||
attrs["sentry.message.template"] = Attribute{
|
||||
Value: message, Type: "string",
|
||||
}
|
||||
for i, p := range args {
|
||||
attrs[fmt.Sprintf("sentry.message.parameters.%d", i)] = Attribute{
|
||||
Value: fmt.Sprint(p), Type: "string",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If `log` was called with SetAttributes, pass the attributes to attrs
|
||||
if len(l.attributes) > 0 {
|
||||
for k, v := range l.attributes {
|
||||
attrs[k] = v
|
||||
}
|
||||
// flush attributes from logger after send
|
||||
clear(l.attributes)
|
||||
}
|
||||
|
||||
// Set default attributes
|
||||
if release := l.client.options.Release; release != "" {
|
||||
attrs["sentry.release"] = Attribute{Value: release, Type: "string"}
|
||||
}
|
||||
if environment := l.client.options.Environment; environment != "" {
|
||||
attrs["sentry.environment"] = Attribute{Value: environment, Type: "string"}
|
||||
}
|
||||
if serverName := l.client.options.ServerName; serverName != "" {
|
||||
attrs["sentry.server.address"] = Attribute{Value: serverName, Type: "string"}
|
||||
} else if serverAddr, err := os.Hostname(); err == nil {
|
||||
attrs["sentry.server.address"] = Attribute{Value: serverAddr, Type: "string"}
|
||||
}
|
||||
scope := hub.Scope()
|
||||
if scope != nil {
|
||||
user := scope.user
|
||||
if !user.IsEmpty() {
|
||||
if user.ID != "" {
|
||||
attrs["user.id"] = Attribute{Value: user.ID, Type: "string"}
|
||||
}
|
||||
if user.Name != "" {
|
||||
attrs["user.name"] = Attribute{Value: user.Name, Type: "string"}
|
||||
}
|
||||
if user.Email != "" {
|
||||
attrs["user.email"] = Attribute{Value: user.Email, Type: "string"}
|
||||
}
|
||||
}
|
||||
}
|
||||
if spanID.String() != "0000000000000000" {
|
||||
attrs["sentry.trace.parent_span_id"] = Attribute{Value: spanID.String(), Type: "string"}
|
||||
}
|
||||
if sdkIdentifier := l.client.sdkIdentifier; sdkIdentifier != "" {
|
||||
attrs["sentry.sdk.name"] = Attribute{Value: sdkIdentifier, Type: "string"}
|
||||
}
|
||||
if sdkVersion := l.client.sdkVersion; sdkVersion != "" {
|
||||
attrs["sentry.sdk.version"] = Attribute{Value: sdkVersion, Type: "string"}
|
||||
}
|
||||
|
||||
log := &Log{
|
||||
Timestamp: time.Now(),
|
||||
TraceID: traceID,
|
||||
Level: level,
|
||||
Severity: severity,
|
||||
Body: fmt.Sprintf(message, args...),
|
||||
Attributes: attrs,
|
||||
}
|
||||
|
||||
if l.client.options.BeforeSendLog != nil {
|
||||
log = l.client.options.BeforeSendLog(log)
|
||||
}
|
||||
|
||||
if log != nil {
|
||||
l.client.batchLogger.logCh <- *log
|
||||
}
|
||||
|
||||
if l.client.options.Debug {
|
||||
DebugLogger.Printf(message, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (l *sentryLogger) SetAttributes(attrs ...attribute.Builder) {
|
||||
for _, v := range attrs {
|
||||
t, ok := mapTypesToStr[v.Value.Type()]
|
||||
if !ok || t == "" {
|
||||
DebugLogger.Printf("invalid attribute type set: %v", t)
|
||||
continue
|
||||
}
|
||||
|
||||
l.attributes[v.Key] = Attribute{
|
||||
Value: v.Value.AsInterface(),
|
||||
Type: t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (l *sentryLogger) Trace(ctx context.Context, v ...interface{}) {
|
||||
l.log(ctx, LogLevelTrace, LogSeverityTrace, fmt.Sprint(v...))
|
||||
}
|
||||
func (l *sentryLogger) Debug(ctx context.Context, v ...interface{}) {
|
||||
l.log(ctx, LogLevelDebug, LogSeverityDebug, fmt.Sprint(v...))
|
||||
}
|
||||
func (l *sentryLogger) Info(ctx context.Context, v ...interface{}) {
|
||||
l.log(ctx, LogLevelInfo, LogSeverityInfo, fmt.Sprint(v...))
|
||||
}
|
||||
func (l *sentryLogger) Warn(ctx context.Context, v ...interface{}) {
|
||||
l.log(ctx, LogLevelWarn, LogSeverityWarning, fmt.Sprint(v...))
|
||||
}
|
||||
func (l *sentryLogger) Error(ctx context.Context, v ...interface{}) {
|
||||
l.log(ctx, LogLevelError, LogSeverityError, fmt.Sprint(v...))
|
||||
}
|
||||
func (l *sentryLogger) Fatal(ctx context.Context, v ...interface{}) {
|
||||
l.log(ctx, LogLevelFatal, LogSeverityFatal, fmt.Sprint(v...))
|
||||
os.Exit(1)
|
||||
}
|
||||
func (l *sentryLogger) Panic(ctx context.Context, v ...interface{}) {
|
||||
l.log(ctx, LogLevelFatal, LogSeverityFatal, fmt.Sprint(v...))
|
||||
panic(fmt.Sprint(v...))
|
||||
}
|
||||
func (l *sentryLogger) Tracef(ctx context.Context, format string, v ...interface{}) {
|
||||
l.log(ctx, LogLevelTrace, LogSeverityTrace, format, v...)
|
||||
}
|
||||
func (l *sentryLogger) Debugf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.log(ctx, LogLevelDebug, LogSeverityDebug, format, v...)
|
||||
}
|
||||
func (l *sentryLogger) Infof(ctx context.Context, format string, v ...interface{}) {
|
||||
l.log(ctx, LogLevelInfo, LogSeverityInfo, format, v...)
|
||||
}
|
||||
func (l *sentryLogger) Warnf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.log(ctx, LogLevelWarn, LogSeverityWarning, format, v...)
|
||||
}
|
||||
func (l *sentryLogger) Errorf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.log(ctx, LogLevelError, LogSeverityError, format, v...)
|
||||
}
|
||||
func (l *sentryLogger) Fatalf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.log(ctx, LogLevelFatal, LogSeverityFatal, format, v...)
|
||||
os.Exit(1)
|
||||
}
|
||||
func (l *sentryLogger) Panicf(ctx context.Context, format string, v ...interface{}) {
|
||||
l.log(ctx, LogLevelFatal, LogSeverityFatal, format, v...)
|
||||
panic(fmt.Sprint(v...))
|
||||
}
|
||||
65
vendor/github.com/getsentry/sentry-go/log_fallback.go
generated
vendored
Normal file
65
vendor/github.com/getsentry/sentry-go/log_fallback.go
generated
vendored
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
package sentry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/getsentry/sentry-go/attribute"
|
||||
)
|
||||
|
||||
// Fallback, no-op logger if logging is disabled.
|
||||
type noopLogger struct{}
|
||||
|
||||
func (*noopLogger) Trace(_ context.Context, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelTrace)
|
||||
}
|
||||
func (*noopLogger) Debug(_ context.Context, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelDebug)
|
||||
}
|
||||
func (*noopLogger) Info(_ context.Context, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelInfo)
|
||||
}
|
||||
func (*noopLogger) Warn(_ context.Context, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelWarn)
|
||||
}
|
||||
func (*noopLogger) Error(_ context.Context, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelError)
|
||||
}
|
||||
func (*noopLogger) Fatal(_ context.Context, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelFatal)
|
||||
os.Exit(1)
|
||||
}
|
||||
func (*noopLogger) Panic(_ context.Context, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelFatal)
|
||||
panic(fmt.Sprintf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelFatal))
|
||||
}
|
||||
func (*noopLogger) Tracef(_ context.Context, _ string, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelTrace)
|
||||
}
|
||||
func (*noopLogger) Debugf(_ context.Context, _ string, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelDebug)
|
||||
}
|
||||
func (*noopLogger) Infof(_ context.Context, _ string, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelInfo)
|
||||
}
|
||||
func (*noopLogger) Warnf(_ context.Context, _ string, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelWarn)
|
||||
}
|
||||
func (*noopLogger) Errorf(_ context.Context, _ string, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelError)
|
||||
}
|
||||
func (*noopLogger) Fatalf(_ context.Context, _ string, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelFatal)
|
||||
os.Exit(1)
|
||||
}
|
||||
func (*noopLogger) Panicf(_ context.Context, _ string, _ ...interface{}) {
|
||||
DebugLogger.Printf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelFatal)
|
||||
panic(fmt.Sprintf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelFatal))
|
||||
}
|
||||
func (*noopLogger) SetAttributes(...attribute.Builder) {
|
||||
DebugLogger.Printf("No attributes attached. Turn on logging via EnableLogs")
|
||||
}
|
||||
func (*noopLogger) Write(_ []byte) (n int, err error) {
|
||||
return 0, fmt.Errorf("Log with level=[%v] is being dropped. Turn on logging via EnableLogs", LogLevelInfo)
|
||||
}
|
||||
2
vendor/github.com/getsentry/sentry-go/mocks.go
generated
vendored
2
vendor/github.com/getsentry/sentry-go/mocks.go
generated
vendored
|
|
@ -1,6 +1,7 @@
|
|||
package sentry
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
|
@ -39,6 +40,7 @@ func (t *MockTransport) SendEvent(event *Event) {
|
|||
func (t *MockTransport) Flush(_ time.Duration) bool {
|
||||
return true
|
||||
}
|
||||
func (t *MockTransport) FlushWithContext(_ context.Context) bool { return true }
|
||||
func (t *MockTransport) Events() []*Event {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
|
|
|
|||
7
vendor/github.com/getsentry/sentry-go/scope.go
generated
vendored
7
vendor/github.com/getsentry/sentry-go/scope.go
generated
vendored
|
|
@ -302,6 +302,11 @@ func (scope *Scope) SetPropagationContext(propagationContext PropagationContext)
|
|||
scope.propagationContext = propagationContext
|
||||
}
|
||||
|
||||
// GetSpan returns the span from the current scope.
|
||||
func (scope *Scope) GetSpan() *Span {
|
||||
return scope.span
|
||||
}
|
||||
|
||||
// SetSpan sets a span for the current scope.
|
||||
func (scope *Scope) SetSpan(span *Span) {
|
||||
scope.mu.Lock()
|
||||
|
|
@ -464,7 +469,7 @@ func (scope *Scope) ApplyToEvent(event *Event, hint *EventHint, client *Client)
|
|||
id := event.EventID
|
||||
event = processor(event, hint)
|
||||
if event == nil {
|
||||
Logger.Printf("Event dropped by one of the Scope EventProcessors: %s\n", id)
|
||||
DebugLogger.Printf("Event dropped by one of the Scope EventProcessors: %s\n", id)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
|
|
|||
19
vendor/github.com/getsentry/sentry-go/sentry.go
generated
vendored
19
vendor/github.com/getsentry/sentry-go/sentry.go
generated
vendored
|
|
@ -6,7 +6,7 @@ import (
|
|||
)
|
||||
|
||||
// The version of the SDK.
|
||||
const SDKVersion = "0.32.0"
|
||||
const SDKVersion = "0.34.0"
|
||||
|
||||
// apiVersion is the minimum version of the Sentry API compatible with the
|
||||
// sentry-go SDK.
|
||||
|
|
@ -125,6 +125,23 @@ func Flush(timeout time.Duration) bool {
|
|||
return hub.Flush(timeout)
|
||||
}
|
||||
|
||||
// FlushWithContext waits until the underlying Transport sends any buffered events
|
||||
// to the Sentry server, blocking for at most the duration specified by the context.
|
||||
// It returns false if the context is canceled before the events are sent. In such a case,
|
||||
// some events may not be delivered.
|
||||
//
|
||||
// FlushWithContext should be called before terminating the program to ensure no
|
||||
// events are unintentionally dropped.
|
||||
//
|
||||
// Avoid calling FlushWithContext indiscriminately after each call to CaptureEvent,
|
||||
// CaptureException, or CaptureMessage. To send events synchronously over the network,
|
||||
// configure the SDK to use HTTPSyncTransport during initialization with Init.
|
||||
|
||||
func FlushWithContext(ctx context.Context) bool {
|
||||
hub := CurrentHub()
|
||||
return hub.FlushWithContext(ctx)
|
||||
}
|
||||
|
||||
// LastEventID returns an ID of last captured event.
|
||||
func LastEventID() EventID {
|
||||
hub := CurrentHub()
|
||||
|
|
|
|||
2
vendor/github.com/getsentry/sentry-go/span_recorder.go
generated
vendored
2
vendor/github.com/getsentry/sentry-go/span_recorder.go
generated
vendored
|
|
@ -24,7 +24,7 @@ func (r *spanRecorder) record(s *Span) {
|
|||
if len(r.spans) >= maxSpans {
|
||||
r.overflowOnce.Do(func() {
|
||||
root := r.spans[0]
|
||||
Logger.Printf("Too many spans: dropping spans from transaction with TraceID=%s SpanID=%s limit=%d",
|
||||
DebugLogger.Printf("Too many spans: dropping spans from transaction with TraceID=%s SpanID=%s limit=%d",
|
||||
root.TraceID, root.SpanID, maxSpans)
|
||||
})
|
||||
// TODO(tracing): mark the transaction event in some way to
|
||||
|
|
|
|||
79
vendor/github.com/getsentry/sentry-go/tracing.go
generated
vendored
79
vendor/github.com/getsentry/sentry-go/tracing.go
generated
vendored
|
|
@ -8,6 +8,7 @@ import (
|
|||
"fmt"
|
||||
"net/http"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
|
@ -72,6 +73,8 @@ type Span struct { //nolint: maligned // prefer readability over optimal memory
|
|||
contexts map[string]Context
|
||||
// a Once instance to make sure that Finish() is only called once.
|
||||
finishOnce sync.Once
|
||||
// explicitSampled is a flag for configuring sampling by using `WithSpanSampled` option.
|
||||
explicitSampled Sampled
|
||||
}
|
||||
|
||||
// TraceParentContext describes the context of a (remote) parent span.
|
||||
|
|
@ -195,10 +198,6 @@ func StartSpan(ctx context.Context, operation string, options ...SpanOption) *Sp
|
|||
clientOptions := span.clientOptions()
|
||||
if clientOptions.EnableTracing {
|
||||
hub := hubFromContext(ctx)
|
||||
if !span.IsTransaction() {
|
||||
// Push a new scope to stack for non transaction span
|
||||
hub.PushScope()
|
||||
}
|
||||
hub.Scope().SetSpan(&span)
|
||||
}
|
||||
|
||||
|
|
@ -302,7 +301,7 @@ func (s *Span) GetTransaction() *Span {
|
|||
// func (s *Span) TransactionName() string
|
||||
// func (s *Span) SetTransactionName(name string)
|
||||
|
||||
// ToSentryTrace returns the seralized TraceParentContext from a transaction/span.
|
||||
// ToSentryTrace returns the serialized TraceParentContext from a transaction/span.
|
||||
// Use this function to propagate the TraceParentContext to a downstream SDK,
|
||||
// either as the value of the "sentry-trace" HTTP header, or as an html "sentry-trace" meta tag.
|
||||
func (s *Span) ToSentryTrace() string {
|
||||
|
|
@ -356,8 +355,9 @@ func (s *Span) doFinish() {
|
|||
|
||||
hub := hubFromContext(s.ctx)
|
||||
if !s.IsTransaction() {
|
||||
// Referenced to StartSpan function that pushes current non-transaction span to scope stack
|
||||
defer hub.PopScope()
|
||||
if s.parent != nil {
|
||||
hub.Scope().SetSpan(s.parent)
|
||||
}
|
||||
}
|
||||
|
||||
if !s.Sampled.Bool() {
|
||||
|
|
@ -449,21 +449,21 @@ func (s *Span) sample() Sampled {
|
|||
// https://develop.sentry.dev/sdk/performance/#sampling
|
||||
// #1 tracing is not enabled.
|
||||
if !clientOptions.EnableTracing {
|
||||
Logger.Printf("Dropping transaction: EnableTracing is set to %t", clientOptions.EnableTracing)
|
||||
DebugLogger.Printf("Dropping transaction: EnableTracing is set to %t", clientOptions.EnableTracing)
|
||||
s.sampleRate = 0.0
|
||||
return SampledFalse
|
||||
}
|
||||
|
||||
// #2 explicit sampling decision via StartSpan/StartTransaction options.
|
||||
if s.Sampled != SampledUndefined {
|
||||
Logger.Printf("Using explicit sampling decision from StartSpan/StartTransaction: %v", s.Sampled)
|
||||
switch s.Sampled {
|
||||
if s.explicitSampled != SampledUndefined {
|
||||
DebugLogger.Printf("Using explicit sampling decision from StartSpan/StartTransaction: %v", s.explicitSampled)
|
||||
switch s.explicitSampled {
|
||||
case SampledTrue:
|
||||
s.sampleRate = 1.0
|
||||
case SampledFalse:
|
||||
s.sampleRate = 0.0
|
||||
}
|
||||
return s.Sampled
|
||||
return s.explicitSampled
|
||||
}
|
||||
|
||||
// Variant for non-transaction spans: they inherit the parent decision.
|
||||
|
|
@ -484,42 +484,52 @@ func (s *Span) sample() Sampled {
|
|||
if sampler != nil {
|
||||
tracesSamplerSampleRate := sampler.Sample(samplingContext)
|
||||
s.sampleRate = tracesSamplerSampleRate
|
||||
// tracesSampler can update the sample_rate on frozen DSC
|
||||
if s.dynamicSamplingContext.HasEntries() {
|
||||
s.dynamicSamplingContext.Entries["sample_rate"] = strconv.FormatFloat(tracesSamplerSampleRate, 'f', -1, 64)
|
||||
}
|
||||
if tracesSamplerSampleRate < 0.0 || tracesSamplerSampleRate > 1.0 {
|
||||
Logger.Printf("Dropping transaction: Returned TracesSampler rate is out of range [0.0, 1.0]: %f", tracesSamplerSampleRate)
|
||||
DebugLogger.Printf("Dropping transaction: Returned TracesSampler rate is out of range [0.0, 1.0]: %f", tracesSamplerSampleRate)
|
||||
return SampledFalse
|
||||
}
|
||||
if tracesSamplerSampleRate == 0 {
|
||||
Logger.Printf("Dropping transaction: Returned TracesSampler rate is: %f", tracesSamplerSampleRate)
|
||||
if tracesSamplerSampleRate == 0.0 {
|
||||
DebugLogger.Printf("Dropping transaction: Returned TracesSampler rate is: %f", tracesSamplerSampleRate)
|
||||
return SampledFalse
|
||||
}
|
||||
|
||||
if rng.Float64() < tracesSamplerSampleRate {
|
||||
return SampledTrue
|
||||
}
|
||||
Logger.Printf("Dropping transaction: TracesSampler returned rate: %f", tracesSamplerSampleRate)
|
||||
DebugLogger.Printf("Dropping transaction: TracesSampler returned rate: %f", tracesSamplerSampleRate)
|
||||
|
||||
return SampledFalse
|
||||
}
|
||||
|
||||
// #4 inherit parent decision.
|
||||
if s.parent != nil {
|
||||
Logger.Printf("Using sampling decision from parent: %v", s.parent.Sampled)
|
||||
switch s.parent.Sampled {
|
||||
if s.Sampled != SampledUndefined {
|
||||
DebugLogger.Printf("Using sampling decision from parent: %v", s.Sampled)
|
||||
switch s.Sampled {
|
||||
case SampledTrue:
|
||||
s.sampleRate = 1.0
|
||||
case SampledFalse:
|
||||
s.sampleRate = 0.0
|
||||
}
|
||||
return s.parent.Sampled
|
||||
return s.Sampled
|
||||
}
|
||||
|
||||
// #5 use TracesSampleRate from ClientOptions.
|
||||
sampleRate := clientOptions.TracesSampleRate
|
||||
s.sampleRate = sampleRate
|
||||
// tracesSampleRate can update the sample_rate on frozen DSC
|
||||
if s.dynamicSamplingContext.HasEntries() {
|
||||
s.dynamicSamplingContext.Entries["sample_rate"] = strconv.FormatFloat(sampleRate, 'f', -1, 64)
|
||||
}
|
||||
if sampleRate < 0.0 || sampleRate > 1.0 {
|
||||
Logger.Printf("Dropping transaction: TracesSamplerRate out of range [0.0, 1.0]: %f", sampleRate)
|
||||
DebugLogger.Printf("Dropping transaction: TracesSampleRate out of range [0.0, 1.0]: %f", sampleRate)
|
||||
return SampledFalse
|
||||
}
|
||||
if sampleRate == 0.0 {
|
||||
Logger.Printf("Dropping transaction: TracesSampleRate rate is: %f", sampleRate)
|
||||
DebugLogger.Printf("Dropping transaction: TracesSampleRate rate is: %f", sampleRate)
|
||||
return SampledFalse
|
||||
}
|
||||
|
||||
|
|
@ -542,7 +552,7 @@ func (s *Span) toEvent() *Event {
|
|||
finished := make([]*Span, 0, len(children))
|
||||
for _, child := range children {
|
||||
if child.EndTime.IsZero() {
|
||||
Logger.Printf("Dropped unfinished span: Op=%q TraceID=%s SpanID=%s", child.Op, child.TraceID, child.SpanID)
|
||||
DebugLogger.Printf("Dropped unfinished span: Op=%q TraceID=%s SpanID=%s", child.Op, child.TraceID, child.SpanID)
|
||||
continue
|
||||
}
|
||||
finished = append(finished, child)
|
||||
|
|
@ -892,7 +902,7 @@ func WithTransactionSource(source TransactionSource) SpanOption {
|
|||
// WithSpanSampled updates the sampling flag for a given span.
|
||||
func WithSpanSampled(sampled Sampled) SpanOption {
|
||||
return func(s *Span) {
|
||||
s.Sampled = sampled
|
||||
s.explicitSampled = sampled
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -903,7 +913,7 @@ func WithSpanOrigin(origin SpanOrigin) SpanOption {
|
|||
}
|
||||
}
|
||||
|
||||
// Continue a trace based on traceparent and bagge values.
|
||||
// ContinueTrace continues a trace based on traceparent and baggage values.
|
||||
// If the SDK is configured with tracing enabled,
|
||||
// this function returns populated SpanOption.
|
||||
// In any other cases, it populates the propagation context on the scope.
|
||||
|
|
@ -932,16 +942,17 @@ func ContinueFromHeaders(trace, baggage string) SpanOption {
|
|||
return func(s *Span) {
|
||||
if trace != "" {
|
||||
s.updateFromSentryTrace([]byte(trace))
|
||||
}
|
||||
if baggage != "" {
|
||||
s.updateFromBaggage([]byte(baggage))
|
||||
}
|
||||
|
||||
// In case a sentry-trace header is present but there are no sentry-related
|
||||
// values in the baggage, create an empty, frozen DynamicSamplingContext.
|
||||
if trace != "" && !s.dynamicSamplingContext.HasEntries() {
|
||||
s.dynamicSamplingContext = DynamicSamplingContext{
|
||||
Frozen: true,
|
||||
if baggage != "" {
|
||||
s.updateFromBaggage([]byte(baggage))
|
||||
}
|
||||
|
||||
// In case a sentry-trace header is present but there are no sentry-related
|
||||
// values in the baggage, create an empty, frozen DynamicSamplingContext.
|
||||
if !s.dynamicSamplingContext.HasEntries() {
|
||||
s.dynamicSamplingContext = DynamicSamplingContext{
|
||||
Frozen: true,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
105
vendor/github.com/getsentry/sentry-go/transport.go
generated
vendored
105
vendor/github.com/getsentry/sentry-go/transport.go
generated
vendored
|
|
@ -16,8 +16,10 @@ import (
|
|||
"github.com/getsentry/sentry-go/internal/ratelimit"
|
||||
)
|
||||
|
||||
const defaultBufferSize = 30
|
||||
const defaultTimeout = time.Second * 30
|
||||
const (
|
||||
defaultBufferSize = 1000
|
||||
defaultTimeout = time.Second * 30
|
||||
)
|
||||
|
||||
// maxDrainResponseBytes is the maximum number of bytes that transport
|
||||
// implementations will read from response bodies when draining them.
|
||||
|
|
@ -33,6 +35,7 @@ const maxDrainResponseBytes = 16 << 10
|
|||
// Transport is used by the Client to deliver events to remote server.
|
||||
type Transport interface {
|
||||
Flush(timeout time.Duration) bool
|
||||
FlushWithContext(ctx context.Context) bool
|
||||
Configure(options ClientOptions)
|
||||
SendEvent(event *Event)
|
||||
Close()
|
||||
|
|
@ -84,14 +87,14 @@ func getRequestBodyFromEvent(event *Event) []byte {
|
|||
}
|
||||
body, err = json.Marshal(event)
|
||||
if err == nil {
|
||||
Logger.Println(msg)
|
||||
DebugLogger.Println(msg)
|
||||
return body
|
||||
}
|
||||
|
||||
// This should _only_ happen when Event.Exception[0].Stacktrace.Frames[0].Vars is unserializable
|
||||
// Which won't ever happen, as we don't use it now (although it's the part of public interface accepted by Sentry)
|
||||
// Juuust in case something, somehow goes utterly wrong.
|
||||
Logger.Println("Event couldn't be marshaled, even with stripped contextual data. Skipping delivery. " +
|
||||
DebugLogger.Println("Event couldn't be marshaled, even with stripped contextual data. Skipping delivery. " +
|
||||
"Please notify the SDK owners with possibly broken payload.")
|
||||
return nil
|
||||
}
|
||||
|
|
@ -144,6 +147,23 @@ func encodeEnvelopeItem(enc *json.Encoder, itemType string, body json.RawMessage
|
|||
return err
|
||||
}
|
||||
|
||||
func encodeEnvelopeLogs(enc *json.Encoder, itemsLength int, body json.RawMessage) error {
|
||||
err := enc.Encode(
|
||||
struct {
|
||||
Type string `json:"type"`
|
||||
ItemCount int `json:"item_count"`
|
||||
ContentType string `json:"content_type"`
|
||||
}{
|
||||
Type: logEvent.Type,
|
||||
ItemCount: itemsLength,
|
||||
ContentType: logEvent.ContentType,
|
||||
})
|
||||
if err == nil {
|
||||
err = enc.Encode(body)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func envelopeFromBody(event *Event, dsn *Dsn, sentAt time.Time, body json.RawMessage) (*bytes.Buffer, error) {
|
||||
var b bytes.Buffer
|
||||
enc := json.NewEncoder(&b)
|
||||
|
|
@ -180,6 +200,8 @@ func envelopeFromBody(event *Event, dsn *Dsn, sentAt time.Time, body json.RawMes
|
|||
switch event.Type {
|
||||
case transactionType, checkInType:
|
||||
err = encodeEnvelopeItem(enc, event.Type, body)
|
||||
case logEvent.Type:
|
||||
err = encodeEnvelopeLogs(enc, len(event.Logs), body)
|
||||
default:
|
||||
err = encodeEnvelopeItem(enc, eventType, body)
|
||||
}
|
||||
|
|
@ -309,7 +331,7 @@ func NewHTTPTransport() *HTTPTransport {
|
|||
func (t *HTTPTransport) Configure(options ClientOptions) {
|
||||
dsn, err := NewDsn(options.Dsn)
|
||||
if err != nil {
|
||||
Logger.Printf("%v\n", err)
|
||||
DebugLogger.Printf("%v\n", err)
|
||||
return
|
||||
}
|
||||
t.dsn = dsn
|
||||
|
|
@ -393,7 +415,7 @@ func (t *HTTPTransport) SendEventWithContext(ctx context.Context, event *Event)
|
|||
} else {
|
||||
eventType = fmt.Sprintf("%s event", event.Level)
|
||||
}
|
||||
Logger.Printf(
|
||||
DebugLogger.Printf(
|
||||
"Sending %s [%s] to %s project: %s",
|
||||
eventType,
|
||||
event.EventID,
|
||||
|
|
@ -401,7 +423,7 @@ func (t *HTTPTransport) SendEventWithContext(ctx context.Context, event *Event)
|
|||
t.dsn.projectID,
|
||||
)
|
||||
default:
|
||||
Logger.Println("Event dropped due to transport buffer being full.")
|
||||
DebugLogger.Println("Event dropped due to transport buffer being full.")
|
||||
}
|
||||
|
||||
t.buffer <- b
|
||||
|
|
@ -418,8 +440,19 @@ func (t *HTTPTransport) SendEventWithContext(ctx context.Context, event *Event)
|
|||
// have the SDK send events over the network synchronously, configure it to use
|
||||
// the HTTPSyncTransport in the call to Init.
|
||||
func (t *HTTPTransport) Flush(timeout time.Duration) bool {
|
||||
toolate := time.After(timeout)
|
||||
timeoutCh := make(chan struct{})
|
||||
time.AfterFunc(timeout, func() {
|
||||
close(timeoutCh)
|
||||
})
|
||||
return t.flushInternal(timeoutCh)
|
||||
}
|
||||
|
||||
// FlushWithContext works like Flush, but it accepts a context.Context instead of a timeout.
|
||||
func (t *HTTPTransport) FlushWithContext(ctx context.Context) bool {
|
||||
return t.flushInternal(ctx.Done())
|
||||
}
|
||||
|
||||
func (t *HTTPTransport) flushInternal(timeout <-chan struct{}) bool {
|
||||
// Wait until processing the current batch has started or the timeout.
|
||||
//
|
||||
// We must wait until the worker has seen the current batch, because it is
|
||||
|
|
@ -427,6 +460,7 @@ func (t *HTTPTransport) Flush(timeout time.Duration) bool {
|
|||
// possible execution flow in which b.done is never closed, and the only way
|
||||
// out of Flush would be waiting for the timeout, which is undesired.
|
||||
var b batch
|
||||
|
||||
for {
|
||||
select {
|
||||
case b = <-t.buffer:
|
||||
|
|
@ -436,7 +470,7 @@ func (t *HTTPTransport) Flush(timeout time.Duration) bool {
|
|||
default:
|
||||
t.buffer <- b
|
||||
}
|
||||
case <-toolate:
|
||||
case <-timeout:
|
||||
goto fail
|
||||
}
|
||||
}
|
||||
|
|
@ -455,14 +489,14 @@ started:
|
|||
// Wait until the current batch is done or the timeout.
|
||||
select {
|
||||
case <-b.done:
|
||||
Logger.Println("Buffer flushed successfully.")
|
||||
DebugLogger.Println("Buffer flushed successfully.")
|
||||
return true
|
||||
case <-toolate:
|
||||
case <-timeout:
|
||||
goto fail
|
||||
}
|
||||
|
||||
fail:
|
||||
Logger.Println("Buffer flushing reached the timeout.")
|
||||
DebugLogger.Println("Buffer flushing was canceled or timed out.")
|
||||
return false
|
||||
}
|
||||
|
||||
|
|
@ -500,15 +534,15 @@ func (t *HTTPTransport) worker() {
|
|||
|
||||
response, err := t.client.Do(item.request)
|
||||
if err != nil {
|
||||
Logger.Printf("There was an issue with sending an event: %v", err)
|
||||
DebugLogger.Printf("There was an issue with sending an event: %v", err)
|
||||
continue
|
||||
}
|
||||
if response.StatusCode >= 400 && response.StatusCode <= 599 {
|
||||
b, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
Logger.Printf("Error while reading response code: %v", err)
|
||||
DebugLogger.Printf("Error while reading response code: %v", err)
|
||||
}
|
||||
Logger.Printf("Sending %s failed with the following error: %s", eventType, string(b))
|
||||
DebugLogger.Printf("Sending %s failed with the following error: %s", eventType, string(b))
|
||||
}
|
||||
|
||||
t.mu.Lock()
|
||||
|
|
@ -535,7 +569,7 @@ func (t *HTTPTransport) disabled(c ratelimit.Category) bool {
|
|||
defer t.mu.RUnlock()
|
||||
disabled := t.limits.IsRateLimited(c)
|
||||
if disabled {
|
||||
Logger.Printf("Too many requests for %q, backing off till: %v", c, t.limits.Deadline(c))
|
||||
DebugLogger.Printf("Too many requests for %q, backing off till: %v", c, t.limits.Deadline(c))
|
||||
}
|
||||
return disabled
|
||||
}
|
||||
|
|
@ -581,7 +615,7 @@ func NewHTTPSyncTransport() *HTTPSyncTransport {
|
|||
func (t *HTTPSyncTransport) Configure(options ClientOptions) {
|
||||
dsn, err := NewDsn(options.Dsn)
|
||||
if err != nil {
|
||||
Logger.Printf("%v\n", err)
|
||||
DebugLogger.Printf("%v\n", err)
|
||||
return
|
||||
}
|
||||
t.dsn = dsn
|
||||
|
|
@ -627,16 +661,18 @@ func (t *HTTPSyncTransport) SendEventWithContext(ctx context.Context, event *Eve
|
|||
return
|
||||
}
|
||||
|
||||
var eventType string
|
||||
switch {
|
||||
case event.Type == transactionType:
|
||||
eventType = "transaction"
|
||||
var eventIdentifier string
|
||||
switch event.Type {
|
||||
case transactionType:
|
||||
eventIdentifier = "transaction"
|
||||
case logEvent.Type:
|
||||
eventIdentifier = fmt.Sprintf("%v log events", len(event.Logs))
|
||||
default:
|
||||
eventType = fmt.Sprintf("%s event", event.Level)
|
||||
eventIdentifier = fmt.Sprintf("%s event", event.Level)
|
||||
}
|
||||
Logger.Printf(
|
||||
DebugLogger.Printf(
|
||||
"Sending %s [%s] to %s project: %s",
|
||||
eventType,
|
||||
eventIdentifier,
|
||||
event.EventID,
|
||||
t.dsn.host,
|
||||
t.dsn.projectID,
|
||||
|
|
@ -644,15 +680,15 @@ func (t *HTTPSyncTransport) SendEventWithContext(ctx context.Context, event *Eve
|
|||
|
||||
response, err := t.client.Do(request)
|
||||
if err != nil {
|
||||
Logger.Printf("There was an issue with sending an event: %v", err)
|
||||
DebugLogger.Printf("There was an issue with sending an event: %v", err)
|
||||
return
|
||||
}
|
||||
if response.StatusCode >= 400 && response.StatusCode <= 599 {
|
||||
b, err := io.ReadAll(response.Body)
|
||||
if err != nil {
|
||||
Logger.Printf("Error while reading response code: %v", err)
|
||||
DebugLogger.Printf("Error while reading response code: %v", err)
|
||||
}
|
||||
Logger.Printf("Sending %s failed with the following error: %s", eventType, string(b))
|
||||
DebugLogger.Printf("Sending %s failed with the following error: %s", eventIdentifier, string(b))
|
||||
}
|
||||
|
||||
t.mu.Lock()
|
||||
|
|
@ -674,12 +710,17 @@ func (t *HTTPSyncTransport) Flush(_ time.Duration) bool {
|
|||
return true
|
||||
}
|
||||
|
||||
// FlushWithContext is a no-op for HTTPSyncTransport. It always returns true immediately.
|
||||
func (t *HTTPSyncTransport) FlushWithContext(_ context.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *HTTPSyncTransport) disabled(c ratelimit.Category) bool {
|
||||
t.mu.Lock()
|
||||
defer t.mu.Unlock()
|
||||
disabled := t.limits.IsRateLimited(c)
|
||||
if disabled {
|
||||
Logger.Printf("Too many requests for %q, backing off till: %v", c, t.limits.Deadline(c))
|
||||
DebugLogger.Printf("Too many requests for %q, backing off till: %v", c, t.limits.Deadline(c))
|
||||
}
|
||||
return disabled
|
||||
}
|
||||
|
|
@ -695,15 +736,19 @@ type noopTransport struct{}
|
|||
var _ Transport = noopTransport{}
|
||||
|
||||
func (noopTransport) Configure(ClientOptions) {
|
||||
Logger.Println("Sentry client initialized with an empty DSN. Using noopTransport. No events will be delivered.")
|
||||
DebugLogger.Println("Sentry client initialized with an empty DSN. Using noopTransport. No events will be delivered.")
|
||||
}
|
||||
|
||||
func (noopTransport) SendEvent(*Event) {
|
||||
Logger.Println("Event dropped due to noopTransport usage.")
|
||||
DebugLogger.Println("Event dropped due to noopTransport usage.")
|
||||
}
|
||||
|
||||
func (noopTransport) Flush(time.Duration) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (noopTransport) FlushWithContext(context.Context) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (noopTransport) Close() {}
|
||||
|
|
|
|||
14
vendor/github.com/getsentry/sentry-go/util.go
generated
vendored
14
vendor/github.com/getsentry/sentry-go/util.go
generated
vendored
|
|
@ -36,7 +36,7 @@ func monotonicTimeSince(start time.Time) (end time.Time) {
|
|||
return start.Add(time.Since(start))
|
||||
}
|
||||
|
||||
// nolint: deadcode, unused
|
||||
// nolint: unused
|
||||
func prettyPrint(data interface{}) {
|
||||
dbg, _ := json.MarshalIndent(data, "", " ")
|
||||
fmt.Println(string(dbg))
|
||||
|
|
@ -62,7 +62,7 @@ func defaultRelease() (release string) {
|
|||
}
|
||||
for _, e := range envs {
|
||||
if release = os.Getenv(e); release != "" {
|
||||
Logger.Printf("Using release from environment variable %s: %s", e, release)
|
||||
DebugLogger.Printf("Using release from environment variable %s: %s", e, release)
|
||||
return release
|
||||
}
|
||||
}
|
||||
|
|
@ -89,23 +89,23 @@ func defaultRelease() (release string) {
|
|||
if err, ok := err.(*exec.ExitError); ok && len(err.Stderr) > 0 {
|
||||
fmt.Fprintf(&s, ": %s", err.Stderr)
|
||||
}
|
||||
Logger.Print(s.String())
|
||||
DebugLogger.Print(s.String())
|
||||
} else {
|
||||
release = strings.TrimSpace(string(b))
|
||||
Logger.Printf("Using release from Git: %s", release)
|
||||
DebugLogger.Printf("Using release from Git: %s", release)
|
||||
return release
|
||||
}
|
||||
}
|
||||
|
||||
Logger.Print("Some Sentry features will not be available. See https://docs.sentry.io/product/releases/.")
|
||||
Logger.Print("To stop seeing this message, pass a Release to sentry.Init or set the SENTRY_RELEASE environment variable.")
|
||||
DebugLogger.Print("Some Sentry features will not be available. See https://docs.sentry.io/product/releases/.")
|
||||
DebugLogger.Print("To stop seeing this message, pass a Release to sentry.Init or set the SENTRY_RELEASE environment variable.")
|
||||
return ""
|
||||
}
|
||||
|
||||
func revisionFromBuildInfo(info *debug.BuildInfo) string {
|
||||
for _, setting := range info.Settings {
|
||||
if setting.Key == "vcs.revision" && setting.Value != "" {
|
||||
Logger.Printf("Using release from debug info: %s", setting.Value)
|
||||
DebugLogger.Printf("Using release from debug info: %s", setting.Value)
|
||||
return setting.Value
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,3 +22,5 @@ _testmain.go
|
|||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
.direnv
|
||||
.envrc
|
||||
|
|
@ -1,11 +1,14 @@
|
|||
# html2text
|
||||
|
||||
[](https://godoc.org/github.com/jaytaylor/html2text)
|
||||
[](https://travis-ci.org/jaytaylor/html2text)
|
||||
[](https://goreportcard.com/report/github.com/jaytaylor/html2text)
|
||||
[](https://pkg.go.dev/github.com/inbucket/html2text)
|
||||
[](https://github.com/inbucket/html2text/actions/workflows/build-and-test.yml)
|
||||
[](https://goreportcard.com/report/github.com/inbucket/html2text)
|
||||
|
||||
### Converts HTML into text of the markdown-flavored variety
|
||||
|
||||
This is a permanent fork of the original
|
||||
[jaytaylor.com/html2text](https://github.com/jaytaylor/html2text) package.
|
||||
|
||||
## Introduction
|
||||
|
||||
Ensure your emails are readable by all!
|
||||
|
|
@ -21,7 +24,7 @@ It requires go 1.x or newer ;)
|
|||
## Download the package
|
||||
|
||||
```bash
|
||||
go get jaytaylor.com/html2text
|
||||
go get github.com/inbucket/html2text
|
||||
```
|
||||
|
||||
## Example usage
|
||||
|
|
@ -34,7 +37,7 @@ package main
|
|||
import (
|
||||
"fmt"
|
||||
|
||||
"jaytaylor.com/html2text"
|
||||
"github.com/inbucket/html2text"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
|
@ -127,17 +130,3 @@ go test
|
|||
# License
|
||||
|
||||
Permissive MIT license.
|
||||
|
||||
## Contact
|
||||
|
||||
You are more than welcome to open issues and send pull requests if you find a bug or want a new feature.
|
||||
|
||||
If you appreciate this library please feel free to drop me a line and tell me! It's always nice to hear from people who have benefitted from my work.
|
||||
|
||||
Email: jay at (my github username).com
|
||||
|
||||
Twitter: [@jtaylor](https://twitter.com/jtaylor)
|
||||
|
||||
# Alternatives
|
||||
|
||||
https://github.com/k3a/html2text - Lightweight
|
||||
|
|
@ -5,11 +5,11 @@
|
|||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701680307,
|
||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1702272962,
|
||||
"narHash": "sha256-D+zHwkwPc6oYQ4G3A1HuadopqRwUY/JkMwHz1YF7j4Q=",
|
||||
"lastModified": 1749369410,
|
||||
"narHash": "sha256-P1eYm8bewiyWg3QaPCHrOp6iWg/7ESi/aGHT4yilyNo=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "e97b3e4186bcadf0ef1b6be22b8558eab1cdeb5d",
|
||||
"rev": "8207ad0d501dd3590e0e81a7c56b386a5c4342c9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
17
vendor/github.com/inbucket/html2text/flake.nix
generated
vendored
Normal file
17
vendor/github.com/inbucket/html2text/flake.nix
generated
vendored
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixpkgs-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs = { nixpkgs, flake-utils, ... }:
|
||||
flake-utils.lib.eachDefaultSystem
|
||||
(system:
|
||||
let
|
||||
pkgs = nixpkgs.legacyPackages.${system};
|
||||
in
|
||||
{
|
||||
devShell = pkgs.callPackage ./shell.nix { };
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -21,48 +21,6 @@ type Options struct {
|
|||
TextOnly bool // Returns only plain text
|
||||
}
|
||||
|
||||
// PrettyTablesOptions overrides tablewriter behaviors
|
||||
type PrettyTablesOptions struct {
|
||||
AutoFormatHeader bool
|
||||
AutoWrapText bool
|
||||
ReflowDuringAutoWrap bool
|
||||
ColWidth int
|
||||
ColumnSeparator string
|
||||
RowSeparator string
|
||||
CenterSeparator string
|
||||
HeaderAlignment int
|
||||
FooterAlignment int
|
||||
Alignment int
|
||||
ColumnAlignment []int
|
||||
NewLine string
|
||||
HeaderLine bool
|
||||
RowLine bool
|
||||
AutoMergeCells bool
|
||||
Borders tablewriter.Border
|
||||
}
|
||||
|
||||
// NewPrettyTablesOptions creates PrettyTablesOptions with default settings
|
||||
func NewPrettyTablesOptions() *PrettyTablesOptions {
|
||||
return &PrettyTablesOptions{
|
||||
AutoFormatHeader: true,
|
||||
AutoWrapText: true,
|
||||
ReflowDuringAutoWrap: true,
|
||||
ColWidth: tablewriter.MAX_ROW_WIDTH,
|
||||
ColumnSeparator: tablewriter.COLUMN,
|
||||
RowSeparator: tablewriter.ROW,
|
||||
CenterSeparator: tablewriter.CENTER,
|
||||
HeaderAlignment: tablewriter.ALIGN_DEFAULT,
|
||||
FooterAlignment: tablewriter.ALIGN_DEFAULT,
|
||||
Alignment: tablewriter.ALIGN_DEFAULT,
|
||||
ColumnAlignment: []int{},
|
||||
NewLine: tablewriter.NEWLINE,
|
||||
HeaderLine: true,
|
||||
RowLine: false,
|
||||
AutoMergeCells: false,
|
||||
Borders: tablewriter.Border{Left: true, Right: true, Bottom: true, Top: true},
|
||||
}
|
||||
}
|
||||
|
||||
// FromHTMLNode renders text output from a pre-parsed HTML document.
|
||||
func FromHTMLNode(doc *html.Node, o ...Options) (string, error) {
|
||||
var options Options
|
||||
|
|
@ -70,6 +28,11 @@ func FromHTMLNode(doc *html.Node, o ...Options) (string, error) {
|
|||
options = o[0]
|
||||
}
|
||||
|
||||
if options.PrettyTables && options.PrettyTablesOptions == nil {
|
||||
// defaults need to make explicit as they are no longer identical with tablewriter
|
||||
options.PrettyTablesOptions = NewPrettyTablesOptions()
|
||||
}
|
||||
|
||||
ctx := textifyTraverseContext{
|
||||
buf: bytes.Buffer{},
|
||||
options: options,
|
||||
|
|
@ -333,31 +296,18 @@ func (ctx *textifyTraverseContext) handleTableElement(node *html.Node) error {
|
|||
|
||||
buf := &bytes.Buffer{}
|
||||
table := tablewriter.NewWriter(buf)
|
||||
if ctx.options.PrettyTablesOptions != nil {
|
||||
options := ctx.options.PrettyTablesOptions
|
||||
table.SetAutoFormatHeaders(options.AutoFormatHeader)
|
||||
table.SetAutoWrapText(options.AutoWrapText)
|
||||
table.SetReflowDuringAutoWrap(options.ReflowDuringAutoWrap)
|
||||
table.SetColWidth(options.ColWidth)
|
||||
table.SetColumnSeparator(options.ColumnSeparator)
|
||||
table.SetRowSeparator(options.RowSeparator)
|
||||
table.SetCenterSeparator(options.CenterSeparator)
|
||||
table.SetHeaderAlignment(options.HeaderAlignment)
|
||||
table.SetFooterAlignment(options.FooterAlignment)
|
||||
table.SetAlignment(options.Alignment)
|
||||
table.SetColumnAlignment(options.ColumnAlignment)
|
||||
table.SetNewLine(options.NewLine)
|
||||
table.SetHeaderLine(options.HeaderLine)
|
||||
table.SetRowLine(options.RowLine)
|
||||
table.SetAutoMergeCells(options.AutoMergeCells)
|
||||
table.SetBorders(options.Borders)
|
||||
ctx.options.PrettyTablesOptions.configureTable(table)
|
||||
|
||||
table.Header(ctx.tableCtx.header)
|
||||
table.Footer(ctx.tableCtx.footer)
|
||||
if err := table.Bulk(ctx.tableCtx.body); err != nil {
|
||||
return err
|
||||
}
|
||||
table.SetHeader(ctx.tableCtx.header)
|
||||
table.SetFooter(ctx.tableCtx.footer)
|
||||
table.AppendBulk(ctx.tableCtx.body)
|
||||
|
||||
// Render the table using ASCII.
|
||||
table.Render()
|
||||
if err := table.Render(); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ctx.emit(buf.String()); err != nil {
|
||||
return err
|
||||
}
|
||||
212
vendor/github.com/inbucket/html2text/prettytables.go
generated
vendored
Normal file
212
vendor/github.com/inbucket/html2text/prettytables.go
generated
vendored
Normal file
|
|
@ -0,0 +1,212 @@
|
|||
package html2text
|
||||
|
||||
import (
|
||||
"github.com/olekukonko/tablewriter"
|
||||
"github.com/olekukonko/tablewriter/tw"
|
||||
)
|
||||
|
||||
// PrettyTablesOptions overrides tablewriter behaviors
|
||||
type PrettyTablesOptions struct {
|
||||
AutoFormatHeader bool
|
||||
AutoWrapText bool
|
||||
// Deprecated. Tablewriter always assumes this to be `true`
|
||||
ReflowDuringAutoWrap bool
|
||||
ColWidth int
|
||||
ColumnSeparator string
|
||||
RowSeparator string
|
||||
CenterSeparator string
|
||||
HeaderAlignment tw.Align
|
||||
FooterAlignment tw.Align
|
||||
Alignment tw.Align
|
||||
ColumnAlignment tw.Alignment
|
||||
// Deprecated. Tablewriter always assumes this to be `\n`
|
||||
NewLine string
|
||||
HeaderLine bool
|
||||
RowLine bool
|
||||
AutoMergeCells bool
|
||||
Borders Border
|
||||
// Configuration allows to directly manipulate the `Table` with all what [tablewriter] offers.
|
||||
// Setting this ignores all the rest of the settings of this struct.
|
||||
Configuration func(table *tablewriter.Table)
|
||||
}
|
||||
|
||||
// NewPrettyTablesOptions creates PrettyTablesOptions with default settings
|
||||
func NewPrettyTablesOptions() *PrettyTablesOptions {
|
||||
return &PrettyTablesOptions{
|
||||
AutoFormatHeader: true,
|
||||
AutoWrapText: true,
|
||||
ColWidth: 32, // old tablewriter.MAX_ROW_WIDTH + borders now count into width
|
||||
ColumnSeparator: defaultBorderStyle.ColumnSeparator,
|
||||
RowSeparator: defaultBorderStyle.RowSeparator,
|
||||
CenterSeparator: defaultBorderStyle.CenterSeparator,
|
||||
HeaderAlignment: tw.AlignCenter,
|
||||
FooterAlignment: tw.AlignCenter,
|
||||
Alignment: tw.AlignDefault,
|
||||
ColumnAlignment: make(tw.Alignment, 0),
|
||||
HeaderLine: true,
|
||||
RowLine: false,
|
||||
AutoMergeCells: false,
|
||||
Borders: Border{Left: true, Right: true, Bottom: true, Top: true},
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PrettyTablesOptions) configureTable(table *tablewriter.Table) {
|
||||
if p.Configuration != nil {
|
||||
p.Configuration(table)
|
||||
return
|
||||
}
|
||||
|
||||
cfg := tablewriter.NewConfigBuilder()
|
||||
|
||||
cfg.WithHeaderAutoFormat(asState(p.AutoFormatHeader)).WithFooterAutoFormat(asState(p.AutoFormatHeader)).
|
||||
WithRowAutoWrap(p.wrapMode()).WithHeaderAutoWrap(p.wrapMode()).WithFooterAutoWrap(p.wrapMode()).
|
||||
WithRowMaxWidth(p.ColWidth).
|
||||
WithHeaderAlignment(p.HeaderAlignment).
|
||||
WithFooterAlignment(p.FooterAlignment).
|
||||
WithRowAlignment(p.Alignment).
|
||||
WithRowMergeMode(p.mergeMode())
|
||||
|
||||
if len(p.ColumnAlignment) > 0 {
|
||||
cfg.Row().Alignment().WithPerColumn(p.ColumnAlignment)
|
||||
}
|
||||
|
||||
rendition := tw.Rendition{
|
||||
Borders: p.Borders.withStates(),
|
||||
Symbols: p.borderStyle(),
|
||||
Settings: p.renderSettings(),
|
||||
}
|
||||
|
||||
table.Options(
|
||||
tablewriter.WithConfig(cfg.Build()),
|
||||
tablewriter.WithRendition(rendition))
|
||||
}
|
||||
|
||||
func (p *PrettyTablesOptions) wrapMode() int {
|
||||
if p.AutoWrapText {
|
||||
return tw.WrapNormal
|
||||
} else {
|
||||
return tw.WrapNone
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PrettyTablesOptions) mergeMode() int {
|
||||
if p.AutoMergeCells {
|
||||
return tw.MergeVertical
|
||||
} else {
|
||||
return tw.MergeNone
|
||||
}
|
||||
}
|
||||
|
||||
func (p *PrettyTablesOptions) renderSettings() tw.Settings {
|
||||
return tw.Settings{
|
||||
Lines: tw.Lines{
|
||||
ShowHeaderLine: asState(p.HeaderLine),
|
||||
},
|
||||
Separators: tw.Separators{
|
||||
BetweenRows: asState(p.RowLine),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Border controls tablewriter borders. It uses simple bools instead of tablewriters `State`
|
||||
type Border struct {
|
||||
Left, Right, Bottom, Top bool
|
||||
}
|
||||
|
||||
func (b Border) withStates() tw.Border {
|
||||
return tw.Border{
|
||||
Left: asState(b.Left),
|
||||
Right: asState(b.Right),
|
||||
Bottom: asState(b.Bottom),
|
||||
Top: asState(b.Top),
|
||||
}
|
||||
}
|
||||
|
||||
type BorderStyle struct {
|
||||
ColumnSeparator string
|
||||
RowSeparator string
|
||||
CenterSeparator string
|
||||
}
|
||||
|
||||
func (b BorderStyle) Name() string {
|
||||
return "html2text"
|
||||
}
|
||||
|
||||
func (b BorderStyle) Center() string {
|
||||
return b.CenterSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) Row() string {
|
||||
return b.RowSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) Column() string {
|
||||
return b.ColumnSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) TopLeft() string {
|
||||
return b.CenterSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) TopMid() string {
|
||||
return b.CenterSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) TopRight() string {
|
||||
return b.CenterSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) MidLeft() string {
|
||||
return b.CenterSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) MidRight() string {
|
||||
return b.CenterSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) BottomLeft() string {
|
||||
return b.CenterSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) BottomMid() string {
|
||||
return b.CenterSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) BottomRight() string {
|
||||
return b.CenterSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) HeaderLeft() string {
|
||||
return b.CenterSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) HeaderMid() string {
|
||||
return b.CenterSeparator
|
||||
}
|
||||
|
||||
func (b BorderStyle) HeaderRight() string {
|
||||
return b.CenterSeparator
|
||||
}
|
||||
|
||||
var defaultBorderStyle = BorderStyle{
|
||||
ColumnSeparator: "|",
|
||||
RowSeparator: "-",
|
||||
CenterSeparator: "+",
|
||||
}
|
||||
|
||||
func (p *PrettyTablesOptions) borderStyle() BorderStyle {
|
||||
return BorderStyle{
|
||||
ColumnSeparator: p.ColumnSeparator,
|
||||
RowSeparator: p.RowSeparator,
|
||||
CenterSeparator: p.CenterSeparator,
|
||||
}
|
||||
}
|
||||
|
||||
func asState(b bool) tw.State {
|
||||
// TableWriter does not provide this by default :(
|
||||
if b {
|
||||
return tw.On
|
||||
} else {
|
||||
return tw.Off
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,8 @@
|
|||
pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
delve
|
||||
go_1_20
|
||||
golint
|
||||
go
|
||||
golangci-lint
|
||||
gopls
|
||||
];
|
||||
|
||||
13
vendor/github.com/jaytaylor/html2text/.travis.yml
generated
vendored
13
vendor/github.com/jaytaylor/html2text/.travis.yml
generated
vendored
|
|
@ -1,13 +0,0 @@
|
|||
language: go
|
||||
go:
|
||||
# n.b. For golang release history, see https://golang.org/doc/devel/release.html
|
||||
- tip
|
||||
- "1.13.8"
|
||||
- "1.12.17"
|
||||
- "1.11.13"
|
||||
- "1.10.8"
|
||||
- "1.9.7"
|
||||
notifications:
|
||||
email:
|
||||
on_success: change
|
||||
on_failure: always
|
||||
|
|
@ -1,5 +1,4 @@
|
|||
run:
|
||||
timeout: 5m
|
||||
version: "2"
|
||||
linters:
|
||||
enable:
|
||||
- asasalint
|
||||
|
|
@ -8,32 +7,19 @@ linters:
|
|||
- bodyclose
|
||||
- containedctx
|
||||
- contextcheck
|
||||
- copyloopvar
|
||||
- decorder
|
||||
# - dupl
|
||||
# - dupword
|
||||
- durationcheck
|
||||
- errchkjson
|
||||
- errname
|
||||
# - errorlint
|
||||
- execinquery
|
||||
# - exhaustive
|
||||
- exportloopref
|
||||
# - forcetypeassert
|
||||
- ginkgolinter
|
||||
- gocheckcompilerdirectives
|
||||
# - gochecknoinits
|
||||
- gochecksumtype
|
||||
- gocritic
|
||||
# - godot
|
||||
# - goerr113
|
||||
- gofmt
|
||||
# - gofumpt
|
||||
- goheader
|
||||
- goimports
|
||||
- gomoddirectives
|
||||
- gomodguard
|
||||
- goprintffuncname
|
||||
# - gosec
|
||||
- gosmopolitan
|
||||
- grouper
|
||||
- importas
|
||||
|
|
@ -45,8 +31,6 @@ linters:
|
|||
- misspell
|
||||
- musttag
|
||||
- nilerr
|
||||
# - nilnil
|
||||
# - nlreturn
|
||||
- noctx
|
||||
- nolintlint
|
||||
- nosprintfhostport
|
||||
|
|
@ -56,20 +40,44 @@ linters:
|
|||
- promlinter
|
||||
- protogetter
|
||||
- reassign
|
||||
# - revive
|
||||
- revive
|
||||
- rowserrcheck
|
||||
- sloglint
|
||||
- stylecheck
|
||||
- staticcheck
|
||||
- tagalign
|
||||
- tagliatelle
|
||||
- tenv
|
||||
- testableexamples
|
||||
- testifylint
|
||||
- thelper
|
||||
- tparallel
|
||||
# - unconvert
|
||||
- unparam
|
||||
- usestdlibvars
|
||||
- wastedassign
|
||||
- whitespace
|
||||
- zerologlint
|
||||
exclusions:
|
||||
generated: lax
|
||||
presets:
|
||||
- comments
|
||||
- common-false-positives
|
||||
- legacy
|
||||
- std-error-handling
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
rules:
|
||||
# Exclude specific linters from test files.
|
||||
- path: _test\.go
|
||||
linters:
|
||||
- gosmopolitan
|
||||
formatters:
|
||||
enable:
|
||||
- gofmt
|
||||
- goimports
|
||||
exclusions:
|
||||
generated: lax
|
||||
paths:
|
||||
- third_party$
|
||||
- builtin$
|
||||
- examples$
|
||||
|
|
@ -27,8 +27,6 @@ simplify:
|
|||
|
||||
lint:
|
||||
@test -z "$(shell gofmt -l . | tee /dev/stderr)" || echo "[WARN] Fix formatting issues with 'make fmt'"
|
||||
@# TODO: lint disabled until `mediatype.MediaTypeParseOptions` renamed to remove repetition.
|
||||
@#@golint -set_exit_status $(PKGS)
|
||||
@go vet $(PKGS)
|
||||
|
||||
reflex:
|
||||
|
|
@ -180,7 +180,7 @@ func (b *boundaryReader) Next() (bool, error) {
|
|||
_, _ = io.Copy(io.Discard, b)
|
||||
}
|
||||
for {
|
||||
var line []byte = nil
|
||||
var line []byte
|
||||
var err error
|
||||
for {
|
||||
// Read whole line, handle extra long lines in cycle
|
||||
|
|
@ -13,7 +13,7 @@ import (
|
|||
"reflect"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/enmime/internal/stringutil"
|
||||
"github.com/jhillyerd/enmime/v2/internal/stringutil"
|
||||
)
|
||||
|
||||
// MailBuilder facilitates the easy construction of a MIME message. Each manipulation method
|
||||
|
|
@ -3,7 +3,7 @@ package enmime
|
|||
import (
|
||||
"strings"
|
||||
|
||||
inttp "github.com/jhillyerd/enmime/internal/textproto"
|
||||
inttp "github.com/jhillyerd/enmime/v2/internal/textproto"
|
||||
)
|
||||
|
||||
// detectMultipartMessage returns true if the message has a recognized multipart Content-Type header
|
||||
|
|
@ -8,10 +8,11 @@ import (
|
|||
"mime/quotedprintable"
|
||||
"net/textproto"
|
||||
"sort"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jhillyerd/enmime/internal/coding"
|
||||
"github.com/jhillyerd/enmime/internal/stringutil"
|
||||
"github.com/jhillyerd/enmime/v2/internal/coding"
|
||||
"github.com/jhillyerd/enmime/v2/internal/stringutil"
|
||||
)
|
||||
|
||||
// b64Percent determines the percent of non-ASCII characters enmime will tolerate before switching
|
||||
|
|
@ -22,6 +23,7 @@ type transferEncoding byte
|
|||
|
||||
const (
|
||||
te7Bit transferEncoding = iota
|
||||
te8Bit
|
||||
teQuoted
|
||||
teBase64
|
||||
teRaw
|
||||
|
|
@ -106,21 +108,33 @@ func (p *Part) setupMIMEHeaders() transferEncoding {
|
|||
|
||||
cte := te7Bit
|
||||
if len(p.Content) > 0 {
|
||||
cte = teBase64
|
||||
if p.TextContent() && p.ContentReader == nil {
|
||||
cte = selectTransferEncoding(p.Content, false)
|
||||
if p.Charset == "" {
|
||||
p.Charset = utf8
|
||||
if strings.Index(strings.ToLower(p.ContentType), "message/") == 0 {
|
||||
// RFC 1341: `message` types must have no encoding other than "7bit", "8bit", or
|
||||
// "binary". The message header fields are always US-ASCII in any case, and data within
|
||||
// the body can still be encoded, in which case the Content-Transfer-Encoding header
|
||||
// field in the encapsulated message will reflect this.
|
||||
cte = te8Bit
|
||||
} else {
|
||||
cte = teBase64
|
||||
if p.TextContent() && p.ContentReader == nil {
|
||||
cte = p.selectTransferEncoding(p.Content, false)
|
||||
if p.Charset == "" {
|
||||
p.Charset = utf8
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// RFC 2045: 7bit is assumed if CTE header not present.
|
||||
switch cte {
|
||||
case te8Bit:
|
||||
p.Header.Set(hnContentEncoding, cte8Bit)
|
||||
case teBase64:
|
||||
p.Header.Set(hnContentEncoding, cteBase64)
|
||||
case teQuoted:
|
||||
p.Header.Set(hnContentEncoding, cteQuotedPrintable)
|
||||
}
|
||||
}
|
||||
|
||||
// Setup headers.
|
||||
if p.FirstChild != nil && p.Boundary == "" {
|
||||
// Multipart, generate random boundary marker.
|
||||
|
|
@ -130,12 +144,13 @@ func (p *Part) setupMIMEHeaders() transferEncoding {
|
|||
p.Header.Set(hnContentID, coding.ToIDHeader(p.ContentID))
|
||||
}
|
||||
fileName := p.FileName
|
||||
switch selectTransferEncoding([]byte(p.FileName), true) {
|
||||
switch p.selectTransferEncoding([]byte(p.FileName), true) {
|
||||
case teBase64:
|
||||
fileName = mime.BEncoding.Encode(utf8, p.FileName)
|
||||
case teQuoted:
|
||||
fileName = mime.QEncoding.Encode(utf8, p.FileName)
|
||||
}
|
||||
|
||||
if p.ContentType != "" {
|
||||
// Build content type header.
|
||||
param := make(map[string]string)
|
||||
|
|
@ -150,6 +165,7 @@ func (p *Part) setupMIMEHeaders() transferEncoding {
|
|||
}
|
||||
p.Header.Set(hnContentType, p.ContentType)
|
||||
}
|
||||
|
||||
if p.Disposition != "" {
|
||||
// Build disposition header.
|
||||
param := make(map[string]string)
|
||||
|
|
@ -162,6 +178,7 @@ func (p *Part) setupMIMEHeaders() transferEncoding {
|
|||
}
|
||||
p.Header.Set(hnContentDisposition, p.Disposition)
|
||||
}
|
||||
|
||||
return cte
|
||||
}
|
||||
|
||||
|
|
@ -178,7 +195,7 @@ func (p *Part) encodeHeader(b *bufio.Writer) error {
|
|||
for _, v := range p.Header[k] {
|
||||
encv := v
|
||||
if !rawContent {
|
||||
switch selectTransferEncoding([]byte(v), true) {
|
||||
switch p.selectTransferEncoding([]byte(v), true) {
|
||||
case teBase64:
|
||||
encv = mime.BEncoding.Encode(utf8, v)
|
||||
case teQuoted:
|
||||
|
|
@ -286,10 +303,15 @@ func (p *Part) encodeContentFromReader(b *bufio.Writer) error {
|
|||
}
|
||||
|
||||
// selectTransferEncoding scans content for non-ASCII characters and selects 'b' or 'q' encoding.
|
||||
func selectTransferEncoding(content []byte, quoteLineBreaks bool) transferEncoding {
|
||||
func (p *Part) selectTransferEncoding(content []byte, quoteLineBreaks bool) transferEncoding {
|
||||
if len(content) == 0 {
|
||||
return te7Bit
|
||||
}
|
||||
|
||||
if p.encoder != nil && p.encoder.forceQuotedPrintableCteOption {
|
||||
return teQuoted
|
||||
}
|
||||
|
||||
// Binary chars remaining before we choose b64 encoding.
|
||||
threshold := b64Percent * len(content) / 100
|
||||
bincount := 0
|
||||
33
vendor/github.com/jhillyerd/enmime/v2/encoder_options.go
generated
vendored
Normal file
33
vendor/github.com/jhillyerd/enmime/v2/encoder_options.go
generated
vendored
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package enmime
|
||||
|
||||
type EncoderOption interface {
|
||||
apply(p *Encoder)
|
||||
}
|
||||
|
||||
// Encoder implements MIME part encoding options
|
||||
type Encoder struct {
|
||||
forceQuotedPrintableCteOption bool
|
||||
}
|
||||
|
||||
// ForceQuotedPrintableCte forces "quoted-printable" transfer encoding when selecting Content Transfer Encoding, preventing the use of base64.
|
||||
func ForceQuotedPrintableCte(b bool) EncoderOption {
|
||||
return forceQuotedPrintableCteOption(b)
|
||||
}
|
||||
|
||||
type forceQuotedPrintableCteOption bool
|
||||
|
||||
func (o forceQuotedPrintableCteOption) apply(p *Encoder) {
|
||||
p.forceQuotedPrintableCteOption = bool(o)
|
||||
}
|
||||
|
||||
func NewEncoder(ops ...EncoderOption) *Encoder {
|
||||
e := Encoder{
|
||||
forceQuotedPrintableCteOption: false,
|
||||
}
|
||||
|
||||
for _, o := range ops {
|
||||
o.apply(&e)
|
||||
}
|
||||
|
||||
return &e
|
||||
}
|
||||
|
|
@ -51,7 +51,7 @@
|
|||
// multi-gigabyte attachments.
|
||||
//
|
||||
// enmime is open source software released under the MIT License. The latest version can be found
|
||||
// at https://github.com/jhillyerd/enmime
|
||||
// at https://github.com/jhillyerd/enmime/v2
|
||||
//
|
||||
// [1]: https://github.com/jaytaylor/html2text
|
||||
package enmime // import "github.com/jhillyerd/enmime"
|
||||
// [1]: https://github.com/inbucket/html2text
|
||||
package enmime // import "github.com/jhillyerd/enmime/v2"
|
||||
|
|
@ -9,9 +9,9 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/jaytaylor/html2text"
|
||||
"github.com/jhillyerd/enmime/internal/coding"
|
||||
inttp "github.com/jhillyerd/enmime/internal/textproto"
|
||||
"github.com/inbucket/html2text"
|
||||
"github.com/jhillyerd/enmime/v2/internal/coding"
|
||||
inttp "github.com/jhillyerd/enmime/v2/internal/textproto"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
|
@ -198,16 +198,14 @@ func (p Parser) EnvelopeFromPart(root *Part) (*Envelope, error) {
|
|||
// Down-convert HTML to text if necessary
|
||||
if e.Text == "" && e.HTML != "" {
|
||||
// We always warn when this happens
|
||||
e.Root.addWarning(
|
||||
ErrorPlainTextFromHTML,
|
||||
"Message did not contain a text/plain part")
|
||||
e.Root.addWarning(ErrorPlainTextFromHTML, "Message did not contain a text/plain part")
|
||||
|
||||
if !p.disableTextConversion {
|
||||
var err error
|
||||
if e.Text, err = html2text.FromString(e.HTML); err != nil {
|
||||
e.Text = "" // Down-conversion shouldn't fail
|
||||
p := e.Root.BreadthMatchFirst(matchHTMLBodyPart)
|
||||
p.addError(ErrorPlainTextFromHTML, "Failed to downconvert HTML: %v", err)
|
||||
p.addErrorf(ErrorPlainTextFromHTML, "Failed to downconvert HTML: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -27,12 +27,6 @@ const (
|
|||
ErrorMalformedChildPart = "Malformed child part"
|
||||
)
|
||||
|
||||
// MaxPartErrors limits number of part parsing errors, errors after the limit are ignored.
|
||||
// 0 means unlimited.
|
||||
//
|
||||
// Deprecated: This limit may be set via the `MaxStoredPartErrors` Parser option.
|
||||
var MaxPartErrors = 0
|
||||
|
||||
// Error describes an error encountered while parsing.
|
||||
type Error struct {
|
||||
Name string // The name or type of error encountered, from Error consts.
|
||||
|
|
@ -49,13 +43,13 @@ func (e *Error) Error() string {
|
|||
return fmt.Sprintf("[%s] %s: %s", sev, e.Name, e.Detail)
|
||||
}
|
||||
|
||||
// String formats the enmime.Error as a string. DEPRECATED; use Error() instead.
|
||||
func (e *Error) String() string {
|
||||
return e.Error()
|
||||
// addError builds a severe Error and appends to the Part error slice.
|
||||
func (p *Part) addError(name string, detail string) {
|
||||
p.addProblem(&Error{name, detail, true})
|
||||
}
|
||||
|
||||
// addWarning builds a severe Error and appends to the Part error slice.
|
||||
func (p *Part) addError(name string, detailFmt string, args ...interface{}) {
|
||||
// addErrorf builds a severe Error and appends to the Part error slice.
|
||||
func (p *Part) addErrorf(name string, detailFmt string, args ...interface{}) {
|
||||
p.addProblem(&Error{
|
||||
name,
|
||||
fmt.Sprintf(detailFmt, args...),
|
||||
|
|
@ -64,7 +58,12 @@ func (p *Part) addError(name string, detailFmt string, args ...interface{}) {
|
|||
}
|
||||
|
||||
// addWarning builds a non-severe Error and appends to the Part error slice.
|
||||
func (p *Part) addWarning(name string, detailFmt string, args ...interface{}) {
|
||||
func (p *Part) addWarning(name string, detail string) {
|
||||
p.addProblem(&Error{name, detail, false})
|
||||
}
|
||||
|
||||
// addWarningf builds a non-severe Error and appends to the Part error slice.
|
||||
func (p *Part) addWarningf(name string, detailFmt string, args ...interface{}) {
|
||||
p.addProblem(&Error{
|
||||
name,
|
||||
fmt.Sprintf(detailFmt, args...),
|
||||
|
|
@ -74,10 +73,10 @@ func (p *Part) addWarning(name string, detailFmt string, args ...interface{}) {
|
|||
|
||||
// addProblem adds general *Error to the Part error slice.
|
||||
func (p *Part) addProblem(err *Error) {
|
||||
maxErrors := MaxPartErrors
|
||||
if p.parser != nil && p.parser.maxStoredPartErrors != nil {
|
||||
maxErrors := 0
|
||||
if p.parser != nil {
|
||||
// Override global var.
|
||||
maxErrors = *p.parser.maxStoredPartErrors
|
||||
maxErrors = p.parser.maxStoredPartErrors
|
||||
}
|
||||
|
||||
if (maxErrors == 0) || (len(p.Errors) < maxErrors) {
|
||||
|
|
@ -97,9 +96,9 @@ type partErrorCollector struct {
|
|||
}
|
||||
|
||||
func (p *partErrorCollector) AddError(name string, detailFmt string, args ...any) {
|
||||
p.part.addError(name, detailFmt, args...)
|
||||
p.part.addErrorf(name, detailFmt, args...)
|
||||
}
|
||||
|
||||
func (p *partErrorCollector) AddWarning(name string, detailFmt string, args ...any) {
|
||||
p.part.addWarning(name, detailFmt, args...)
|
||||
p.part.addWarningf(name, detailFmt, args...)
|
||||
}
|
||||
61
vendor/github.com/jhillyerd/enmime/v2/flake.lock
generated
vendored
Normal file
61
vendor/github.com/jhillyerd/enmime/v2/flake.lock
generated
vendored
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1736657626,
|
||||
"narHash": "sha256-FWlPMUzp0lkQBdhKlPqtQdqmp+/C+1MBiEytaYfrCTY=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2f9e2f85cb14a46410a1399aa9ea7ecf433e422e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
|
|
@ -9,10 +9,9 @@ import (
|
|||
"net/textproto"
|
||||
"strings"
|
||||
|
||||
"github.com/jhillyerd/enmime/internal/coding"
|
||||
"github.com/jhillyerd/enmime/internal/stringutil"
|
||||
inttp "github.com/jhillyerd/enmime/internal/textproto"
|
||||
"github.com/jhillyerd/enmime/mediatype"
|
||||
"github.com/jhillyerd/enmime/v2/internal/coding"
|
||||
"github.com/jhillyerd/enmime/v2/internal/stringutil"
|
||||
inttp "github.com/jhillyerd/enmime/v2/internal/textproto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
|
@ -101,26 +100,6 @@ func ParseAddressList(list string) ([]*mail.Address, error) {
|
|||
return ret, nil
|
||||
}
|
||||
|
||||
// Terminology from RFC 2047:
|
||||
// encoded-word: the entire =?charset?encoding?encoded-text?= string
|
||||
// charset: the character set portion of the encoded word
|
||||
// encoding: the character encoding type used for the encoded-text
|
||||
// encoded-text: the text we are decoding
|
||||
|
||||
// ParseMediaType is a more tolerant implementation of Go's mime.ParseMediaType function.
|
||||
//
|
||||
// Tolerances accounted for:
|
||||
// - Missing ';' between content-type and media parameters
|
||||
// - Repeating media parameters
|
||||
// - Unquoted values in media parameters containing 'tspecials' characters
|
||||
//
|
||||
// Deprecated: Use mediaType.Parse instead
|
||||
func ParseMediaType(ctype string) (mtype string, params map[string]string, invalidParams []string,
|
||||
err error) {
|
||||
// Export of internal function.
|
||||
return mediatype.Parse(ctype)
|
||||
}
|
||||
|
||||
// ReadHeader reads a block of SMTP or MIME headers and returns a
|
||||
// textproto.MIMEHeader. Header parse warnings & errors will be added to
|
||||
// ErrorCollector, io errors will be returned directly.
|
||||
|
|
@ -5,8 +5,8 @@ import (
|
|||
"bytes"
|
||||
"io"
|
||||
|
||||
"github.com/jhillyerd/enmime/internal/coding"
|
||||
"github.com/jhillyerd/enmime/internal/textproto"
|
||||
"github.com/jhillyerd/enmime/v2/internal/coding"
|
||||
"github.com/jhillyerd/enmime/v2/internal/textproto"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
|
@ -34,6 +34,7 @@ func EnsureCommaDelimitedAddresses(s string) string {
|
|||
inQuotes := false
|
||||
inDomain := false
|
||||
escapeSequence := false
|
||||
inAngles := false
|
||||
sb := strings.Builder{}
|
||||
for i, r := range s {
|
||||
if escapeSequence {
|
||||
|
|
@ -46,6 +47,19 @@ func EnsureCommaDelimitedAddresses(s string) string {
|
|||
sb.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
|
||||
if r == '<' {
|
||||
inAngles = true
|
||||
sb.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
|
||||
if r == '>' {
|
||||
inAngles = false
|
||||
sb.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
|
||||
if inQuotes {
|
||||
if r == '\\' {
|
||||
escapeSequence = true
|
||||
|
|
@ -74,13 +88,17 @@ func EnsureCommaDelimitedAddresses(s string) string {
|
|||
sb.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
if r == ' ' {
|
||||
if r == ' ' && !inAngles {
|
||||
inDomain = false
|
||||
sb.WriteRune(',')
|
||||
sb.WriteRune(r)
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if inAngles && r == ' ' {
|
||||
continue
|
||||
}
|
||||
}
|
||||
sb.WriteRune(r)
|
||||
}
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
package stringutil
|
||||
|
||||
// Wrap builds a byte slice from strs, wrapping on word boundaries before max chars
|
||||
func Wrap(max int, strs ...string) []byte {
|
||||
// Wrap builds a byte slice from strs, wrapping on word boundaries before maxLen chars
|
||||
func Wrap(maxLen int, strs ...string) []byte {
|
||||
input := make([]byte, 0)
|
||||
output := make([]byte, 0)
|
||||
for _, s := range strs {
|
||||
input = append(input, []byte(s)...)
|
||||
}
|
||||
if len(input) < max {
|
||||
if len(input) < maxLen {
|
||||
// Doesn't need to be wrapped
|
||||
return input
|
||||
}
|
||||
|
|
@ -20,7 +20,7 @@ func Wrap(max int, strs ...string) []byte {
|
|||
case ' ', '\t':
|
||||
ls = i
|
||||
}
|
||||
if ll >= max {
|
||||
if ll >= maxLen {
|
||||
if ls >= 0 {
|
||||
output = append(output, input[lw+1:ls]...)
|
||||
output = append(output, '\r', '\n', ' ')
|
||||
|
|
@ -6,8 +6,8 @@ import (
|
|||
"strings"
|
||||
_utf8 "unicode/utf8"
|
||||
|
||||
"github.com/jhillyerd/enmime/internal/coding"
|
||||
"github.com/jhillyerd/enmime/internal/stringutil"
|
||||
"github.com/jhillyerd/enmime/v2/internal/coding"
|
||||
"github.com/jhillyerd/enmime/v2/internal/stringutil"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
|
|
@ -30,8 +30,8 @@ const (
|
|||
utf8 = "utf-8"
|
||||
)
|
||||
|
||||
// MediaTypeParseOptions controls the parsing of content-type and media-type strings.
|
||||
type MediaTypeParseOptions struct {
|
||||
// ParseOptions controls the parsing of content-type and media-type strings.
|
||||
type ParseOptions struct {
|
||||
StripMediaTypeInvalidCharacters bool
|
||||
}
|
||||
|
||||
|
|
@ -43,11 +43,11 @@ type MediaTypeParseOptions struct {
|
|||
// - Unquoted values in media parameters containing 'tspecials' characters
|
||||
// - Newline characters
|
||||
func Parse(ctype string) (mtype string, params map[string]string, invalidParams []string, err error) {
|
||||
return ParseWithOptions(ctype, MediaTypeParseOptions{})
|
||||
return ParseWithOptions(ctype, ParseOptions{})
|
||||
}
|
||||
|
||||
// ParseWithOptions parses media-type with additional options controlling the parsing behavior.
|
||||
func ParseWithOptions(ctype string, options MediaTypeParseOptions) (mtype string, params map[string]string, invalidParams []string, err error) {
|
||||
func ParseWithOptions(ctype string, options ParseOptions) (mtype string, params map[string]string, invalidParams []string, err error) {
|
||||
mtype, params, err = mime.ParseMediaType(
|
||||
fixNewlines(fixUnescapedQuotes(fixUnquotedSpecials(fixMangledMediaType(removeTrailingHTMLTags(ctype), ';', options)))))
|
||||
if err != nil {
|
||||
|
|
@ -73,7 +73,7 @@ func ParseWithOptions(ctype string, options MediaTypeParseOptions) (mtype string
|
|||
|
||||
// fixMangledMediaType is used to insert ; separators into media type strings that lack them, and
|
||||
// remove repeated parameters.
|
||||
func fixMangledMediaType(mtype string, sep rune, options MediaTypeParseOptions) string {
|
||||
func fixMangledMediaType(mtype string, sep rune, options ParseOptions) string {
|
||||
strsep := string([]rune{sep})
|
||||
if mtype == "" {
|
||||
return ""
|
||||
|
|
@ -48,8 +48,7 @@ func MaxStoredPartErrors(n int) Option {
|
|||
type maxStoredPartErrorsOption int
|
||||
|
||||
func (o maxStoredPartErrorsOption) apply(p *Parser) {
|
||||
max := int(o)
|
||||
p.maxStoredPartErrors = &max
|
||||
p.maxStoredPartErrors = int(o)
|
||||
}
|
||||
|
||||
// RawContent if set to true will not try to decode the CTE and return the raw part content.
|
||||
|
|
@ -112,3 +111,16 @@ func (o disableCharacterDetectionOption) apply(p *Parser) {
|
|||
func DisableCharacterDetection(disableCharacterDetection bool) Option {
|
||||
return disableCharacterDetectionOption(disableCharacterDetection)
|
||||
}
|
||||
|
||||
type minCharsetDetectRunesOption int
|
||||
|
||||
func (o minCharsetDetectRunesOption) apply(p *Parser) {
|
||||
p.minCharsetDetectRunes = int(o)
|
||||
}
|
||||
|
||||
// MinCharsetDetectRunes sets the minimum length of a MIME part before enmime will attempt to
|
||||
// detect its character set. The shorter the text, the more likely an incorrect character set
|
||||
// will be chosen. The default is 100.
|
||||
func MinCharsetDetectRunes(minCharsetDetectRunes int) Option {
|
||||
return minCharsetDetectRunesOption(minCharsetDetectRunes)
|
||||
}
|
||||
|
|
@ -16,10 +16,9 @@ func AllowCorruptTextPartErrorPolicy(p *Part, err error) bool {
|
|||
// CustomParseMediaType parses media type. See ParseMediaType for more details
|
||||
type CustomParseMediaType func(ctype string) (mtype string, params map[string]string, invalidParams []string, err error)
|
||||
|
||||
// Parser parses MIME.
|
||||
// Default parser is a valid one.
|
||||
// Parser parses MIME. Create with NewParser to inherit recommended defaults.
|
||||
type Parser struct {
|
||||
maxStoredPartErrors *int // TODO: Pointer until global var removed.
|
||||
maxStoredPartErrors int
|
||||
multipartWOBoundaryAsSinglePart bool
|
||||
readPartErrorPolicy ReadPartErrorPolicy
|
||||
skipMalformedParts bool
|
||||
|
|
@ -28,14 +27,18 @@ type Parser struct {
|
|||
stripMediaTypeInvalidCharacters bool
|
||||
disableTextConversion bool
|
||||
disableCharacterDetection bool
|
||||
minCharsetDetectRunes int
|
||||
}
|
||||
|
||||
// defaultParser is a Parser with default configuration.
|
||||
var defaultParser = Parser{}
|
||||
var defaultParser = *NewParser()
|
||||
|
||||
// NewParser creates new parser with given options.
|
||||
func NewParser(ops ...Option) *Parser {
|
||||
p := Parser{}
|
||||
// Construct parser with default options.
|
||||
p := Parser{
|
||||
minCharsetDetectRunes: 100,
|
||||
}
|
||||
|
||||
for _, o := range ops {
|
||||
o.apply(&p)
|
||||
|
|
@ -13,15 +13,14 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/gogs/chardet"
|
||||
"github.com/jhillyerd/enmime/internal/coding"
|
||||
inttp "github.com/jhillyerd/enmime/internal/textproto"
|
||||
"github.com/jhillyerd/enmime/mediatype"
|
||||
"github.com/jhillyerd/enmime/v2/internal/coding"
|
||||
inttp "github.com/jhillyerd/enmime/v2/internal/textproto"
|
||||
"github.com/jhillyerd/enmime/v2/mediatype"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
const (
|
||||
minCharsetConfidence = 85
|
||||
minCharsetRuneLength = 100
|
||||
)
|
||||
|
||||
// Part represents a node in the MIME multipart tree. The Content-Type, Disposition and File Name
|
||||
|
|
@ -51,6 +50,8 @@ type Part struct {
|
|||
parser *Parser // Provides access to parsing options.
|
||||
|
||||
randSource rand.Source // optional rand for uuid boundary generation
|
||||
|
||||
encoder *Encoder // provides encoding options
|
||||
}
|
||||
|
||||
// NewPart creates a new Part object.
|
||||
|
|
@ -130,7 +131,7 @@ func (p *Part) setupHeaders(r *bufio.Reader, defaultContentType string) error {
|
|||
return err
|
||||
}
|
||||
for i := range minvalidParams {
|
||||
p.addWarning(
|
||||
p.addWarningf(
|
||||
ErrorMalformedHeader,
|
||||
"Content-Type header has malformed parameter %q",
|
||||
minvalidParams[i])
|
||||
|
|
@ -172,7 +173,7 @@ func (p *Part) readPartContent(r io.Reader, readPartErrorPolicy ReadPartErrorPol
|
|||
buf, err := io.ReadAll(r)
|
||||
if err != nil {
|
||||
if readPartErrorPolicy != nil && readPartErrorPolicy(p, err) {
|
||||
p.addWarning(ErrorMalformedChildPart, "partial content: %s", err.Error())
|
||||
p.addWarningf(ErrorMalformedChildPart, "partial content: %s", err.Error())
|
||||
return buf, nil
|
||||
}
|
||||
return nil, err
|
||||
|
|
@ -199,26 +200,30 @@ func (p *Part) convertFromDetectedCharset(r io.Reader, readPartErrorPolicy ReadP
|
|||
return nil, errors.WithStack(err)
|
||||
}
|
||||
|
||||
// Restore r.
|
||||
r = bytes.NewReader(buf)
|
||||
|
||||
if p.parser.disableCharacterDetection && p.Charset != "" {
|
||||
// Charset detection is disabled, use declared charset.
|
||||
return p.convertFromStatedCharset(r), nil
|
||||
}
|
||||
|
||||
cs, err := cd.DetectBest(buf)
|
||||
switch err {
|
||||
case nil:
|
||||
// Carry on
|
||||
default:
|
||||
p.addWarning(ErrorCharsetDeclaration, "charset could not be detected: %v", err)
|
||||
p.addWarningf(ErrorCharsetDeclaration, "charset could not be detected: %v", err)
|
||||
}
|
||||
|
||||
// Restore r.
|
||||
r = bytes.NewReader(buf)
|
||||
|
||||
if (p.parser.disableCharacterDetection && p.Charset != "") ||
|
||||
(cs == nil || cs.Confidence < minCharsetConfidence || len(bytes.Runes(buf)) < minCharsetRuneLength) {
|
||||
if cs == nil || cs.Confidence < minCharsetConfidence || len(bytes.Runes(buf)) < p.parser.minCharsetDetectRunes {
|
||||
// Low confidence or not enough characters, use declared character set.
|
||||
return p.convertFromStatedCharset(r), nil
|
||||
}
|
||||
|
||||
// Confidence exceeded our threshold, use detected character set.
|
||||
if p.Charset != "" && !strings.EqualFold(cs.Charset, p.Charset) {
|
||||
p.addWarning(ErrorCharsetDeclaration,
|
||||
p.addWarningf(ErrorCharsetDeclaration,
|
||||
"declared charset %q, detected %q, confidence %d",
|
||||
p.Charset, cs.Charset, cs.Confidence)
|
||||
}
|
||||
|
|
@ -244,7 +249,7 @@ func (p *Part) convertFromStatedCharset(r io.Reader) io.Reader {
|
|||
reader, err := coding.NewCharsetReader(p.Charset, r)
|
||||
if err != nil {
|
||||
// Failed to get a conversion reader.
|
||||
p.addWarning(ErrorCharsetConversion, "failed to get reader for charset %q: %v", p.Charset, err)
|
||||
p.addWarningf(ErrorCharsetConversion, "failed to get reader for charset %q: %v", p.Charset, err)
|
||||
} else {
|
||||
return reader
|
||||
}
|
||||
|
|
@ -258,7 +263,7 @@ func (p *Part) convertFromStatedCharset(r io.Reader) io.Reader {
|
|||
reader, err = coding.NewCharsetReader(p.Charset, r)
|
||||
if err != nil {
|
||||
// Failed to get a conversion reader.
|
||||
p.addWarning(ErrorCharsetConversion, "failed to get reader for charset %q: %v", p.Charset, err)
|
||||
p.addWarningf(ErrorCharsetConversion, "failed to get reader for charset %q: %v", p.Charset, err)
|
||||
} else {
|
||||
return reader
|
||||
}
|
||||
|
|
@ -294,7 +299,7 @@ func (p *Part) decodeContent(r io.Reader, readPartErrorPolicy ReadPartErrorPolic
|
|||
default:
|
||||
// Unknown encoding.
|
||||
validEncoding = false
|
||||
p.addWarning(
|
||||
p.addWarningf(
|
||||
ErrorContentEncoding,
|
||||
"Unrecognized Content-Transfer-Encoding type %q",
|
||||
encoding)
|
||||
|
|
@ -321,7 +326,7 @@ func (p *Part) decodeContent(r io.Reader, readPartErrorPolicy ReadPartErrorPolic
|
|||
}
|
||||
// Set empty content-type error.
|
||||
if p.ContentType == "" {
|
||||
p.addWarning(
|
||||
p.addWarningf(
|
||||
ErrorMissingContentType, "content-type is empty for part id: %s", p.PartID)
|
||||
}
|
||||
return nil
|
||||
|
|
@ -330,7 +335,7 @@ func (p *Part) decodeContent(r io.Reader, readPartErrorPolicy ReadPartErrorPolic
|
|||
// parses media type using custom or default media type parser
|
||||
func (p *Part) parseMediaType(ctype string) (mtype string, params map[string]string, invalidParams []string, err error) {
|
||||
if p.parser == nil || p.parser.customParseMediaType == nil {
|
||||
return mediatype.ParseWithOptions(ctype, mediatype.MediaTypeParseOptions{StripMediaTypeInvalidCharacters: p.parser.stripMediaTypeInvalidCharacters})
|
||||
return mediatype.ParseWithOptions(ctype, mediatype.ParseOptions{StripMediaTypeInvalidCharacters: p.parser.stripMediaTypeInvalidCharacters})
|
||||
}
|
||||
|
||||
return p.parser.customParseMediaType(ctype)
|
||||
|
|
@ -426,7 +431,7 @@ func parseParts(parent *Part, reader *bufio.Reader) error {
|
|||
return err
|
||||
}
|
||||
if br.unbounded {
|
||||
parent.addWarning(ErrorMissingBoundary, "Boundary %q was not closed correctly",
|
||||
parent.addWarningf(ErrorMissingBoundary, "Boundary %q was not closed correctly",
|
||||
parent.Boundary)
|
||||
}
|
||||
if !next {
|
||||
|
|
@ -445,7 +450,7 @@ func parseParts(parent *Part, reader *bufio.Reader) error {
|
|||
bbr := bufio.NewReader(br)
|
||||
if err = p.setupHeaders(bbr, ""); err != nil {
|
||||
if p.parser.skipMalformedParts {
|
||||
parent.addError(ErrorMalformedChildPart, "read header: %s", err.Error())
|
||||
parent.addErrorf(ErrorMalformedChildPart, "read header: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
|
||||
|
|
@ -457,7 +462,7 @@ func parseParts(parent *Part, reader *bufio.Reader) error {
|
|||
// Content is text or data, decode it.
|
||||
if err = p.decodeContent(bbr, p.parser.readPartErrorPolicy); err != nil {
|
||||
if p.parser.skipMalformedParts {
|
||||
parent.addError(ErrorMalformedChildPart, "decode content: %s", err.Error())
|
||||
parent.addErrorf(ErrorMalformedChildPart, "decode content: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
return err
|
||||
|
|
@ -470,7 +475,7 @@ func parseParts(parent *Part, reader *bufio.Reader) error {
|
|||
// Content is another multipart.
|
||||
if err = parseParts(p, bbr); err != nil {
|
||||
if p.parser.skipMalformedParts {
|
||||
parent.addError(ErrorMalformedChildPart, "parse parts: %s", err.Error())
|
||||
parent.addErrorf(ErrorMalformedChildPart, "parse parts: %s", err.Error())
|
||||
continue
|
||||
}
|
||||
return err
|
||||
|
|
@ -491,3 +496,10 @@ func parseParts(parent *Part, reader *bufio.Reader) error {
|
|||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Part) WithEncoder(e *Encoder) *Part {
|
||||
if e != nil {
|
||||
p.encoder = e
|
||||
}
|
||||
return p
|
||||
}
|
||||
11
vendor/github.com/jhillyerd/enmime/v2/shell.nix
generated
vendored
Normal file
11
vendor/github.com/jhillyerd/enmime/v2/shell.nix
generated
vendored
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
{ pkgs ? import <nixpkgs> { } }:
|
||||
pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
delve
|
||||
go
|
||||
golangci-lint
|
||||
gopls
|
||||
];
|
||||
|
||||
hardeningDisable = [ "fortify" ];
|
||||
}
|
||||
3
vendor/github.com/olekukonko/errors/.gitignore
generated
vendored
Normal file
3
vendor/github.com/olekukonko/errors/.gitignore
generated
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Created by .ignore support plugin (hsz.mobi)
|
||||
.idea/
|
||||
tmp/
|
||||
21
vendor/github.com/olekukonko/errors/LICENSE
generated
vendored
Normal file
21
vendor/github.com/olekukonko/errors/LICENSE
generated
vendored
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2025 Oleku Konko
|
||||
|
||||
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.
|
||||
1565
vendor/github.com/olekukonko/errors/README.md
generated
vendored
Normal file
1565
vendor/github.com/olekukonko/errors/README.md
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
610
vendor/github.com/olekukonko/errors/chain.go
generated
vendored
Normal file
610
vendor/github.com/olekukonko/errors/chain.go
generated
vendored
Normal file
|
|
@ -0,0 +1,610 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog" // Standard structured logging package
|
||||
"reflect"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Chain executes functions sequentially with enhanced error handling.
|
||||
// Logging is optional and configured via a slog.Handler.
|
||||
type Chain struct {
|
||||
steps []chainStep // List of steps to execute
|
||||
errors []error // Accumulated errors during execution
|
||||
config chainConfig // Chain-wide configuration
|
||||
lastStep *chainStep // Pointer to the last added step for configuration
|
||||
logHandler slog.Handler // Optional logging handler (nil means no logging)
|
||||
cancel context.CancelFunc // Function to cancel the context
|
||||
}
|
||||
|
||||
// chainStep represents a single step in the chain.
|
||||
type chainStep struct {
|
||||
execute func() error // Function to execute for this step
|
||||
optional bool // If true, errors don't stop the chain
|
||||
config stepConfig // Step-specific configuration
|
||||
}
|
||||
|
||||
// chainConfig holds chain-wide settings.
|
||||
type chainConfig struct {
|
||||
timeout time.Duration // Maximum duration for the entire chain
|
||||
maxErrors int // Maximum number of errors before stopping (-1 for unlimited)
|
||||
autoWrap bool // Whether to automatically wrap errors with additional context
|
||||
}
|
||||
|
||||
// stepConfig holds configuration for an individual step.
|
||||
type stepConfig struct {
|
||||
context map[string]interface{} // Arbitrary key-value pairs for context
|
||||
category ErrorCategory // Category for error classification
|
||||
code int // Numeric error code
|
||||
retry *Retry // Retry policy for the step
|
||||
logOnFail bool // Whether to log errors automatically
|
||||
metricsLabel string // Label for metrics (not used in this code)
|
||||
logAttrs []slog.Attr // Additional attributes for logging
|
||||
}
|
||||
|
||||
// ChainOption defines a function that configures a Chain.
|
||||
type ChainOption func(*Chain)
|
||||
|
||||
// NewChain creates a new Chain with the given options.
|
||||
// Logging is disabled by default (logHandler is nil).
|
||||
func NewChain(opts ...ChainOption) *Chain {
|
||||
c := &Chain{
|
||||
config: chainConfig{
|
||||
autoWrap: true, // Enable error wrapping by default
|
||||
maxErrors: -1, // No limit on errors by default
|
||||
},
|
||||
// logHandler is nil, meaning no logging unless explicitly configured
|
||||
}
|
||||
// Apply each configuration option
|
||||
for _, opt := range opts {
|
||||
opt(c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// ChainWithLogHandler sets a custom slog.Handler for logging.
|
||||
// If handler is nil, logging is effectively disabled.
|
||||
func ChainWithLogHandler(handler slog.Handler) ChainOption {
|
||||
return func(c *Chain) {
|
||||
c.logHandler = handler
|
||||
}
|
||||
}
|
||||
|
||||
// ChainWithTimeout sets a timeout for the entire chain.
|
||||
func ChainWithTimeout(d time.Duration) ChainOption {
|
||||
return func(c *Chain) {
|
||||
c.config.timeout = d
|
||||
}
|
||||
}
|
||||
|
||||
// ChainWithMaxErrors sets the maximum number of errors allowed.
|
||||
// A value <= 0 means no limit.
|
||||
func ChainWithMaxErrors(max int) ChainOption {
|
||||
return func(c *Chain) {
|
||||
if max <= 0 {
|
||||
c.config.maxErrors = -1 // No limit
|
||||
} else {
|
||||
c.config.maxErrors = max
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ChainWithAutoWrap enables or disables automatic error wrapping.
|
||||
func ChainWithAutoWrap(auto bool) ChainOption {
|
||||
return func(c *Chain) {
|
||||
c.config.autoWrap = auto
|
||||
}
|
||||
}
|
||||
|
||||
// Step adds a new step to the chain with the provided function.
|
||||
// The function must return an error or nil.
|
||||
func (c *Chain) Step(fn func() error) *Chain {
|
||||
if fn == nil {
|
||||
// Panic to enforce valid input
|
||||
panic("Chain.Step: provided function cannot be nil")
|
||||
}
|
||||
// Create a new step with default configuration
|
||||
step := chainStep{execute: fn, config: stepConfig{}}
|
||||
c.steps = append(c.steps, step)
|
||||
// Update lastStep to point to the newly added step
|
||||
c.lastStep = &c.steps[len(c.steps)-1]
|
||||
return c
|
||||
}
|
||||
|
||||
// Call adds a step by wrapping a function with arguments.
|
||||
// It uses reflection to validate and invoke the function.
|
||||
func (c *Chain) Call(fn interface{}, args ...interface{}) *Chain {
|
||||
// Wrap the function and arguments into an executable step
|
||||
wrappedFn, err := c.wrapCallable(fn, args...)
|
||||
if err != nil {
|
||||
// Panic on setup errors to catch them early
|
||||
panic(fmt.Sprintf("Chain.Call setup error: %v", err))
|
||||
}
|
||||
// Add the wrapped function as a step
|
||||
step := chainStep{execute: wrappedFn, config: stepConfig{}}
|
||||
c.steps = append(c.steps, step)
|
||||
c.lastStep = &c.steps[len(c.steps)-1]
|
||||
return c
|
||||
}
|
||||
|
||||
// Optional marks the last step as optional.
|
||||
// Optional steps don't stop the chain on error.
|
||||
func (c *Chain) Optional() *Chain {
|
||||
if c.lastStep == nil {
|
||||
// Panic if no step exists to mark as optional
|
||||
panic("Chain.Optional: must call Step() or Call() before Optional()")
|
||||
}
|
||||
c.lastStep.optional = true
|
||||
return c
|
||||
}
|
||||
|
||||
// WithLog adds logging attributes to the last step.
|
||||
func (c *Chain) WithLog(attrs ...slog.Attr) *Chain {
|
||||
if c.lastStep == nil {
|
||||
// Panic if no step exists to configure
|
||||
panic("Chain.WithLog: must call Step() or Call() before WithLog()")
|
||||
}
|
||||
// Append attributes to the step's logging configuration
|
||||
c.lastStep.config.logAttrs = append(c.lastStep.config.logAttrs, attrs...)
|
||||
return c
|
||||
}
|
||||
|
||||
// Timeout sets a timeout for the entire chain.
|
||||
func (c *Chain) Timeout(d time.Duration) *Chain {
|
||||
c.config.timeout = d
|
||||
return c
|
||||
}
|
||||
|
||||
// MaxErrors sets the maximum number of errors allowed.
|
||||
func (c *Chain) MaxErrors(max int) *Chain {
|
||||
if max <= 0 {
|
||||
c.config.maxErrors = -1 // No limit
|
||||
} else {
|
||||
c.config.maxErrors = max
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// With adds a key-value pair to the last step's context.
|
||||
func (c *Chain) With(key string, value interface{}) *Chain {
|
||||
if c.lastStep == nil {
|
||||
// Panic if no step exists to configure
|
||||
panic("Chain.With: must call Step() or Call() before With()")
|
||||
}
|
||||
// Initialize context map if nil
|
||||
if c.lastStep.config.context == nil {
|
||||
c.lastStep.config.context = make(map[string]interface{})
|
||||
}
|
||||
// Add the key-value pair
|
||||
c.lastStep.config.context[key] = value
|
||||
return c
|
||||
}
|
||||
|
||||
// Tag sets an error category for the last step.
|
||||
func (c *Chain) Tag(category ErrorCategory) *Chain {
|
||||
if c.lastStep == nil {
|
||||
// Panic if no step exists to configure
|
||||
panic("Chain.Tag: must call Step() or Call() before Tag()")
|
||||
}
|
||||
c.lastStep.config.category = category
|
||||
return c
|
||||
}
|
||||
|
||||
// Code sets a numeric error code for the last step.
|
||||
func (c *Chain) Code(code int) *Chain {
|
||||
if c.lastStep == nil {
|
||||
// Panic if no step exists to configure
|
||||
panic("Chain.Code: must call Step() or Call() before Code()")
|
||||
}
|
||||
c.lastStep.config.code = code
|
||||
return c
|
||||
}
|
||||
|
||||
// Retry configures retry behavior for the last step.
|
||||
// Retry configures retry behavior for the last step.
|
||||
func (c *Chain) Retry(maxAttempts int, delay time.Duration, opts ...RetryOption) *Chain {
|
||||
if c.lastStep == nil {
|
||||
panic("Chain.Retry: must call Step() or Call() before Retry()")
|
||||
}
|
||||
if maxAttempts < 1 {
|
||||
maxAttempts = 1
|
||||
}
|
||||
|
||||
// Define default retry options
|
||||
retryOpts := []RetryOption{
|
||||
WithMaxAttempts(maxAttempts),
|
||||
WithDelay(delay),
|
||||
WithRetryIf(func(err error) bool { return IsRetryable(err) }),
|
||||
}
|
||||
|
||||
// Add logging for retry attempts if a handler is configured
|
||||
if c.logHandler != nil {
|
||||
step := c.lastStep
|
||||
retryOpts = append(retryOpts, WithOnRetry(func(attempt int, err error) {
|
||||
// Prepare logging attributes
|
||||
logAttrs := []slog.Attr{
|
||||
slog.Int("attempt", attempt),
|
||||
slog.Int("max_attempts", maxAttempts),
|
||||
}
|
||||
// Enhance the error with step context
|
||||
enhancedErr := c.enhanceError(err, step)
|
||||
// Log the retry attempt
|
||||
c.logError(enhancedErr, fmt.Sprintf("Retrying step (attempt %d/%d)", attempt, maxAttempts), step.config, logAttrs...)
|
||||
}))
|
||||
}
|
||||
|
||||
// Append any additional retry options
|
||||
retryOpts = append(retryOpts, opts...)
|
||||
// Create and assign the retry configuration
|
||||
c.lastStep.config.retry = NewRetry(retryOpts...)
|
||||
return c
|
||||
}
|
||||
|
||||
// LogOnFail enables automatic logging of errors for the last step.
|
||||
func (c *Chain) LogOnFail() *Chain {
|
||||
if c.lastStep == nil {
|
||||
// Panic if no step exists to configure
|
||||
panic("Chain.LogOnFail: must call Step() or Call() before LogOnFail()")
|
||||
}
|
||||
c.lastStep.config.logOnFail = true
|
||||
return c
|
||||
}
|
||||
|
||||
// Run executes the chain, stopping on the first non-optional error.
|
||||
// It returns the first error encountered or nil if all steps succeed.
|
||||
func (c *Chain) Run() error {
|
||||
// Create a context with timeout or cancellation
|
||||
ctx, cancel := c.getContextAndCancel()
|
||||
defer cancel()
|
||||
c.cancel = cancel
|
||||
// Clear any previous errors
|
||||
c.errors = c.errors[:0]
|
||||
|
||||
// Execute each step in sequence
|
||||
for i := range c.steps {
|
||||
step := &c.steps[i]
|
||||
// Check if the context has been canceled
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err := ctx.Err()
|
||||
// Enhance the error with step context
|
||||
enhancedErr := c.enhanceError(err, step)
|
||||
c.errors = append(c.errors, enhancedErr)
|
||||
// Log the context error
|
||||
c.logError(enhancedErr, "Chain stopped due to context error before step", step.config)
|
||||
return enhancedErr
|
||||
default:
|
||||
}
|
||||
|
||||
// Execute the step
|
||||
err := c.executeStep(ctx, step)
|
||||
if err != nil {
|
||||
// Enhance the error with step context
|
||||
enhancedErr := c.enhanceError(err, step)
|
||||
c.errors = append(c.errors, enhancedErr)
|
||||
// Log the error if required
|
||||
if step.config.logOnFail || !step.optional {
|
||||
logMsg := "Chain stopped due to error in step"
|
||||
if step.optional {
|
||||
logMsg = "Optional step failed"
|
||||
}
|
||||
c.logError(enhancedErr, logMsg, step.config)
|
||||
}
|
||||
// Stop execution if the step is not optional
|
||||
if !step.optional {
|
||||
return enhancedErr
|
||||
}
|
||||
}
|
||||
}
|
||||
// Return nil if all steps completed successfully
|
||||
return nil
|
||||
}
|
||||
|
||||
// RunAll executes all steps, collecting errors without stopping.
|
||||
// It returns a MultiError containing all errors or nil if none occurred.
|
||||
func (c *Chain) RunAll() error {
|
||||
ctx, cancel := c.getContextAndCancel()
|
||||
defer cancel()
|
||||
c.cancel = cancel
|
||||
c.errors = c.errors[:0]
|
||||
multi := NewMultiError()
|
||||
|
||||
for i := range c.steps {
|
||||
step := &c.steps[i]
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
err := ctx.Err()
|
||||
enhancedErr := c.enhanceError(err, step)
|
||||
c.errors = append(c.errors, enhancedErr)
|
||||
multi.Add(enhancedErr)
|
||||
c.logError(enhancedErr, "Chain stopped due to context error before step (RunAll)", step.config)
|
||||
goto endRunAll
|
||||
default:
|
||||
}
|
||||
|
||||
err := c.executeStep(ctx, step)
|
||||
if err != nil {
|
||||
enhancedErr := c.enhanceError(err, step)
|
||||
c.errors = append(c.errors, enhancedErr)
|
||||
multi.Add(enhancedErr)
|
||||
if step.config.logOnFail && c.logHandler != nil {
|
||||
c.logError(enhancedErr, "Step failed during RunAll", step.config)
|
||||
}
|
||||
if c.config.maxErrors > 0 && multi.Count() >= c.config.maxErrors {
|
||||
if c.logHandler != nil {
|
||||
// Create a logger to log the max errors condition
|
||||
logger := slog.New(c.logHandler)
|
||||
logger.LogAttrs(
|
||||
context.Background(),
|
||||
slog.LevelError,
|
||||
fmt.Sprintf("Stopping RunAll after reaching max errors (%d)", c.config.maxErrors),
|
||||
slog.Int("max_errors", c.config.maxErrors),
|
||||
)
|
||||
}
|
||||
goto endRunAll
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
endRunAll:
|
||||
return multi.Single()
|
||||
}
|
||||
|
||||
// Errors returns a copy of the collected errors.
|
||||
func (c *Chain) Errors() []error {
|
||||
if len(c.errors) == 0 {
|
||||
return nil
|
||||
}
|
||||
// Create a copy to prevent external modification
|
||||
errs := make([]error, len(c.errors))
|
||||
copy(errs, c.errors)
|
||||
return errs
|
||||
}
|
||||
|
||||
// Len returns the number of steps in the chain.
|
||||
func (c *Chain) Len() int {
|
||||
return len(c.steps)
|
||||
}
|
||||
|
||||
// HasErrors checks if any errors were collected.
|
||||
func (c *Chain) HasErrors() bool {
|
||||
return len(c.errors) > 0
|
||||
}
|
||||
|
||||
// LastError returns the most recent error or nil if none exist.
|
||||
func (c *Chain) LastError() error {
|
||||
if len(c.errors) > 0 {
|
||||
return c.errors[len(c.errors)-1]
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Reset clears the chain's steps, errors, and context.
|
||||
func (c *Chain) Reset() {
|
||||
if c.cancel != nil {
|
||||
// Cancel any active context
|
||||
c.cancel()
|
||||
c.cancel = nil
|
||||
}
|
||||
// Clear steps and errors
|
||||
c.steps = c.steps[:0]
|
||||
c.errors = c.errors[:0]
|
||||
c.lastStep = nil
|
||||
}
|
||||
|
||||
// Unwrap returns the collected errors (alias for Errors).
|
||||
func (c *Chain) Unwrap() []error {
|
||||
return c.errors
|
||||
}
|
||||
|
||||
// getContextAndCancel creates a context based on the chain's timeout.
|
||||
// It returns a context and its cancellation function.
|
||||
func (c *Chain) getContextAndCancel() (context.Context, context.CancelFunc) {
|
||||
parentCtx := context.Background()
|
||||
if c.config.timeout > 0 {
|
||||
// Create a context with a timeout
|
||||
return context.WithTimeout(parentCtx, c.config.timeout)
|
||||
}
|
||||
// Create a cancellable context
|
||||
return context.WithCancel(parentCtx)
|
||||
}
|
||||
|
||||
// logError logs an error with step-specific context and attributes.
|
||||
// It only logs if a handler is configured and the error is non-nil.
|
||||
func (c *Chain) logError(err error, msg string, config stepConfig, additionalAttrs ...slog.Attr) {
|
||||
// Skip logging if no handler is set or error is nil
|
||||
if c == nil || c.logHandler == nil || err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
// Create a logger on demand using the configured handler
|
||||
logger := slog.New(c.logHandler)
|
||||
|
||||
// Initialize attributes with error and timestamp
|
||||
allAttrs := make([]slog.Attr, 0, 5+len(config.logAttrs)+len(additionalAttrs))
|
||||
allAttrs = append(allAttrs, slog.Any("error", err))
|
||||
allAttrs = append(allAttrs, slog.Time("timestamp", time.Now()))
|
||||
|
||||
// Add step-specific metadata
|
||||
if config.category != "" {
|
||||
allAttrs = append(allAttrs, slog.String("category", string(config.category)))
|
||||
}
|
||||
if config.code != 0 {
|
||||
allAttrs = append(allAttrs, slog.Int("code", config.code))
|
||||
}
|
||||
for k, v := range config.context {
|
||||
allAttrs = append(allAttrs, slog.Any(k, v))
|
||||
}
|
||||
allAttrs = append(allAttrs, config.logAttrs...)
|
||||
allAttrs = append(allAttrs, additionalAttrs...)
|
||||
|
||||
// Add stack trace and error name if the error is of type *Error
|
||||
if e, ok := err.(*Error); ok {
|
||||
if stack := e.Stack(); len(stack) > 0 {
|
||||
// Format stack trace, truncating if too long
|
||||
stackStr := "\n\t" + strings.Join(stack, "\n\t")
|
||||
if len(stackStr) > 1000 {
|
||||
stackStr = stackStr[:1000] + "..."
|
||||
}
|
||||
allAttrs = append(allAttrs, slog.String("stacktrace", stackStr))
|
||||
}
|
||||
if name := e.Name(); name != "" {
|
||||
allAttrs = append(allAttrs, slog.String("error_name", name))
|
||||
}
|
||||
}
|
||||
|
||||
// Log the error at ERROR level with all attributes
|
||||
// Use a defer to catch any panics during logging
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
// Print to stdout to avoid infinite recursion
|
||||
fmt.Printf("ERROR: Recovered from panic during logging: %v\nAttributes: %v\n", r, allAttrs)
|
||||
}
|
||||
}()
|
||||
logger.LogAttrs(context.Background(), slog.LevelError, msg, allAttrs...)
|
||||
}
|
||||
|
||||
// wrapCallable wraps a function and its arguments into an executable step.
|
||||
// It uses reflection to validate the function and arguments.
|
||||
func (c *Chain) wrapCallable(fn interface{}, args ...interface{}) (func() error, error) {
|
||||
val := reflect.ValueOf(fn)
|
||||
typ := val.Type()
|
||||
|
||||
// Ensure the provided value is a function
|
||||
if typ.Kind() != reflect.Func {
|
||||
return nil, fmt.Errorf("provided 'fn' is not a function (got %T)", fn)
|
||||
}
|
||||
// Check if the number of arguments matches the function's signature
|
||||
if typ.NumIn() != len(args) {
|
||||
return nil, fmt.Errorf("function expects %d arguments, but %d were provided", typ.NumIn(), len(args))
|
||||
}
|
||||
|
||||
// Prepare argument values
|
||||
argVals := make([]reflect.Value, len(args))
|
||||
errorType := reflect.TypeOf((*error)(nil)).Elem()
|
||||
for i, arg := range args {
|
||||
expectedType := typ.In(i)
|
||||
var providedVal reflect.Value
|
||||
if arg != nil {
|
||||
providedVal = reflect.ValueOf(arg)
|
||||
// Check if the argument type is assignable to the expected type
|
||||
if !providedVal.Type().AssignableTo(expectedType) {
|
||||
// Special case for error interfaces
|
||||
if expectedType.Kind() == reflect.Interface && expectedType.Implements(errorType) && providedVal.Type().Implements(errorType) {
|
||||
// Allow error interface
|
||||
} else {
|
||||
return nil, fmt.Errorf("argument %d type mismatch: expected %s, got %s", i, expectedType, providedVal.Type())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Handle nil arguments for nullable types
|
||||
switch expectedType.Kind() {
|
||||
case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice:
|
||||
providedVal = reflect.Zero(expectedType)
|
||||
default:
|
||||
return nil, fmt.Errorf("argument %d is nil, but expected non-nillable type %s", i, expectedType)
|
||||
}
|
||||
}
|
||||
argVals[i] = providedVal
|
||||
}
|
||||
|
||||
// Validate the function's return type
|
||||
if typ.NumOut() > 1 || (typ.NumOut() == 1 && !typ.Out(0).Implements(errorType)) {
|
||||
return nil, fmt.Errorf("function must return either no values or a single error (got %d return values)", typ.NumOut())
|
||||
}
|
||||
|
||||
// Return a wrapped function that calls the original with the provided arguments
|
||||
return func() error {
|
||||
results := val.Call(argVals)
|
||||
if len(results) == 1 && results[0].Interface() != nil {
|
||||
return results[0].Interface().(error)
|
||||
}
|
||||
return nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
// executeStep runs a single step, applying retries if configured.
|
||||
func (c *Chain) executeStep(ctx context.Context, step *chainStep) error {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
default:
|
||||
}
|
||||
if step.config.retry != nil {
|
||||
retry := step.config.retry.Transform(WithContext(ctx))
|
||||
// Wrap step execution to respect context
|
||||
wrappedFn := func() error {
|
||||
type result struct {
|
||||
err error
|
||||
}
|
||||
done := make(chan result, 1)
|
||||
go func() {
|
||||
done <- result{err: step.execute()}
|
||||
}()
|
||||
select {
|
||||
case res := <-done:
|
||||
return res.err
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
return retry.Execute(wrappedFn)
|
||||
}
|
||||
// Non-retry case also respects context
|
||||
type result struct {
|
||||
err error
|
||||
}
|
||||
done := make(chan result, 1)
|
||||
go func() {
|
||||
done <- result{err: step.execute()}
|
||||
}()
|
||||
select {
|
||||
case res := <-done:
|
||||
return res.err
|
||||
case <-ctx.Done():
|
||||
return ctx.Err()
|
||||
}
|
||||
}
|
||||
|
||||
// enhanceError wraps an error with additional context from the step.
|
||||
func (c *Chain) enhanceError(err error, step *chainStep) error {
|
||||
if err == nil || !c.config.autoWrap {
|
||||
// Return the error unchanged if nil or autoWrap is disabled
|
||||
return err
|
||||
}
|
||||
|
||||
// Initialize the base error
|
||||
var baseError *Error
|
||||
if e, ok := err.(*Error); ok {
|
||||
// Copy existing *Error to preserve its properties
|
||||
baseError = e.Copy()
|
||||
} else {
|
||||
// Create a new *Error wrapping the original
|
||||
baseError = New(err.Error()).Wrap(err).WithStack()
|
||||
}
|
||||
|
||||
if step != nil {
|
||||
// Add step-specific context to the error
|
||||
if step.config.category != "" && baseError.Category() == "" {
|
||||
baseError.WithCategory(step.config.category)
|
||||
}
|
||||
if step.config.code != 0 && baseError.Code() == 0 {
|
||||
baseError.WithCode(step.config.code)
|
||||
}
|
||||
for k, v := range step.config.context {
|
||||
baseError.With(k, v)
|
||||
}
|
||||
for _, attr := range step.config.logAttrs {
|
||||
baseError.With(attr.Key, attr.Value.Any())
|
||||
}
|
||||
if step.config.retry != nil && !baseError.HasContextKey(ctxRetry) {
|
||||
// Mark the error as retryable if retries are configured
|
||||
baseError.WithRetryable()
|
||||
}
|
||||
}
|
||||
|
||||
return baseError
|
||||
}
|
||||
1496
vendor/github.com/olekukonko/errors/errors.go
generated
vendored
Normal file
1496
vendor/github.com/olekukonko/errors/errors.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load diff
432
vendor/github.com/olekukonko/errors/helper.go
generated
vendored
Normal file
432
vendor/github.com/olekukonko/errors/helper.go
generated
vendored
Normal file
|
|
@ -0,0 +1,432 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// As wraps errors.As, using custom type assertion for *Error types.
|
||||
// Falls back to standard errors.As for non-*Error types.
|
||||
// Returns false if either err or target is nil.
|
||||
func As(err error, target interface{}) bool {
|
||||
if err == nil || target == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
// First try our custom *Error handling
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.As(target)
|
||||
}
|
||||
|
||||
// Fall back to standard errors.As
|
||||
return errors.As(err, target)
|
||||
}
|
||||
|
||||
// Code returns the status code of an error, if it is an *Error.
|
||||
// Returns 500 as a default for non-*Error types to indicate an internal error.
|
||||
func Code(err error) int {
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.Code()
|
||||
}
|
||||
return DefaultCode
|
||||
}
|
||||
|
||||
// Context extracts the context map from an error, if it is an *Error.
|
||||
// Returns nil for non-*Error types or if no context is present.
|
||||
func Context(err error) map[string]interface{} {
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.Context()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert transforms any error into an *Error, preserving its message and wrapping it if needed.
|
||||
// Returns nil if the input is nil; returns the original if already an *Error.
|
||||
// Uses multiple strategies: direct assertion, errors.As, manual unwrapping, and fallback creation.
|
||||
func Convert(err error) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// First try direct type assertion (fast path)
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e
|
||||
}
|
||||
|
||||
// Try using errors.As (more flexible)
|
||||
var e *Error
|
||||
if errors.As(err, &e) {
|
||||
return e
|
||||
}
|
||||
|
||||
// Manual unwrapping as fallback
|
||||
visited := make(map[error]bool)
|
||||
for unwrapped := err; unwrapped != nil; {
|
||||
if visited[unwrapped] {
|
||||
break // Cycle detected
|
||||
}
|
||||
visited[unwrapped] = true
|
||||
if e, ok := unwrapped.(*Error); ok {
|
||||
return e
|
||||
}
|
||||
unwrapped = errors.Unwrap(unwrapped)
|
||||
}
|
||||
|
||||
// Final fallback: create new error with original message and wrap it
|
||||
return New(err.Error()).Wrap(err)
|
||||
}
|
||||
|
||||
// Count returns the occurrence count of an error, if it is an *Error.
|
||||
// Returns 0 for non-*Error types.
|
||||
func Count(err error) uint64 {
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.Count()
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// Find searches the error chain for the first error matching pred.
|
||||
// Returns nil if no match is found or pred is nil; traverses both Unwrap() and Cause() chains.
|
||||
func Find(err error, pred func(error) bool) error {
|
||||
for current := err; current != nil; {
|
||||
if pred(current) {
|
||||
return current
|
||||
}
|
||||
|
||||
// Attempt to unwrap using Unwrap() or Cause()
|
||||
switch v := current.(type) {
|
||||
case interface{ Unwrap() error }:
|
||||
current = v.Unwrap()
|
||||
case interface{ Cause() error }:
|
||||
current = v.Cause()
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// From transforms any error into an *Error, preserving its message and wrapping it if needed.
|
||||
// Alias of Convert; returns nil if input is nil, original if already an *Error.
|
||||
func From(err error) *Error {
|
||||
return Convert(err)
|
||||
}
|
||||
|
||||
// FromContext creates an *Error from a context and an existing error.
|
||||
// Enhances the error with context info: timeout status, deadline, or cancellation.
|
||||
// Returns nil if input error is nil; does not store context values directly.
|
||||
func FromContext(ctx context.Context, err error) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
e := New(err.Error())
|
||||
|
||||
// Handle context errors
|
||||
switch ctx.Err() {
|
||||
case context.DeadlineExceeded:
|
||||
e.WithTimeout()
|
||||
if deadline, ok := ctx.Deadline(); ok {
|
||||
e.With("deadline", deadline.Format(time.RFC3339))
|
||||
}
|
||||
case context.Canceled:
|
||||
e.With("cancelled", true)
|
||||
}
|
||||
|
||||
return e
|
||||
}
|
||||
|
||||
// Category returns the category of an error, if it is an *Error.
|
||||
// Returns an empty string for non-*Error types or unset categories.
|
||||
func Category(err error) string {
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.category
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Has checks if an error contains meaningful content.
|
||||
// Returns true for non-nil standard errors or *Error with content (msg, name, template, or cause).
|
||||
func Has(err error) bool {
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.Has()
|
||||
}
|
||||
return err != nil
|
||||
}
|
||||
|
||||
// HasContextKey checks if the error's context contains the specified key.
|
||||
// Returns false for non-*Error types or if the key is not present in the context.
|
||||
func HasContextKey(err error, key string) bool {
|
||||
if e, ok := err.(*Error); ok {
|
||||
ctx := e.Context()
|
||||
if ctx != nil {
|
||||
_, exists := ctx[key]
|
||||
return exists
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Is wraps errors.Is, using custom matching for *Error types.
|
||||
// Falls back to standard errors.Is for non-*Error types; returns true if err equals target.
|
||||
func Is(err, target error) bool {
|
||||
if err == nil || target == nil {
|
||||
return err == target
|
||||
}
|
||||
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.Is(target)
|
||||
}
|
||||
|
||||
// Use standard errors.Is for non-Error types
|
||||
return errors.Is(err, target)
|
||||
}
|
||||
|
||||
// IsError checks if an error is an instance of *Error.
|
||||
// Returns true only for this package's custom error type; false for nil or other types.
|
||||
func IsError(err error) bool {
|
||||
_, ok := err.(*Error)
|
||||
return ok
|
||||
}
|
||||
|
||||
// IsEmpty checks if an error has no meaningful content.
|
||||
// Returns true for nil errors, empty *Error instances, or standard errors with whitespace-only messages.
|
||||
func IsEmpty(err error) bool {
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.IsEmpty()
|
||||
}
|
||||
return strings.TrimSpace(err.Error()) == ""
|
||||
}
|
||||
|
||||
// IsNull checks if an error is nil or represents a NULL value.
|
||||
// Delegates to *Error’s IsNull for custom errors; uses sqlNull for others.
|
||||
func IsNull(err error) bool {
|
||||
if err == nil {
|
||||
return true
|
||||
}
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.IsNull()
|
||||
}
|
||||
return sqlNull(err)
|
||||
}
|
||||
|
||||
// IsRetryable checks if an error is retryable.
|
||||
// For *Error, checks context for retry flag; for others, looks for "retry" or timeout in message.
|
||||
// Returns false for nil errors; thread-safe for *Error types.
|
||||
func IsRetryable(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
if e, ok := err.(*Error); ok {
|
||||
e.mu.RLock()
|
||||
defer e.mu.RUnlock()
|
||||
// Check smallContext directly if context map isn’t populated
|
||||
for i := int32(0); i < e.smallCount; i++ {
|
||||
if e.smallContext[i].key == ctxRetry {
|
||||
if val, ok := e.smallContext[i].value.(bool); ok {
|
||||
return val
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check regular context
|
||||
if e.context != nil {
|
||||
if val, ok := e.context[ctxRetry].(bool); ok {
|
||||
return val
|
||||
}
|
||||
}
|
||||
// Check cause recursively
|
||||
if e.cause != nil {
|
||||
return IsRetryable(e.cause)
|
||||
}
|
||||
}
|
||||
lowerMsg := strings.ToLower(err.Error())
|
||||
return IsTimeout(err) || strings.Contains(lowerMsg, "retry")
|
||||
}
|
||||
|
||||
// IsTimeout checks if an error indicates a timeout.
|
||||
// For *Error, checks context for timeout flag; for others, looks for "timeout" in message.
|
||||
// Returns false for nil errors.
|
||||
func IsTimeout(err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
if e, ok := err.(*Error); ok {
|
||||
if val, ok := e.Context()[ctxTimeout].(bool); ok {
|
||||
return val
|
||||
}
|
||||
}
|
||||
return strings.Contains(strings.ToLower(err.Error()), "timeout")
|
||||
}
|
||||
|
||||
// Merge combines multiple errors into a single *Error.
|
||||
// Aggregates messages with "; " separator, merges contexts and stacks; returns nil if no errors provided.
|
||||
func Merge(errs ...error) *Error {
|
||||
if len(errs) == 0 {
|
||||
return nil
|
||||
}
|
||||
var messages []string
|
||||
combined := New("")
|
||||
for _, err := range errs {
|
||||
if err == nil {
|
||||
continue
|
||||
}
|
||||
messages = append(messages, err.Error())
|
||||
if e, ok := err.(*Error); ok {
|
||||
if e.stack != nil && combined.stack == nil {
|
||||
combined.WithStack() // Capture stack from first *Error with stack
|
||||
}
|
||||
if ctx := e.Context(); ctx != nil {
|
||||
for k, v := range ctx {
|
||||
combined.With(k, v)
|
||||
}
|
||||
}
|
||||
if e.cause != nil {
|
||||
combined.Wrap(e.cause)
|
||||
}
|
||||
} else {
|
||||
combined.Wrap(err)
|
||||
}
|
||||
}
|
||||
if len(messages) > 0 {
|
||||
combined.msg = strings.Join(messages, "; ")
|
||||
}
|
||||
return combined
|
||||
}
|
||||
|
||||
// Name returns the name of an error, if it is an *Error.
|
||||
// Returns an empty string for non-*Error types or unset names.
|
||||
func Name(err error) string {
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// UnwrapAll returns a slice of all errors in the chain, including the root error.
|
||||
// Traverses both Unwrap() and Cause() chains; returns nil if err is nil.
|
||||
func UnwrapAll(err error) []error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.UnwrapAll()
|
||||
}
|
||||
var result []error
|
||||
Walk(err, func(e error) {
|
||||
result = append(result, e)
|
||||
})
|
||||
return result
|
||||
}
|
||||
|
||||
// Stack extracts the stack trace from an error, if it is an *Error.
|
||||
// Returns nil for non-*Error types or if no stack is present.
|
||||
func Stack(err error) []string {
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.Stack()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Transform applies transformations to an error, returning a new *Error.
|
||||
// Creates a new *Error from non-*Error types before applying fn; returns nil if err is nil.
|
||||
func Transform(err error, fn func(*Error)) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if e, ok := err.(*Error); ok {
|
||||
newErr := e.Copy()
|
||||
fn(newErr)
|
||||
return newErr
|
||||
}
|
||||
// If not an *Error, create a new one and transform it
|
||||
newErr := New(err.Error())
|
||||
fn(newErr)
|
||||
return newErr
|
||||
}
|
||||
|
||||
// Unwrap returns the underlying cause of an error, if it implements Unwrap.
|
||||
// For *Error, returns cause; for others, returns the error itself; nil if err is nil.
|
||||
func Unwrap(err error) error {
|
||||
for current := err; current != nil; {
|
||||
if e, ok := current.(*Error); ok {
|
||||
if e.cause == nil {
|
||||
return current
|
||||
}
|
||||
current = e.cause
|
||||
} else {
|
||||
return current
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Walk traverses the error chain, applying fn to each error.
|
||||
// Supports both Unwrap() and Cause() interfaces; stops at nil or non-unwrappable errors.
|
||||
func Walk(err error, fn func(error)) {
|
||||
for current := err; current != nil; {
|
||||
fn(current)
|
||||
|
||||
// Attempt to unwrap using Unwrap() or Cause()
|
||||
switch v := current.(type) {
|
||||
case interface{ Unwrap() error }:
|
||||
current = v.Unwrap()
|
||||
case interface{ Cause() error }:
|
||||
current = v.Cause()
|
||||
default:
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// With adds a key-value pair to an error's context, if it is an *Error.
|
||||
// Returns the original error unchanged if not an *Error; no-op for non-*Error types.
|
||||
func With(err error, key string, value interface{}) error {
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.With(key, value)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// WithStack converts any error to an *Error and captures a stack trace.
|
||||
// Returns nil if input is nil; adds stack to existing *Error or wraps non-*Error types.
|
||||
func WithStack(err error) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if e, ok := err.(*Error); ok {
|
||||
return e.WithStack()
|
||||
}
|
||||
return New(err.Error()).WithStack().Wrap(err)
|
||||
}
|
||||
|
||||
// Wrap creates a new *Error that wraps another error with additional context.
|
||||
// Uses a copy of the provided wrapper *Error; returns nil if err is nil.
|
||||
func Wrap(err error, wrapper *Error) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
if wrapper == nil {
|
||||
wrapper = newError()
|
||||
}
|
||||
newErr := wrapper.Copy()
|
||||
newErr.cause = err
|
||||
return newErr
|
||||
}
|
||||
|
||||
// Wrapf creates a new formatted *Error that wraps another error.
|
||||
// Formats the message and sets the cause; returns nil if err is nil.
|
||||
func Wrapf(err error, format string, args ...interface{}) *Error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
e := newError()
|
||||
e.msg = fmt.Sprintf(format, args...)
|
||||
e.cause = err
|
||||
return e
|
||||
}
|
||||
225
vendor/github.com/olekukonko/errors/inspect.go
generated
vendored
Normal file
225
vendor/github.com/olekukonko/errors/inspect.go
generated
vendored
Normal file
|
|
@ -0,0 +1,225 @@
|
|||
// File: inspect.go
|
||||
// Updated to support both error and *Error with delegation for cleaner *Error handling
|
||||
|
||||
package errors
|
||||
|
||||
import (
|
||||
stderrs "errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// Inspect provides detailed examination of an error, handling both single errors and MultiError
|
||||
func Inspect(err error) {
|
||||
if err == nil {
|
||||
fmt.Println("No error occurred")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("\n=== Error Inspection ===\n")
|
||||
fmt.Printf("Top-level error: %v\n", err)
|
||||
fmt.Printf("Top-level error type: %T\n", err)
|
||||
|
||||
// Handle *Error directly
|
||||
if e, ok := err.(*Error); ok {
|
||||
InspectError(e)
|
||||
return
|
||||
}
|
||||
|
||||
// Handle MultiError
|
||||
if multi, ok := err.(*MultiError); ok {
|
||||
allErrors := multi.Errors()
|
||||
fmt.Printf("\nContains %d errors:\n", len(allErrors))
|
||||
for i, e := range allErrors {
|
||||
fmt.Printf("\n--- Error %d ---\n", i+1)
|
||||
inspectSingleError(e)
|
||||
}
|
||||
} else {
|
||||
// Inspect single error if not MultiError or *Error
|
||||
fmt.Println("\n--- Details ---")
|
||||
inspectSingleError(err)
|
||||
}
|
||||
|
||||
// Additional diagnostics
|
||||
fmt.Println("\n--- Diagnostics ---")
|
||||
if IsRetryable(err) {
|
||||
fmt.Println("- Error chain contains retryable errors")
|
||||
}
|
||||
if IsTimeout(err) {
|
||||
fmt.Println("- Error chain contains timeout errors")
|
||||
}
|
||||
if code := getErrorCode(err); code != 0 {
|
||||
fmt.Printf("- Highest priority error code: %d\n", code)
|
||||
}
|
||||
fmt.Printf("========================\n\n")
|
||||
}
|
||||
|
||||
// InspectError provides detailed inspection of a specific *Error instance
|
||||
func InspectError(err *Error) {
|
||||
if err == nil {
|
||||
fmt.Println("No error occurred")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("\n=== Error Inspection (*Error) ===\n")
|
||||
fmt.Printf("Top-level error: %v\n", err)
|
||||
fmt.Printf("Top-level error type: %T\n", err)
|
||||
|
||||
fmt.Println("\n--- Details ---")
|
||||
inspectSingleError(err) // Delegate to handle unwrapping and details
|
||||
|
||||
// Additional diagnostics specific to *Error
|
||||
fmt.Println("\n--- Diagnostics ---")
|
||||
if IsRetryable(err) {
|
||||
fmt.Println("- Error is retryable")
|
||||
}
|
||||
if IsTimeout(err) {
|
||||
fmt.Println("- Error chain contains timeout errors")
|
||||
}
|
||||
if code := err.Code(); code != 0 {
|
||||
fmt.Printf("- Error code: %d\n", code)
|
||||
}
|
||||
fmt.Printf("========================\n\n")
|
||||
}
|
||||
|
||||
// inspectSingleError handles inspection of a single error (may be part of a chain)
|
||||
func inspectSingleError(err error) {
|
||||
if err == nil {
|
||||
fmt.Println(" (nil error)")
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf(" Error: %v\n", err)
|
||||
fmt.Printf(" Type: %T\n", err)
|
||||
|
||||
// Handle wrapped errors, including *Error type
|
||||
var currentErr error = err
|
||||
depth := 0
|
||||
for currentErr != nil {
|
||||
prefix := strings.Repeat(" ", depth+1)
|
||||
if depth > 0 {
|
||||
fmt.Printf("%sWrapped Cause (%T): %v\n", prefix, currentErr, currentErr)
|
||||
}
|
||||
|
||||
// Check if it's our specific *Error type
|
||||
if e, ok := currentErr.(*Error); ok {
|
||||
if name := e.Name(); name != "" {
|
||||
fmt.Printf("%sName: %s\n", prefix, name)
|
||||
}
|
||||
if cat := e.Category(); cat != "" {
|
||||
fmt.Printf("%sCategory: %s\n", prefix, cat)
|
||||
}
|
||||
if code := e.Code(); code != 0 {
|
||||
fmt.Printf("%sCode: %d\n", prefix, code)
|
||||
}
|
||||
if ctx := e.Context(); len(ctx) > 0 {
|
||||
fmt.Printf("%sContext:\n", prefix)
|
||||
for k, v := range ctx {
|
||||
fmt.Printf("%s %s: %v\n", prefix, k, v)
|
||||
}
|
||||
}
|
||||
if stack := e.Stack(); len(stack) > 0 {
|
||||
fmt.Printf("%sStack (Top 3):\n", prefix)
|
||||
limit := 3
|
||||
if len(stack) < limit {
|
||||
limit = len(stack)
|
||||
}
|
||||
for i := 0; i < limit; i++ {
|
||||
fmt.Printf("%s %s\n", prefix, stack[i])
|
||||
}
|
||||
if len(stack) > limit {
|
||||
fmt.Printf("%s ... (%d more frames)\n", prefix, len(stack)-limit)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Unwrap using standard errors.Unwrap and handle *Error Unwrap
|
||||
var nextErr error
|
||||
// Prioritize *Error's Unwrap if available AND it returns non-nil
|
||||
if e, ok := currentErr.(*Error); ok {
|
||||
unwrapped := e.Unwrap()
|
||||
if unwrapped != nil {
|
||||
nextErr = unwrapped
|
||||
} else {
|
||||
// If *Error.Unwrap returns nil, fall back to standard unwrap
|
||||
// This handles cases where *Error might wrap a non-standard error
|
||||
// or where its internal cause is deliberately nil.
|
||||
nextErr = stderrs.Unwrap(currentErr)
|
||||
}
|
||||
} else {
|
||||
nextErr = stderrs.Unwrap(currentErr) // Fall back to standard unwrap for non-*Error types
|
||||
}
|
||||
|
||||
// Prevent infinite loops if Unwrap returns the same error, or stop if no more unwrapping
|
||||
if nextErr == currentErr || nextErr == nil {
|
||||
break
|
||||
}
|
||||
currentErr = nextErr
|
||||
depth++
|
||||
if depth > 10 { // Safety break for very deep or potentially cyclic chains
|
||||
fmt.Printf("%s... (chain too deep or potential cycle)\n", strings.Repeat(" ", depth+1))
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// getErrorCode traverses the error chain to find the highest priority code.
|
||||
// It uses errors.As to find the first *Error in the chain.
|
||||
func getErrorCode(err error) int {
|
||||
var code int = 0 // Default code
|
||||
var target *Error
|
||||
if As(err, &target) { // Use the package's As helper
|
||||
if target != nil { // Add nil check for safety
|
||||
code = target.Code()
|
||||
}
|
||||
}
|
||||
// If the top-level error is *Error and has a code, it might take precedence.
|
||||
// This depends on desired logic. Let's keep it simple for now: first code found by As.
|
||||
if code == 0 { // Only check top-level if As didn't find one with a code
|
||||
if e, ok := err.(*Error); ok {
|
||||
code = e.Code()
|
||||
}
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
// handleError demonstrates using Inspect with additional handling logic
|
||||
func handleError(err error) {
|
||||
fmt.Println("\n=== Processing Failure ===")
|
||||
Inspect(err) // Use the primary Inspect function
|
||||
|
||||
// Additional handling based on inspection
|
||||
code := getErrorCode(err) // Use the helper
|
||||
|
||||
switch {
|
||||
case IsTimeout(err):
|
||||
fmt.Println("\nAction: Check connectivity or increase timeout")
|
||||
case code == 402: // Check code obtained via helper
|
||||
fmt.Println("\nAction: Payment processing failed - notify billing")
|
||||
default:
|
||||
fmt.Println("\nAction: Generic failure handling")
|
||||
}
|
||||
}
|
||||
|
||||
// processOrder demonstrates Chain usage with Inspect
|
||||
func processOrder() error {
|
||||
validateInput := func() error { return nil }
|
||||
processPayment := func() error { return stderrs.New("credit card declined") }
|
||||
sendNotification := func() error { fmt.Println("Notification sent."); return nil }
|
||||
logOrder := func() error { fmt.Println("Order logged."); return nil }
|
||||
|
||||
chain := NewChain(ChainWithTimeout(2*time.Second)).
|
||||
Step(validateInput).Tag("validation").
|
||||
Step(processPayment).Tag("billing").Code(402).Retry(3, 100*time.Millisecond, WithRetryIf(IsRetryable)).
|
||||
Step(sendNotification).Optional().
|
||||
Step(logOrder)
|
||||
|
||||
err := chain.Run()
|
||||
if err != nil {
|
||||
handleError(err) // Call the unified error handler
|
||||
return err // Propagate the error if needed
|
||||
}
|
||||
fmt.Println("Order processed successfully!")
|
||||
return nil
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue