From 562e6633b31ef3aaf75806322bfff45c1d7eebdc Mon Sep 17 00:00:00 2001 From: Konstantin Grachev Date: Thu, 15 Feb 2024 19:02:01 +0300 Subject: [PATCH] merge-channel: init --- merge-channel/Makefile | 53 ++++++++++++++++++++++++++++++++++++++ merge-channel/go.mod | 11 ++++++++ merge-channel/go.sum | 10 +++++++ merge-channel/main.go | 38 +++++++++++++++++++++++++++ merge-channel/main_test.go | 36 ++++++++++++++++++++++++++ 5 files changed, 148 insertions(+) create mode 100644 merge-channel/Makefile create mode 100644 merge-channel/go.mod create mode 100644 merge-channel/go.sum create mode 100644 merge-channel/main.go create mode 100644 merge-channel/main_test.go diff --git a/merge-channel/Makefile b/merge-channel/Makefile new file mode 100644 index 0000000..977c1c9 --- /dev/null +++ b/merge-channel/Makefile @@ -0,0 +1,53 @@ +.DEFAULT_GOAL := help + +# ==================================================================================== # +# HELPERS +# ==================================================================================== # + +## help: print this help message +.PHONY: help +help: + @echo 'Usage:' + @sed -n 's/^##//p' ${MAKEFILE_LIST} | column -t -s ':' | sed -e 's/^/ /' + +## all: tidy + audit + test/cover +all: generate tidy test lint test/cover + + +# ==================================================================================== # +# QUALITY CONTROL +# ==================================================================================== # + +## tidy: format code and tidy modfile +.PHONY: tidy +tidy: + go fmt ./... + goimports -local=$(shell cat go.mod | grep module | awk '{print $$2}')/ -w . + go mod tidy -v + +## audit: run quality control checks +.PHONY: lint +lint: + go mod verify + go run github.com/golangci/golangci-lint/cmd/golangci-lint@latest run -v ./... + + +# ==================================================================================== # +# DEVELOPMENT +# ==================================================================================== # + +## generate: run all generators +.PHONY: generate +generate: + go generate ./... + +## test: run all tests +.PHONY: test +test: + go test -race -buildvcs ./... + +## test/cover: run all tests and display coverage +.PHONY: test/cover +test/cover: + go test -race -buildvcs -coverprofile=/tmp/coverage.out ./... + go tool cover -html=/tmp/coverage.out diff --git a/merge-channel/go.mod b/merge-channel/go.mod new file mode 100644 index 0000000..3147e31 --- /dev/null +++ b/merge-channel/go.mod @@ -0,0 +1,11 @@ +module merge-channel + +go 1.21.6 + +require github.com/stretchr/testify v1.8.4 + +require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/merge-channel/go.sum b/merge-channel/go.sum new file mode 100644 index 0000000..fa4b6e6 --- /dev/null +++ b/merge-channel/go.sum @@ -0,0 +1,10 @@ +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/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.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +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= diff --git a/merge-channel/main.go b/merge-channel/main.go new file mode 100644 index 0000000..6a581c5 --- /dev/null +++ b/merge-channel/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "errors" + "sync" +) + +var InsufficientChannelsErr = errors.New("channels count must be >2") + +func merge[T any](channels ...<-chan T) (<-chan T, error) { + out := make(chan T) + + if len(channels) < 2 { + close(out) + + return out, InsufficientChannelsErr + } + + var wg sync.WaitGroup + wg.Add(len(channels)) + + for _, c := range channels { + go func(channel <-chan T) { + defer wg.Done() + + for v := range channel { + out <- v + } + }(c) + } + + go func() { + wg.Wait() + close(out) + }() + + return out, nil +} diff --git a/merge-channel/main_test.go b/merge-channel/main_test.go new file mode 100644 index 0000000..622c4c4 --- /dev/null +++ b/merge-channel/main_test.go @@ -0,0 +1,36 @@ +package main + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestMerge(t *testing.T) { + a := make(chan int, 5) + b := make(chan int, 5) + + for i := 0; i < 5; i++ { + a <- i + b <- i + } + + merged, err := merge(a, b) + assert.Nil(t, err) + + close(a) + close(b) + + sum := 0 + for v := range merged { + sum += v + } + assert.Equal(t, sum, 20) +} + +func TestInsufficient(t *testing.T) { + a := make(chan int) + + _, err := merge(a) + assert.Equal(t, err, InsufficientChannelsErr) +}