mirror of
https://github.com/foomo/gofuncy.git
synced 2025-10-16 12:25:41 +00:00
initial commit
This commit is contained in:
commit
e17ae3a43a
12
.editorconfig
Normal file
12
.editorconfig
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
charset = utf-8
|
||||||
|
end_of_line = lf
|
||||||
|
indent_size = 2
|
||||||
|
indent_style = tab
|
||||||
|
insert_final_newline = true
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
|
||||||
|
[*.{yaml,yml,md,mdx}]
|
||||||
|
indent_style = space
|
||||||
25
.github/dependabot.yml
vendored
Normal file
25
.github/dependabot.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
updates:
|
||||||
|
- package-ecosystem: 'github-actions'
|
||||||
|
directory: '/'
|
||||||
|
schedule:
|
||||||
|
day: 'sunday'
|
||||||
|
interval: 'weekly'
|
||||||
|
groups:
|
||||||
|
github-actions:
|
||||||
|
patterns:
|
||||||
|
- '*'
|
||||||
|
|
||||||
|
- package-ecosystem: 'gomod'
|
||||||
|
directory: '/'
|
||||||
|
schedule:
|
||||||
|
day: 'sunday'
|
||||||
|
interval: 'weekly'
|
||||||
|
groups:
|
||||||
|
gomod-security:
|
||||||
|
applies-to: security-updates
|
||||||
|
patterns: ['*']
|
||||||
|
gomod-update:
|
||||||
|
applies-to: version-updates
|
||||||
|
patterns: ['*']
|
||||||
29
.github/workflows/release.yml
vendored
Normal file
29
.github/workflows/release.yml
vendored
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
name: Releases Tag
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- v*.*.*
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
release:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
- uses: goreleaser/goreleaser-action@v6
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
args: release --clean
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
38
.github/workflows/test.yml
vendored
Normal file
38
.github/workflows/test.yml
vendored
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
name: Test Branch
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches: [ main ]
|
||||||
|
pull_request:
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
concurrency:
|
||||||
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
test:
|
||||||
|
name: test
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: actions/setup-go@v5
|
||||||
|
with:
|
||||||
|
go-version-file: go.mod
|
||||||
|
|
||||||
|
- uses: gotesttools/gotestfmt-action@v2
|
||||||
|
with:
|
||||||
|
token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- uses: golangci/golangci-lint-action@v6
|
||||||
|
with:
|
||||||
|
version: latest
|
||||||
|
|
||||||
|
- run: make test
|
||||||
|
|
||||||
|
# TODO enable once we have tests
|
||||||
|
# - uses: coverallsapp/github-action@v2
|
||||||
|
# with:
|
||||||
|
# file: coverage.out
|
||||||
|
|
||||||
25
.gitignore
vendored
Normal file
25
.gitignore
vendored
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
.*
|
||||||
|
*.zip
|
||||||
|
*.tar
|
||||||
|
*.out
|
||||||
|
*.log
|
||||||
|
/bin/
|
||||||
|
/tmp/
|
||||||
|
|
||||||
|
## Git
|
||||||
|
!.gitkeep
|
||||||
|
!.gitignore
|
||||||
|
|
||||||
|
## GitHub
|
||||||
|
!.github/
|
||||||
|
|
||||||
|
## Editorconfig
|
||||||
|
!.editorconfig
|
||||||
|
|
||||||
|
## Husky
|
||||||
|
!.husky/
|
||||||
|
!.husky.yaml
|
||||||
|
|
||||||
|
## Golang
|
||||||
|
!.golangci.yml
|
||||||
|
!.goreleaser.yml
|
||||||
186
.golangci.yml
Normal file
186
.golangci.yml
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
run:
|
||||||
|
timeout: 5m
|
||||||
|
|
||||||
|
issues:
|
||||||
|
exclude-dirs:
|
||||||
|
- 'tmp'
|
||||||
|
|
||||||
|
linters-settings:
|
||||||
|
# https://golangci-lint.run/usage/linters/#misspell
|
||||||
|
misspell:
|
||||||
|
mode: restricted
|
||||||
|
# https://golangci-lint.run/usage/linters/#asasalint
|
||||||
|
asasalint:
|
||||||
|
ignore-test: true
|
||||||
|
# https://golangci-lint.run/usage/linters/#exhaustive
|
||||||
|
exhaustive:
|
||||||
|
default-signifies-exhaustive: true
|
||||||
|
# https://golangci-lint.run/usage/linters/#predeclared
|
||||||
|
predeclared:
|
||||||
|
ignore: "new,error"
|
||||||
|
# https://golangci-lint.run/usage/linters/#gocritic
|
||||||
|
gocritic:
|
||||||
|
disabled-checks:
|
||||||
|
- ifElseChain
|
||||||
|
- commentFormatting
|
||||||
|
# https://golangci-lint.run/usage/linters/#testifylint
|
||||||
|
testifylint:
|
||||||
|
disable:
|
||||||
|
- float-compare
|
||||||
|
# https://golangci-lint.run/usage/linters/#gosec
|
||||||
|
gosec:
|
||||||
|
confidence: medium
|
||||||
|
# https://golangci-lint.run/usage/linters/#importas
|
||||||
|
importas:
|
||||||
|
no-unaliased: true
|
||||||
|
# https://golangci-lint.run/usage/linters/#gomoddirectives
|
||||||
|
gomoddirectives:
|
||||||
|
replace-local: true
|
||||||
|
# https://golangci-lint.run/usage/linters/#revive
|
||||||
|
revive:
|
||||||
|
ignore-generated-header: true
|
||||||
|
enable-all-rules: true
|
||||||
|
rules:
|
||||||
|
- name: line-length-limit
|
||||||
|
disabled: true
|
||||||
|
- name: cognitive-complexity
|
||||||
|
disabled: true
|
||||||
|
- name: unused-parameter
|
||||||
|
disabled: true
|
||||||
|
- name: add-constant
|
||||||
|
disabled: true
|
||||||
|
- name: cyclomatic
|
||||||
|
disabled: true
|
||||||
|
- name: function-length
|
||||||
|
disabled: true
|
||||||
|
- name: function-result-limit
|
||||||
|
disabled: true
|
||||||
|
- name: flag-parameter
|
||||||
|
disabled: true
|
||||||
|
- name: unused-receiver
|
||||||
|
disabled: true
|
||||||
|
- name: argument-limit
|
||||||
|
disabled: true
|
||||||
|
- name: max-control-nesting
|
||||||
|
disabled: true
|
||||||
|
- name: comment-spacings
|
||||||
|
disabled: true
|
||||||
|
- name: struct-tag
|
||||||
|
arguments:
|
||||||
|
- "json,inline"
|
||||||
|
- "yaml,squash"
|
||||||
|
- name: unhandled-error
|
||||||
|
arguments:
|
||||||
|
- "fmt.Println"
|
||||||
|
|
||||||
|
linters:
|
||||||
|
disable-all: true
|
||||||
|
enable:
|
||||||
|
## Enabled by default linters:
|
||||||
|
- errcheck # errcheck is a program for checking for unchecked errors in Go code. These unchecked errors can be critical bugs in some cases [fast: false, auto-fix: false]
|
||||||
|
- gosimple # (megacheck) Linter for Go source code that specializes in simplifying code [fast: false, auto-fix: false]
|
||||||
|
- govet # (vet, vetshadow) Vet examines Go source code and reports suspicious constructs. It is roughly the same as 'go vet' and uses its passes. [fast: false, auto-fix: false]
|
||||||
|
- ineffassign # Detects when assignments to existing variables are not used [fast: true, auto-fix: false]
|
||||||
|
- staticcheck # (megacheck) It's a set of rules from staticcheck. It's not the same thing as the staticcheck binary. The author of staticcheck doesn't support or approve the use of staticcheck as a library inside golangci-lint. [fast: false, auto-fix: false]
|
||||||
|
- unused # (megacheck) Checks Go code for unused constants, variables, functions and types [fast: false, auto-fix: false]
|
||||||
|
|
||||||
|
## Disabled by your configuration linters:
|
||||||
|
- asasalint # check for pass []any as any in variadic func(...any) [fast: false, auto-fix: false]
|
||||||
|
- asciicheck # checks that all code identifiers does not have non-ASCII symbols in the name [fast: true, auto-fix: false]
|
||||||
|
- bidichk # Checks for dangerous unicode character sequences [fast: true, auto-fix: false]
|
||||||
|
- bodyclose # checks whether HTTP response body is closed successfully [fast: false, auto-fix: false]
|
||||||
|
#- containedctx # containedctx is a linter that detects struct contained context.Context field [fast: false, auto-fix: false]
|
||||||
|
- contextcheck # check whether the function uses a non-inherited context [fast: false, auto-fix: false]
|
||||||
|
- copyloopvar # (go >= 1.22) copyloopvar is a linter detects places where loop variables are copied [fast: true, auto-fix: false]
|
||||||
|
- decorder # check declaration order and count of types, constants, variables and functions [fast: true, auto-fix: false]
|
||||||
|
- durationcheck # check for two durations multiplied together [fast: false, auto-fix: false]
|
||||||
|
- errchkjson # Checks types passed to the json encoding functions. Reports unsupported types and reports occations, where the check for the returned error can be omitted. [fast: false, auto-fix: false]
|
||||||
|
- errname # Checks that sentinel errors are prefixed with the `Err` and error types are suffixed with the `Error`. [fast: false, auto-fix: false]
|
||||||
|
- errorlint # errorlint is a linter for that can be used to find code that will cause problems with the error wrapping scheme introduced in Go 1.13. [fast: false, auto-fix: false]
|
||||||
|
- exhaustive # check exhaustiveness of enum switch statements [fast: false, auto-fix: false]
|
||||||
|
- exportloopref # checks for pointers to enclosing loop variables [fast: false, auto-fix: false]
|
||||||
|
- forbidigo # Forbids identifiers [fast: false, auto-fix: false]
|
||||||
|
- forcetypeassert # finds forced type assertions [fast: true, auto-fix: false]
|
||||||
|
- gocheckcompilerdirectives # Checks that go compiler directive comments (//go:) are valid. [fast: true, auto-fix: false]
|
||||||
|
- gochecksumtype # Run exhaustiveness checks on Go "sum types" [fast: false, auto-fix: false]
|
||||||
|
#- goconst # Finds repeated strings that could be replaced by a constant [fast: true, auto-fix: false]
|
||||||
|
- gocritic # Provides diagnostics that check for bugs, performance and style issues. [fast: false, auto-fix: true]
|
||||||
|
- gofmt # Gofmt checks whether code was gofmt-ed. By default this tool runs with -s option to check for code simplification [fast: true, auto-fix: true]
|
||||||
|
- goheader # Checks is file header matches to pattern [fast: true, auto-fix: true]
|
||||||
|
- goimports # Check import statements are formatted according to the 'goimport' command. Reformat imports in autofix mode. [fast: true, auto-fix: true]
|
||||||
|
- gomoddirectives # Manage the use of 'replace', 'retract', and 'excludes' directives in go.mod. [fast: true, auto-fix: false]
|
||||||
|
- gomodguard # Allow and block list linter for direct Go module dependencies. This is different from depguard where there are different block types for example version constraints and module recommendations. [fast: true, auto-fix: false]
|
||||||
|
- goprintffuncname # Checks that printf-like functions are named with `f` at the end. [fast: true, auto-fix: false]
|
||||||
|
- gosec # (gas) Inspects source code for security problems [fast: false, auto-fix: false]
|
||||||
|
- gosmopolitan # Report certain i18n/l10n anti-patterns in your Go codebase [fast: false, auto-fix: false]
|
||||||
|
- grouper # Analyze expression groups. [fast: true, auto-fix: false]
|
||||||
|
- importas # Enforces consistent import aliases [fast: false, auto-fix: false]
|
||||||
|
- inamedparam # reports interfaces with unnamed method parameters [fast: true, auto-fix: false]
|
||||||
|
- intrange # (go >= 1.22) intrange is a linter to find places where for loops could make use of an integer range. [fast: true, auto-fix: false]
|
||||||
|
- loggercheck # (logrlint) Checks key value pairs for common logger libraries (kitlog,klog,logr,zap). [fast: false, auto-fix: false]
|
||||||
|
- makezero # Finds slice declarations with non-zero initial length [fast: false, auto-fix: false]
|
||||||
|
- misspell # Finds commonly misspelled English words [fast: true, auto-fix: true]
|
||||||
|
- mirror # reports wrong mirror patterns of bytes/strings usage [fast: false, auto-fix: true]
|
||||||
|
- musttag # enforce field tags in (un)marshaled structs [fast: false, auto-fix: false]
|
||||||
|
- nakedret # Checks that functions with naked returns are not longer than a maximum size (can be zero). [fast: true, auto-fix: false]
|
||||||
|
- nilerr # Finds the code that returns nil even if it checks that the error is not nil. [fast: false, auto-fix: false]
|
||||||
|
- nilnil # Checks that there is no simultaneous return of `nil` error and an invalid value. [fast: false, auto-fix: false]
|
||||||
|
- noctx # Finds sending http request without context.Context [fast: false, auto-fix: false]
|
||||||
|
- nolintlint # Reports ill-formed or insufficient nolint directives [fast: true, auto-fix: true]
|
||||||
|
- nonamedreturns # Reports all named returns [fast: false, auto-fix: false]
|
||||||
|
- nosprintfhostport # Checks for misuse of Sprintf to construct a host with port in a URL. [fast: true, auto-fix: false]
|
||||||
|
- paralleltest # Detects missing usage of t.Parallel() method in your Go test [fast: false, auto-fix: false]
|
||||||
|
- predeclared # find code that shadows one of Go's predeclared identifiers [fast: true, auto-fix: false]
|
||||||
|
- promlinter # Check Prometheus metrics naming via promlint [fast: true, auto-fix: false]
|
||||||
|
- reassign # Checks that package variables are not reassigned [fast: false, auto-fix: false]
|
||||||
|
- revive # Fast, configurable, extensible, flexible, and beautiful linter for Go. Drop-in replacement of golint. [fast: false, auto-fix: false]
|
||||||
|
- rowserrcheck # checks whether Rows.Err of rows is checked successfully [fast: false, auto-fix: false]
|
||||||
|
- spancheck # Checks for mistakes with OpenTelemetry/Census spans. [fast: false, auto-fix: false]
|
||||||
|
- sqlclosecheck # Checks that sql.Rows, sql.Stmt, sqlx.NamedStmt, pgx.Query are closed. [fast: false, auto-fix: false]
|
||||||
|
- stylecheck # Stylecheck is a replacement for golint [fast: false, auto-fix: false]
|
||||||
|
- tenv # tenv is analyzer that detects using os.Setenv instead of t.Setenv since Go1.17 [fast: false, auto-fix: false]
|
||||||
|
- testableexamples # linter checks if examples are testable (have an expected output) [fast: true, auto-fix: false]
|
||||||
|
- testifylint # Checks usage of github.com/stretchr/testify. [fast: false, auto-fix: false]
|
||||||
|
- testpackage # linter that makes you use a separate _test package [fast: true, auto-fix: false]
|
||||||
|
- thelper # thelper detects tests helpers which is not start with t.Helper() method. [fast: false, auto-fix: false]
|
||||||
|
- tparallel # tparallel detects inappropriate usage of t.Parallel() method in your Go test codes. [fast: false, auto-fix: false]
|
||||||
|
- unconvert # Remove unnecessary type conversions [fast: false, auto-fix: false]
|
||||||
|
- usestdlibvars # A linter that detect the possibility to use variables/constants from the Go standard library. [fast: true, auto-fix: false]
|
||||||
|
- wastedassign # Finds wasted assignment statements [fast: false, auto-fix: false]
|
||||||
|
- whitespace # Whitespace is a linter that checks for unnecessary newlines at the start and end of functions, if, for, etc. [fast: true, auto-fix: true]
|
||||||
|
|
||||||
|
## Don't enable
|
||||||
|
#- cyclop # checks function and package cyclomatic complexity [fast: false, auto-fix: false]
|
||||||
|
#- depguard # Go linter that checks if package imports are in a list of acceptable packages [fast: true, auto-fix: false]
|
||||||
|
#- dupl # Tool for code clone detection [fast: true, auto-fix: false]
|
||||||
|
#- dupword # checks for duplicate words in the source code [fast: true, auto-fix: false]
|
||||||
|
#- dogsled # Checks assignments with too many blank identifiers (e.g. x, _, _, _, := f()) [fast: true, auto-fix: false]
|
||||||
|
#- err113 # Go linter to check the errors handling expressions [fast: false, auto-fix: false]
|
||||||
|
#- exhaustruct # Checks if all structure fields are initialized [fast: false, auto-fix: false]
|
||||||
|
#- gci # Gci controls Go package import order and makes it always deterministic. [fast: true, auto-fix: true]
|
||||||
|
#- gochecknoglobals # Check that no global variables exist. [fast: false, auto-fix: false]
|
||||||
|
#- gochecknoinits # Checks that no init functions are present in Go code [fast: true, auto-fix: false]
|
||||||
|
#- gocyclo # Computes and checks the cyclomatic complexity of functions [fast: true, auto-fix: false]
|
||||||
|
#- godot # Check if comments end in a period [fast: true, auto-fix: true]
|
||||||
|
#- godox # Tool for detection of FIXME, TODO and other comment keywords [fast: true, auto-fix: false]
|
||||||
|
#- gofumpt # Gofumpt checks whether code was gofumpt-ed. [fast: true, auto-fix: true]
|
||||||
|
#- funlen # Tool for detection of long functions [fast: true, auto-fix: false]
|
||||||
|
#- ginkgolinter # enforces standards of using ginkgo and gomega [fast: false, auto-fix: false]
|
||||||
|
#- gocognit # Computes and checks the cognitive complexity of functions [fast: true, auto-fix: false]
|
||||||
|
#- interfacebloat # A linter that checks the number of methods inside an interface. [fast: true, auto-fix: false]
|
||||||
|
#- lll # Reports long lines [fast: true, auto-fix: false]
|
||||||
|
#- ireturn # Accept Interfaces, Return Concrete Types [fast: false, auto-fix: false]
|
||||||
|
#- maintidx # maintidx measures the maintainability index of each function. [fast: true, auto-fix: false]
|
||||||
|
#- nestif # Reports deeply nested if statements [fast: true, auto-fix: false]
|
||||||
|
#- nlreturn # nlreturn checks for a new line before return and branch statements to increase code clarity [fast: true, auto-fix: false]
|
||||||
|
#- perfsprint # Checks that fmt.Sprintf can be replaced with a faster alternative. [fast: false, auto-fix: false]
|
||||||
|
#- prealloc # Finds slice declarations that could potentially be pre-allocated [fast: true, auto-fix: false]
|
||||||
|
#- protogetter # Reports direct reads from proto message fields when getters should be used [fast: false, auto-fix: true]
|
||||||
|
#- sloglint # ensure consistent code style when using log/slog [fast: false, auto-fix: false]
|
||||||
|
#- tagalign # check that struct tags are well aligned [fast: true, auto-fix: true]
|
||||||
|
#- tagliatelle # Checks the struct tags. [fast: true, auto-fix: false]
|
||||||
|
#- unparam # Reports unused function parameters [fast: false, auto-fix: false]
|
||||||
|
#- varnamelen # checks that the length of a variable's name matches its scope [fast: false, auto-fix: false]
|
||||||
|
#- wrapcheck # Checks that errors returned from external packages are wrapped [fast: false, auto-fix: false]
|
||||||
|
#- wsl # add or remove empty lines [fast: true, auto-fix: false]
|
||||||
|
#- zerologlint # Detects the wrong usage of `zerolog` that a user forgets to dispatch with `Send` or `Msg` [fast: false, auto-fix: false]
|
||||||
10
.goreleaser.yml
Normal file
10
.goreleaser.yml
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
version: 2
|
||||||
|
|
||||||
|
builds:
|
||||||
|
- skip: true
|
||||||
|
|
||||||
|
release:
|
||||||
|
prerelease: auto
|
||||||
|
|
||||||
|
changelog:
|
||||||
|
use: github-native
|
||||||
15
.husky.yaml
Normal file
15
.husky.yaml
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
hooks:
|
||||||
|
pre-commit:
|
||||||
|
- golangci-lint run --fast
|
||||||
|
- husky lint-staged
|
||||||
|
commit-msg:
|
||||||
|
# only execute if not in a merge
|
||||||
|
- if [[ -z $(git rev-parse -q --verify MERGE_HEAD) ]]; then husky lint-commit; fi
|
||||||
|
|
||||||
|
lint-staged:
|
||||||
|
'*.go':
|
||||||
|
- goimports -l -w
|
||||||
|
|
||||||
|
lint-commit:
|
||||||
|
types: '^(feat|fix|build|chore|docs|perf|refactor|revert|style|test|wip)$'
|
||||||
|
header: '^(?P<type>\w+)(\((?P<scope>[\w/.-]+)\))?(?P<breaking>!)?:( +)?(?P<header>.+)'
|
||||||
128
CODE_OF_CONDUCT.md
Normal file
128
CODE_OF_CONDUCT.md
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
# Contributor Covenant Code of Conduct
|
||||||
|
|
||||||
|
## Our Pledge
|
||||||
|
|
||||||
|
We as members, contributors, and leaders pledge to make participation in our
|
||||||
|
community a harassment-free experience for everyone, regardless of age, body
|
||||||
|
size, visible or invisible disability, ethnicity, sex characteristics, gender
|
||||||
|
identity and expression, level of experience, education, socio-economic status,
|
||||||
|
nationality, personal appearance, race, religion, or sexual identity
|
||||||
|
and orientation.
|
||||||
|
|
||||||
|
We pledge to act and interact in ways that contribute to an open, welcoming,
|
||||||
|
diverse, inclusive, and healthy community.
|
||||||
|
|
||||||
|
## Our Standards
|
||||||
|
|
||||||
|
Examples of behavior that contributes to a positive environment for our
|
||||||
|
community include:
|
||||||
|
|
||||||
|
* Demonstrating empathy and kindness toward other people
|
||||||
|
* Being respectful of differing opinions, viewpoints, and experiences
|
||||||
|
* Giving and gracefully accepting constructive feedback
|
||||||
|
* Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
|
and learning from the experience
|
||||||
|
* Focusing on what is best not just for us as individuals, but for the
|
||||||
|
overall community
|
||||||
|
|
||||||
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
|
* The use of sexualized language or imagery, and sexual attention or
|
||||||
|
advances of any kind
|
||||||
|
* Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
|
* Public or private harassment
|
||||||
|
* Publishing others' private information, such as a physical or email
|
||||||
|
address, without their explicit permission
|
||||||
|
* Other conduct which could reasonably be considered inappropriate in a
|
||||||
|
professional setting
|
||||||
|
|
||||||
|
## Enforcement Responsibilities
|
||||||
|
|
||||||
|
Community leaders are responsible for clarifying and enforcing our standards of
|
||||||
|
acceptable behavior and will take appropriate and fair corrective action in
|
||||||
|
response to any behavior that they deem inappropriate, threatening, offensive,
|
||||||
|
or harmful.
|
||||||
|
|
||||||
|
Community leaders have the right and responsibility to remove, edit, or reject
|
||||||
|
comments, commits, code, wiki edits, issues, and other contributions that are
|
||||||
|
not aligned to this Code of Conduct, and will communicate reasons for moderation
|
||||||
|
decisions when appropriate.
|
||||||
|
|
||||||
|
## Scope
|
||||||
|
|
||||||
|
This Code of Conduct applies within all community spaces, and also applies when
|
||||||
|
an individual is officially representing the community in public spaces.
|
||||||
|
Examples of representing our community include using an official e-mail address,
|
||||||
|
posting via an official social media account, or acting as an appointed
|
||||||
|
representative at an online or offline event.
|
||||||
|
|
||||||
|
## Enforcement
|
||||||
|
|
||||||
|
Instances of abusive, harassing, or otherwise unacceptable behavior may be
|
||||||
|
reported to the community leaders responsible for enforcement at
|
||||||
|
info@bestbytes.de.
|
||||||
|
All complaints will be reviewed and investigated promptly and fairly.
|
||||||
|
|
||||||
|
All community leaders are obligated to respect the privacy and security of the
|
||||||
|
reporter of any incident.
|
||||||
|
|
||||||
|
## Enforcement Guidelines
|
||||||
|
|
||||||
|
Community leaders will follow these Community Impact Guidelines in determining
|
||||||
|
the consequences for any action they deem in violation of this Code of Conduct:
|
||||||
|
|
||||||
|
### 1. Correction
|
||||||
|
|
||||||
|
**Community Impact**: Use of inappropriate language or other behavior deemed
|
||||||
|
unprofessional or unwelcome in the community.
|
||||||
|
|
||||||
|
**Consequence**: A private, written warning from community leaders, providing
|
||||||
|
clarity around the nature of the violation and an explanation of why the
|
||||||
|
behavior was inappropriate. A public apology may be requested.
|
||||||
|
|
||||||
|
### 2. Warning
|
||||||
|
|
||||||
|
**Community Impact**: A violation through a single incident or series
|
||||||
|
of actions.
|
||||||
|
|
||||||
|
**Consequence**: A warning with consequences for continued behavior. No
|
||||||
|
interaction with the people involved, including unsolicited interaction with
|
||||||
|
those enforcing the Code of Conduct, for a specified period of time. This
|
||||||
|
includes avoiding interactions in community spaces as well as external channels
|
||||||
|
like social media. Violating these terms may lead to a temporary or
|
||||||
|
permanent ban.
|
||||||
|
|
||||||
|
### 3. Temporary Ban
|
||||||
|
|
||||||
|
**Community Impact**: A serious violation of community standards, including
|
||||||
|
sustained inappropriate behavior.
|
||||||
|
|
||||||
|
**Consequence**: A temporary ban from any sort of interaction or public
|
||||||
|
communication with the community for a specified period of time. No public or
|
||||||
|
private interaction with the people involved, including unsolicited interaction
|
||||||
|
with those enforcing the Code of Conduct, is allowed during this period.
|
||||||
|
Violating these terms may lead to a permanent ban.
|
||||||
|
|
||||||
|
### 4. Permanent Ban
|
||||||
|
|
||||||
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
|
the community.
|
||||||
|
|
||||||
|
## Attribution
|
||||||
|
|
||||||
|
This Code of Conduct is adapted from the [Contributor Covenant][homepage],
|
||||||
|
version 2.0, available at
|
||||||
|
https://www.contributor-covenant.org/version/2/0/code_of_conduct.html.
|
||||||
|
|
||||||
|
Community Impact Guidelines were inspired by [Mozilla's code of conduct
|
||||||
|
enforcement ladder](https://github.com/mozilla/diversity).
|
||||||
|
|
||||||
|
[homepage]: https://www.contributor-covenant.org
|
||||||
|
|
||||||
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
|
https://www.contributor-covenant.org/translations.
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) Foomo
|
||||||
|
|
||||||
|
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.
|
||||||
83
Makefile
Normal file
83
Makefile
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
.DEFAULT_GOAL:=help
|
||||||
|
-include .makerc
|
||||||
|
|
||||||
|
# --- Targets -----------------------------------------------------------------
|
||||||
|
|
||||||
|
# This allows us to accept extra arguments
|
||||||
|
%: .husky
|
||||||
|
@:
|
||||||
|
|
||||||
|
.PHONY: .husky
|
||||||
|
# Configure git hooks for husky
|
||||||
|
.husky:
|
||||||
|
@if ! command -v husky &> /dev/null; then \
|
||||||
|
echo "ERROR: missing executeable 'husky', please run:"; \
|
||||||
|
echo "\n$ go install github.com/go-courier/husky/cmd/husky@latest\n"; \
|
||||||
|
fi
|
||||||
|
@git config core.hooksPath .husky
|
||||||
|
|
||||||
|
## === Tasks ===
|
||||||
|
.PHONY: doc
|
||||||
|
## Open go docs
|
||||||
|
doc:
|
||||||
|
@open "http://localhost:6060/pkg/github.com/foomo/etcports/"
|
||||||
|
@godoc -http=localhost:6060 -play
|
||||||
|
|
||||||
|
.PHONY: test
|
||||||
|
## Run tests
|
||||||
|
test:
|
||||||
|
@GO_TEST_TAGS=-skip go test -coverprofile=coverage.out -race -json ./... | gotestfmt
|
||||||
|
|
||||||
|
.PHONY: lint
|
||||||
|
## Run linter
|
||||||
|
lint:
|
||||||
|
@golangci-lint run
|
||||||
|
|
||||||
|
.PHONY: lint.fix
|
||||||
|
## Fix lint violations
|
||||||
|
lint.fix:
|
||||||
|
@golangci-lint run --fix
|
||||||
|
|
||||||
|
.PHONY: tidy
|
||||||
|
## Run go mod tidy
|
||||||
|
tidy:
|
||||||
|
@go mod tidy
|
||||||
|
|
||||||
|
.PHONY: outdated
|
||||||
|
## Show outdated direct dependencies
|
||||||
|
outdated:
|
||||||
|
@go list -u -m -json all | go-mod-outdated -update -direct
|
||||||
|
|
||||||
|
## === Utils ===
|
||||||
|
|
||||||
|
## Show help text
|
||||||
|
help:
|
||||||
|
@awk '{ \
|
||||||
|
if ($$0 ~ /^.PHONY: [a-zA-Z\-\_0-9]+$$/) { \
|
||||||
|
helpCommand = substr($$0, index($$0, ":") + 2); \
|
||||||
|
if (helpMessage) { \
|
||||||
|
printf "\033[36m%-23s\033[0m %s\n", \
|
||||||
|
helpCommand, helpMessage; \
|
||||||
|
helpMessage = ""; \
|
||||||
|
} \
|
||||||
|
} else if ($$0 ~ /^[a-zA-Z\-\_0-9.]+:/) { \
|
||||||
|
helpCommand = substr($$0, 0, index($$0, ":")); \
|
||||||
|
if (helpMessage) { \
|
||||||
|
printf "\033[36m%-23s\033[0m %s\n", \
|
||||||
|
helpCommand, helpMessage"\n"; \
|
||||||
|
helpMessage = ""; \
|
||||||
|
} \
|
||||||
|
} else if ($$0 ~ /^##/) { \
|
||||||
|
if (helpMessage) { \
|
||||||
|
helpMessage = helpMessage"\n "substr($$0, 3); \
|
||||||
|
} else { \
|
||||||
|
helpMessage = substr($$0, 3); \
|
||||||
|
} \
|
||||||
|
} else { \
|
||||||
|
if (helpMessage) { \
|
||||||
|
print "\n "helpMessage"\n" \
|
||||||
|
} \
|
||||||
|
helpMessage = ""; \
|
||||||
|
} \
|
||||||
|
}' \
|
||||||
|
$(MAKEFILE_LIST)
|
||||||
69
README.md
Normal file
69
README.md
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
# gofuncy
|
||||||
|
|
||||||
|
[](https://github.com/foomo/gofuncy/actions/workflows/test.yml)
|
||||||
|
[](https://goreportcard.com/report/github.com/foomo/gofuncy)
|
||||||
|
[](https://godoc.org/github.com/foomo/gofuncy)
|
||||||
|
[](https://github.com/foomo/gofuncy/actions)
|
||||||
|
|
||||||
|
> Stop using `go func`, start using `gofuncy`!
|
||||||
|
|
||||||
|
- ctx as a first class citizen
|
||||||
|
- error return as a first class citizen
|
||||||
|
- optional: enable telemetry (metrics & traces)
|
||||||
|
- `gofuncy.routine.count` counter
|
||||||
|
- `gofuncy.routine.duration` histogram
|
||||||
|
- `gofuncy.channel.sent.count` counter
|
||||||
|
- `gofuncy.channel.sent.duration` histogram
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Environment variables:
|
||||||
|
|
||||||
|
- `OTEL_ENABLED`: enable telemetry
|
||||||
|
- `GOFUNCY_CHANNEL_VALUE_EVENTS_ENABLED`: creates a span event for every value sent into the channel
|
||||||
|
- `GOFUNCY_CHANNEL_VALUE_ATTRIBUTE_ENABLED`: adds the json dump of the data to the span event
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
From:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
go func() {
|
||||||
|
numbers, err := GenerateNumbers(5)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
To:
|
||||||
|
|
||||||
|
```go
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/foomo/gofuncy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
errChan := gofuncy.Go(func(ctx context.Context) error {
|
||||||
|
numbers, err := GenerateNumbers(5)
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
if err := <-errChan; err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## How to Contribute
|
||||||
|
|
||||||
|
Make a pull request...
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
Distributed under MIT License, please see license file within the code for more details.
|
||||||
3
_examples/01_bad/go.mod
Normal file
3
_examples/01_bad/go.mod
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module github.com/foomo/gofuncy/examples/01_bad
|
||||||
|
|
||||||
|
go 1.22
|
||||||
38
_examples/01_bad/main.go
Normal file
38
_examples/01_bad/main.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
msg := make(chan string, 10)
|
||||||
|
|
||||||
|
fmt.Println("start")
|
||||||
|
go send(msg)
|
||||||
|
go send(msg)
|
||||||
|
go send(msg)
|
||||||
|
|
||||||
|
go receive("a", msg)
|
||||||
|
receive("b", msg)
|
||||||
|
fmt.Println("done")
|
||||||
|
}
|
||||||
|
|
||||||
|
func send(msg chan string) {
|
||||||
|
var i int
|
||||||
|
for {
|
||||||
|
// fmt.Println("sending message")
|
||||||
|
msg <- fmt.Sprintf("Hello World #%d", i)
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func receive(n string, msg chan string) {
|
||||||
|
for m := range msg {
|
||||||
|
// fmt.Println(m)
|
||||||
|
fmt.Println(n, m, len(msg))
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
17
_examples/02_good/go.mod
Normal file
17
_examples/02_good/go.mod
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
module github.com/foomo/gofuncy/examples/02_good
|
||||||
|
|
||||||
|
go 1.22
|
||||||
|
|
||||||
|
replace github.com/foomo/gofuncy => ../../
|
||||||
|
|
||||||
|
require github.com/foomo/gofuncy v0.0.0-00010101000000-000000000000
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Ju0x/humanhash v1.0.2 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||||
|
)
|
||||||
25
_examples/02_good/go.sum
Normal file
25
_examples/02_good/go.sum
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
github.com/Ju0x/humanhash v1.0.2 h1:SfXEWvO+FQZF1lI6+/K0w939ys3DJ10ieFf3D4gQWfg=
|
||||||
|
github.com/Ju0x/humanhash v1.0.2/go.mod h1:ZPhyn4aMWJVyMkkGUz9auRHR/mY6DKBRpPgTh+en6hU=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||||
|
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
48
_examples/02_good/main.go
Normal file
48
_examples/02_good/main.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/foomo/gofuncy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
msg := make(chan string, 10)
|
||||||
|
|
||||||
|
fmt.Println("start")
|
||||||
|
|
||||||
|
_ = gofuncy.Go(func(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
_ = gofuncy.Go(send(msg))
|
||||||
|
_ = gofuncy.Go(send(msg), gofuncy.WithName("sender-b"))
|
||||||
|
_ = gofuncy.Go(send(msg), gofuncy.WithName("sender-c"))
|
||||||
|
|
||||||
|
_ = gofuncy.Go(receive(msg), gofuncy.WithName("receiver-a"))
|
||||||
|
_ = receive(msg)(context.Background())
|
||||||
|
fmt.Println("done")
|
||||||
|
}
|
||||||
|
|
||||||
|
func send(msg chan string) gofuncy.Func {
|
||||||
|
return func(ctx context.Context) error {
|
||||||
|
for {
|
||||||
|
// fmt.Println("sending message")
|
||||||
|
msg <- fmt.Sprintf("Hello World #%s", gofuncy.RoutineFromContext(ctx))
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func receive(msg chan string) gofuncy.Func {
|
||||||
|
return func(ctx context.Context) error {
|
||||||
|
for m := range msg {
|
||||||
|
fmt.Println(m, "by", gofuncy.RoutineFromContext(ctx))
|
||||||
|
// fmt.Println(m, len(msg))
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
17
_examples/03_better/go.mod
Normal file
17
_examples/03_better/go.mod
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
module github.com/foomo/gofuncy/examples/03_better
|
||||||
|
|
||||||
|
go 1.22
|
||||||
|
|
||||||
|
replace github.com/foomo/gofuncy => ../../
|
||||||
|
|
||||||
|
require github.com/foomo/gofuncy v0.0.0-00010101000000-000000000000
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Ju0x/humanhash v1.0.2 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
go.opentelemetry.io/otel v1.28.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||||
|
)
|
||||||
25
_examples/03_better/go.sum
Normal file
25
_examples/03_better/go.sum
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
github.com/Ju0x/humanhash v1.0.2 h1:SfXEWvO+FQZF1lI6+/K0w939ys3DJ10ieFf3D4gQWfg=
|
||||||
|
github.com/Ju0x/humanhash v1.0.2/go.mod h1:ZPhyn4aMWJVyMkkGUz9auRHR/mY6DKBRpPgTh+en6hU=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||||
|
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
48
_examples/03_better/main.go
Normal file
48
_examples/03_better/main.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/foomo/gofuncy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
msg := gofuncy.NewChannel[string](
|
||||||
|
gofuncy.ChannelWithBufferSize[string](10),
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println("start")
|
||||||
|
|
||||||
|
_ = gofuncy.Go(send(msg), gofuncy.WithName("sender-a"))
|
||||||
|
_ = gofuncy.Go(send(msg), gofuncy.WithName("sender-b"))
|
||||||
|
_ = gofuncy.Go(send(msg), gofuncy.WithName("sender-c"))
|
||||||
|
|
||||||
|
_ = gofuncy.Go(receive(msg), gofuncy.WithName("receiver-a"))
|
||||||
|
_ = receive(msg)(context.Background())
|
||||||
|
fmt.Println("done")
|
||||||
|
}
|
||||||
|
|
||||||
|
func send(msg *gofuncy.Channel[string]) gofuncy.Func {
|
||||||
|
return func(ctx context.Context) error {
|
||||||
|
for {
|
||||||
|
if err := msg.Send(ctx, fmt.Sprintf("Hello World")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func receive(msg *gofuncy.Channel[string]) gofuncy.Func {
|
||||||
|
return func(ctx context.Context) error {
|
||||||
|
for m := range msg.Receive() {
|
||||||
|
fmt.Println("Message:", m.Data, "Handler:", gofuncy.RoutineFromContext(ctx), "Sender:", gofuncy.SenderFromContext(m.Context()))
|
||||||
|
// fmt.Println(m, len(msg))
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
24
_examples/04_best/go.mod
Normal file
24
_examples/04_best/go.mod
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
module github.com/foomo/gofuncy/examples/04_best
|
||||||
|
|
||||||
|
go 1.22
|
||||||
|
|
||||||
|
replace github.com/foomo/gofuncy => ../../
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/foomo/gofuncy v0.0.0-00010101000000-000000000000
|
||||||
|
go.opentelemetry.io/otel v1.28.0
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0
|
||||||
|
go.opentelemetry.io/otel/sdk v1.28.0
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.28.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Ju0x/humanhash v1.0.2 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0 // indirect
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0 // indirect
|
||||||
|
golang.org/x/sys v0.21.0 // indirect
|
||||||
|
)
|
||||||
35
_examples/04_best/go.sum
Normal file
35
_examples/04_best/go.sum
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
github.com/Ju0x/humanhash v1.0.2 h1:SfXEWvO+FQZF1lI6+/K0w939ys3DJ10ieFf3D4gQWfg=
|
||||||
|
github.com/Ju0x/humanhash v1.0.2/go.mod h1:ZPhyn4aMWJVyMkkGUz9auRHR/mY6DKBRpPgTh+en6hU=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||||
|
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0 h1:BJee2iLkfRfl9lc7aFmBwkWxY/RI1RDdXepSF6y8TPE=
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.28.0/go.mod h1:DIzlHs3DRscCIBU3Y9YSzPfScwnYnzfnCd4g8zA7bZc=
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0 h1:EVSnY9JbEEW92bEkIYOVMw4q1WJxIAGoFTrtYOzWuRQ=
|
||||||
|
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.28.0/go.mod h1:Ea1N1QQryNXpCD0I1fdLibBAIpQuBkznMmkdKrapk1Y=
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.28.0 h1:OkuaKgKrgAbYrrY0t92c+cC+2F6hsFNnCQArXCKlg08=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.28.0/go.mod h1:cWPjykihLAPvXKi4iZc1dpER3Jdq2Z0YLse3moQUCpg=
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||||
|
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||||
|
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
102
_examples/04_best/main.go
Normal file
102
_examples/04_best/main.go
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/foomo/gofuncy"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/exporters/stdout/stdoutmetric"
|
||||||
|
"go.opentelemetry.io/otel/exporters/stdout/stdouttrace"
|
||||||
|
"go.opentelemetry.io/otel/sdk/metric"
|
||||||
|
"go.opentelemetry.io/otel/sdk/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
var meter *metric.MeterProvider
|
||||||
|
var tracer *trace.TracerProvider
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
{
|
||||||
|
exp, _ := stdoutmetric.New(
|
||||||
|
stdoutmetric.WithPrettyPrint(),
|
||||||
|
stdoutmetric.WithWriter(os.Stdout),
|
||||||
|
)
|
||||||
|
meter = metric.NewMeterProvider(
|
||||||
|
metric.WithReader(metric.NewPeriodicReader(exp)),
|
||||||
|
)
|
||||||
|
otel.SetMeterProvider(meter)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
exp, _ := stdouttrace.New(stdouttrace.WithPrettyPrint())
|
||||||
|
tracer = trace.NewTracerProvider(
|
||||||
|
trace.WithBatcher(exp),
|
||||||
|
)
|
||||||
|
otel.SetTracerProvider(tracer)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
exp, err := stdoutlog.New()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
processor := log.NewSimpleProcessor(exp)
|
||||||
|
provider := log.NewLoggerProvider(log.WithProcessor(processor))
|
||||||
|
defer func() {
|
||||||
|
if err := provider.Shutdown(context.Background()); err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
global.SetLoggerProvider(provider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
go func() {
|
||||||
|
time.Sleep(10 * time.Second)
|
||||||
|
// _ = meter.ForceFlush(context.Background())
|
||||||
|
_ = tracer.ForceFlush(context.Background())
|
||||||
|
os.Exit(0)
|
||||||
|
}()
|
||||||
|
|
||||||
|
msg := gofuncy.NewChannel[string](
|
||||||
|
// gofuncy.ChannelWithBufferSize[string](5),
|
||||||
|
gofuncy.ChannelWithTelemetryEnabled[string](true),
|
||||||
|
gofuncy.ChannelWithValueEventsEnabled[string](true),
|
||||||
|
gofuncy.ChannelWithValueAttributeEnabled[string](true),
|
||||||
|
)
|
||||||
|
|
||||||
|
fmt.Println("start")
|
||||||
|
|
||||||
|
_ = gofuncy.Go(send(msg), gofuncy.WithName("sender-a"), gofuncy.WithTelemetryEnabled(true))
|
||||||
|
_ = gofuncy.Go(send(msg), gofuncy.WithName("sender-b"), gofuncy.WithTelemetryEnabled(true))
|
||||||
|
_ = gofuncy.Go(send(msg), gofuncy.WithName("sender-c"), gofuncy.WithTelemetryEnabled(true))
|
||||||
|
|
||||||
|
_ = gofuncy.Go(receive(msg), gofuncy.WithName("receiver-a"))
|
||||||
|
_ = receive(msg)(context.Background())
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func send(msg *gofuncy.Channel[string]) gofuncy.Func {
|
||||||
|
return func(ctx context.Context) error {
|
||||||
|
for {
|
||||||
|
if err := msg.Send(ctx, fmt.Sprintf("Hello World")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
time.Sleep(300 * time.Millisecond)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func receive(msg *gofuncy.Channel[string]) gofuncy.Func {
|
||||||
|
return func(ctx context.Context) error {
|
||||||
|
for m := range msg.Receive() {
|
||||||
|
fmt.Println("Message:", m.Data, "Handler:", gofuncy.RoutineFromContext(ctx), "Sender:", gofuncy.SenderFromContext(m.Context()))
|
||||||
|
// fmt.Println(m, len(msg))
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
221
channel.go
Normal file
221
channel.go
Normal file
@ -0,0 +1,221 @@
|
|||||||
|
package gofuncy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/attribute"
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
"go.opentelemetry.io/otel/trace"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Channel[T any] struct {
|
||||||
|
l *slog.Logger
|
||||||
|
// channel
|
||||||
|
channel chan *Value[T]
|
||||||
|
bufferSize int
|
||||||
|
// telemetry
|
||||||
|
meter metric.Meter
|
||||||
|
tracer trace.Tracer
|
||||||
|
counter metric.Int64Counter
|
||||||
|
counterName string
|
||||||
|
histogram metric.Int64Histogram
|
||||||
|
histogramName string
|
||||||
|
valueEventsEnabled bool
|
||||||
|
valueAttributeEnabled bool
|
||||||
|
telemetryEnabled bool
|
||||||
|
// closing
|
||||||
|
isClosed atomic.Bool
|
||||||
|
closing chan struct{}
|
||||||
|
// closed chan struct{}
|
||||||
|
}
|
||||||
|
ChannelOption[T any] func(channel *Channel[T])
|
||||||
|
)
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// ~ Options
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func ChannelWithLogger[T any](logger *slog.Logger) ChannelOption[T] {
|
||||||
|
return func(channel *Channel[T]) {
|
||||||
|
channel.l = logger
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChannelWithBufferSize[T any](size int) ChannelOption[T] {
|
||||||
|
return func(channel *Channel[T]) {
|
||||||
|
channel.bufferSize = size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChannelWithTelemetryEnabled[T any](enabled bool) ChannelOption[T] {
|
||||||
|
return func(channel *Channel[T]) {
|
||||||
|
channel.telemetryEnabled = enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChannelWithMeter[T any](meter metric.Meter) ChannelOption[T] {
|
||||||
|
return func(channel *Channel[T]) {
|
||||||
|
channel.meter = meter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChannelWithCounterName[T any](name string) ChannelOption[T] {
|
||||||
|
return func(channel *Channel[T]) {
|
||||||
|
channel.counterName = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChannelWithHistogramName(name string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.histogramName = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChannelWithTracer[T any](tracer trace.Tracer) ChannelOption[T] {
|
||||||
|
return func(channel *Channel[T]) {
|
||||||
|
channel.tracer = tracer
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChannelWithValueEventsEnabled[T any](enabled bool) ChannelOption[T] {
|
||||||
|
return func(channel *Channel[T]) {
|
||||||
|
channel.valueEventsEnabled = enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChannelWithValueAttributeEnabled[T any](enabled bool) ChannelOption[T] {
|
||||||
|
return func(channel *Channel[T]) {
|
||||||
|
channel.valueAttributeEnabled = enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// ~ Constructor
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func NewChannel[T any](opts ...ChannelOption[T]) *Channel[T] {
|
||||||
|
inst := &Channel[T]{
|
||||||
|
l: slog.Default(),
|
||||||
|
bufferSize: 0,
|
||||||
|
counterName: "gofuncy.channel.sent.count",
|
||||||
|
histogramName: "gofuncy.channel.sent.duration",
|
||||||
|
telemetryEnabled: os.Getenv("OTEL_ENABLED") == "true",
|
||||||
|
valueEventsEnabled: os.Getenv("GOFUNCY_CHANNEL_VALUE_EVENTS_ENABLED") == "true",
|
||||||
|
valueAttributeEnabled: os.Getenv("GOFUNCY_CHANNEL_VALUE_ATTRIBUTE_ENABLED") == "true",
|
||||||
|
}
|
||||||
|
for _, opt := range opts {
|
||||||
|
if opt != nil {
|
||||||
|
opt(inst)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create channel
|
||||||
|
inst.channel = make(chan *Value[T], inst.bufferSize)
|
||||||
|
|
||||||
|
// create telemetry if enabled
|
||||||
|
if inst.telemetryEnabled {
|
||||||
|
if inst.meter == nil {
|
||||||
|
inst.meter = otel.Meter("gofuncy")
|
||||||
|
}
|
||||||
|
if value, err := inst.meter.Int64Counter(inst.counterName); err != nil {
|
||||||
|
inst.l.Error("failed to initialize counter", "error", err)
|
||||||
|
} else {
|
||||||
|
inst.counter = value
|
||||||
|
}
|
||||||
|
if value, err := inst.meter.Int64Histogram(inst.histogramName); err != nil {
|
||||||
|
inst.l.Error("failed to initialize histogram", "error", err)
|
||||||
|
} else {
|
||||||
|
inst.histogram = value
|
||||||
|
}
|
||||||
|
if inst.tracer == nil {
|
||||||
|
inst.tracer = otel.Tracer("gofuncy")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return inst
|
||||||
|
}
|
||||||
|
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
// ~ Public methods
|
||||||
|
// ------------------------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
func (g *Channel[T]) Receive() <-chan *Value[T] {
|
||||||
|
return g.channel
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *Channel[T]) Send(ctx context.Context, values ...T) error {
|
||||||
|
if g.isClosed.Load() {
|
||||||
|
return ErrChannelClosed
|
||||||
|
}
|
||||||
|
if g.histogram != nil {
|
||||||
|
start := time.Now()
|
||||||
|
defer func() {
|
||||||
|
g.histogram.Record(ctx, time.Since(start).Milliseconds())
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
var span trace.Span
|
||||||
|
if g.tracer != nil {
|
||||||
|
ctx, span = g.tracer.Start(ctx, "Send", trace.WithAttributes(
|
||||||
|
attribute.Int("num", len(values)),
|
||||||
|
attribute.Int("chan_cap", cap(g.channel)),
|
||||||
|
attribute.Int("chan_size", len(g.channel)),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
defer span.End()
|
||||||
|
|
||||||
|
newValue := func(ctx context.Context, span trace.Span, data T) *Value[T] {
|
||||||
|
ret := &Value[T]{
|
||||||
|
ctx: injectSenderIntoContext(ctx, RoutineFromContext(ctx)),
|
||||||
|
Data: data,
|
||||||
|
}
|
||||||
|
if g.counter != nil {
|
||||||
|
g.counter.Add(ctx, 1)
|
||||||
|
}
|
||||||
|
if g.tracer != nil {
|
||||||
|
if g.valueEventsEnabled {
|
||||||
|
var attrs []attribute.KeyValue
|
||||||
|
if g.valueAttributeEnabled {
|
||||||
|
if value, err := json.Marshal(data); err == nil {
|
||||||
|
attrs = append(attrs, attribute.String("value", string(value)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span.AddEvent("value", trace.WithAttributes(attrs...))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, data := range values {
|
||||||
|
select {
|
||||||
|
case <-g.closing:
|
||||||
|
return ErrChannelClosed
|
||||||
|
default:
|
||||||
|
}
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-g.closing:
|
||||||
|
return ErrChannelClosed
|
||||||
|
case g.channel <- newValue(ctx, span, data):
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Close closes the GoChannel Pub/Sub.
|
||||||
|
func (g *Channel[T]) Close() {
|
||||||
|
if g.isClosed.Load() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
g.isClosed.Store(true)
|
||||||
|
g.closing <- struct{}{}
|
||||||
|
close(g.channel)
|
||||||
|
}
|
||||||
46
context.go
Normal file
46
context.go
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
package gofuncy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type contextKey string
|
||||||
|
|
||||||
|
const (
|
||||||
|
contextKeyRoutine contextKey = "routine"
|
||||||
|
contextKeyParentRoutine contextKey = "parentRoutine"
|
||||||
|
contextKeySender contextKey = "sender"
|
||||||
|
)
|
||||||
|
|
||||||
|
func injectRoutineIntoContext(ctx context.Context, name string) context.Context {
|
||||||
|
return context.WithValue(ctx, contextKeyRoutine, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func RoutineFromContext(ctx context.Context) string {
|
||||||
|
if value, ok := ctx.Value(contextKeyRoutine).(string); ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return "noname"
|
||||||
|
}
|
||||||
|
|
||||||
|
func injectSenderIntoContext(ctx context.Context, name string) context.Context {
|
||||||
|
return context.WithValue(ctx, contextKeySender, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SenderFromContext(ctx context.Context) string {
|
||||||
|
if value, ok := ctx.Value(contextKeySender).(string); ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func injectParentRoutineIntoContext(ctx context.Context, name string) context.Context {
|
||||||
|
return context.WithValue(ctx, contextKeyParentRoutine, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParentRoutineFromContext(ctx context.Context) string {
|
||||||
|
if value, ok := ctx.Value(contextKeyParentRoutine).(string); ok {
|
||||||
|
return value
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
7
errors.go
Normal file
7
errors.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package gofuncy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
)
|
||||||
|
|
||||||
|
var ErrChannelClosed = errors.New("channel is closed")
|
||||||
7
func.go
Normal file
7
func.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package gofuncy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Func func(ctx context.Context) error
|
||||||
129
go.go
Normal file
129
go.go
Normal file
@ -0,0 +1,129 @@
|
|||||||
|
package gofuncy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"crypto/sha256"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"runtime"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/Ju0x/humanhash"
|
||||||
|
"go.opentelemetry.io/otel"
|
||||||
|
"go.opentelemetry.io/otel/metric"
|
||||||
|
semconv "go.opentelemetry.io/otel/semconv/v1.26.0"
|
||||||
|
)
|
||||||
|
|
||||||
|
type (
|
||||||
|
Options struct {
|
||||||
|
l *slog.Logger
|
||||||
|
ctx context.Context
|
||||||
|
name string
|
||||||
|
// telemetry
|
||||||
|
meter metric.Meter
|
||||||
|
counter metric.Int64UpDownCounter
|
||||||
|
counterName string
|
||||||
|
histogram metric.Int64Histogram
|
||||||
|
histogramName string
|
||||||
|
telemetryEnabled bool
|
||||||
|
}
|
||||||
|
Option func(*Options)
|
||||||
|
)
|
||||||
|
|
||||||
|
func WithName(name string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithContext(ctx context.Context) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.ctx = ctx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithTelemetryEnabled(enabled bool) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.telemetryEnabled = enabled
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithMeter(meter metric.Meter) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.meter = meter
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithCounterName(name string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.counterName = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithHistogramName(name string) Option {
|
||||||
|
return func(o *Options) {
|
||||||
|
o.histogramName = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Go(fn Func, opts ...Option) <-chan error {
|
||||||
|
o := &Options{
|
||||||
|
l: slog.Default(),
|
||||||
|
counterName: "gofuncy.routine.count",
|
||||||
|
histogramName: "gofuncy.routine.duration",
|
||||||
|
telemetryEnabled: os.Getenv("OTEL_ENABLED") == "true",
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, opt := range opts {
|
||||||
|
if opt != nil {
|
||||||
|
opt(o)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if o.ctx == nil {
|
||||||
|
o.ctx = context.Background()
|
||||||
|
}
|
||||||
|
if o.name == "" {
|
||||||
|
if _, file, line, ok := runtime.Caller(0); ok {
|
||||||
|
h := sha256.New()
|
||||||
|
_, _ = h.Write([]byte(fmt.Sprintf("%s:%d", file, line)))
|
||||||
|
o.name, _ = humanhash.Humanize(h.Sum(nil), 2, "-")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// create telemetry if enabled
|
||||||
|
if o.telemetryEnabled {
|
||||||
|
if o.meter == nil {
|
||||||
|
o.meter = otel.Meter("gofuncy")
|
||||||
|
}
|
||||||
|
if value, err := o.meter.Int64UpDownCounter(o.counterName); err != nil {
|
||||||
|
o.l.Error("failed to initialize gauge", "error", err)
|
||||||
|
} else {
|
||||||
|
o.counter = value
|
||||||
|
}
|
||||||
|
if value, err := o.meter.Int64Histogram(o.histogramName); err != nil {
|
||||||
|
o.l.Error("failed to initialize histogram", "error", err)
|
||||||
|
} else {
|
||||||
|
o.histogram = value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err := make(chan error)
|
||||||
|
go func(o *Options, err chan<- error) {
|
||||||
|
// create telemetry if enabled
|
||||||
|
if o.counter != nil {
|
||||||
|
o.counter.Add(o.ctx, 1, metric.WithAttributes(semconv.ProcessRuntimeName(o.name)))
|
||||||
|
defer o.counter.Add(o.ctx, -1, metric.WithAttributes(semconv.ProcessRuntimeName(o.name)))
|
||||||
|
}
|
||||||
|
if o.histogram != nil {
|
||||||
|
start := time.Now()
|
||||||
|
defer func() {
|
||||||
|
o.histogram.Record(o.ctx, time.Since(start).Milliseconds(), metric.WithAttributes(semconv.ProcessRuntimeName(o.name)))
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
ctx := injectParentRoutineIntoContext(o.ctx, RoutineFromContext(o.ctx))
|
||||||
|
ctx = injectRoutineIntoContext(ctx, o.name)
|
||||||
|
err <- fn(ctx)
|
||||||
|
}(o, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
20
go.mod
Normal file
20
go.mod
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
module github.com/foomo/gofuncy
|
||||||
|
|
||||||
|
go 1.22
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/Ju0x/humanhash v1.0.2
|
||||||
|
github.com/stretchr/testify v1.9.0
|
||||||
|
go.opentelemetry.io/otel v1.28.0
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||||
|
github.com/go-logr/logr v1.4.2 // indirect
|
||||||
|
github.com/go-logr/stdr v1.2.2 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
|
)
|
||||||
27
go.sum
Normal file
27
go.sum
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
github.com/Ju0x/humanhash v1.0.2 h1:SfXEWvO+FQZF1lI6+/K0w939ys3DJ10ieFf3D4gQWfg=
|
||||||
|
github.com/Ju0x/humanhash v1.0.2/go.mod h1:ZPhyn4aMWJVyMkkGUz9auRHR/mY6DKBRpPgTh+en6hU=
|
||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||||
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo=
|
||||||
|
go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4=
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q=
|
||||||
|
go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s=
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g=
|
||||||
|
go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||||
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
49
go_test.go
Normal file
49
go_test.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
package gofuncy_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/foomo/gofuncy"
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestGo(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
operation := func(ctx context.Context) error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
errChan := gofuncy.Go(operation)
|
||||||
|
assert.NoError(t, <-errChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGoError(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
err := errors.New("error")
|
||||||
|
operation := func(ctx context.Context) error {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
errChan := gofuncy.Go(operation)
|
||||||
|
assert.ErrorIs(t, err, <-errChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGo_WithContext(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
operation := func(ctx context.Context) error {
|
||||||
|
assert.Equal(t, "value", ctx.Value("key"))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
errChan := gofuncy.Go(operation, gofuncy.WithContext(context.WithValue(context.Background(), "key", "value")))
|
||||||
|
assert.NoError(t, <-errChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestGo_WithName(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
operation := func(ctx context.Context) error {
|
||||||
|
assert.Equal(t, "gofuncy", gofuncy.RoutineFromContext(ctx))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
errChan := gofuncy.Go(operation, gofuncy.WithName("gofuncy"))
|
||||||
|
assert.NoError(t, <-errChan)
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user