pax_global_header00006660000000000000000000000064143355523120014515gustar00rootroot0000000000000052 comment=6768b02747a809252563b620fcd19321634a612d plumbing-1.3.0/000077500000000000000000000000001433555231200133335ustar00rootroot00000000000000plumbing-1.3.0/.github/000077500000000000000000000000001433555231200146735ustar00rootroot00000000000000plumbing-1.3.0/.github/dependabot.yml000066400000000000000000000003151433555231200175220ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "daily" - package-ecosystem: "gomod" directory: "/" schedule: interval: "daily" plumbing-1.3.0/.github/release.yml000066400000000000000000000005721433555231200170420ustar00rootroot00000000000000changelog: exclude: labels: - ignore-for-release categories: - title: Breaking Changes labels: - breaking-change - title: New Features labels: - enhancement - title: Bug Fixes labels: - bug - title: Documentation labels: - documentation - title: Other Changes labels: - "*" plumbing-1.3.0/.github/workflows/000077500000000000000000000000001433555231200167305ustar00rootroot00000000000000plumbing-1.3.0/.github/workflows/main.yml000066400000000000000000000026411433555231200204020ustar00rootroot00000000000000--- name: build on: push: tags: - v* branches: - main - master pull_request: permissions: contents: write pull-requests: read jobs: build: runs-on: ubuntu-latest strategy: fail-fast: false matrix: go: - '1.18' - '1.19' steps: - name: Checkout uses: actions/checkout@v3 - name: Set up Go uses: actions/setup-go@v3 with: go-version: ${{ matrix.go }} - name: golangci-lint uses: golangci/golangci-lint-action@v3 with: only-new-issues: true - name: Test run: go test -v -coverprofile=cover.out ./... - name: Send coverage uses: shogo82148/actions-goveralls@v1 with: path-to-profile: cover.out flag-name: Go-${{ matrix.go }} parallel: true finish: needs: build runs-on: ubuntu-latest steps: - uses: shogo82148/actions-goveralls@v1 with: parallel-finished: true - name: Checkout uses: actions/checkout@v3 with: fetch-depth: 0 - name: Set up Go uses: actions/setup-go@v3 with: go-version: '1.19' - name: Run GoReleaser uses: goreleaser/goreleaser-action@v3.2.0 with: version: latest args: release --rm-dist env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} plumbing-1.3.0/.golangci.yaml000066400000000000000000000002301433555231200160530ustar00rootroot00000000000000--- linters: enable-all: true disable: - dupword - exhaustivestruct - exhaustruct - nonamedreturns - varnamelen - wrapcheck plumbing-1.3.0/.goreleaser.yml000066400000000000000000000001271433555231200162640ustar00rootroot00000000000000--- builds: - skip: true release: prerelease: auto changelog: use: github-native plumbing-1.3.0/LICENSE000066400000000000000000000027501433555231200143440ustar00rootroot00000000000000BSD 3-Clause License Copyright (c) 2019, Matt Dainty All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name of the copyright holder nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. plumbing-1.3.0/README.md000066400000000000000000000013631433555231200146150ustar00rootroot00000000000000[![Build Status](https://img.shields.io/github/workflow/status/bodgit/plumbing/build)](https://github.com/bodgit/plumbing/actions?query=workflow%3Abuild) [![Coverage Status](https://coveralls.io/repos/github/bodgit/plumbing/badge.svg?branch=master)](https://coveralls.io/github/bodgit/plumbing?branch=master) [![Go Report Card](https://goreportcard.com/badge/github.com/bodgit/plumbing)](https://goreportcard.com/report/github.com/bodgit/plumbing) [![GoDoc](https://godoc.org/github.com/bodgit/plumbing?status.svg)](https://godoc.org/github.com/bodgit/plumbing) ![Go version](https://img.shields.io/badge/Go-1.19-brightgreen.svg) ![Go version](https://img.shields.io/badge/Go-1.18-brightgreen.svg) plumbing ======== Assorted I/O U-bends, T-pieces, etc. plumbing-1.3.0/count.go000066400000000000000000000006571433555231200150220ustar00rootroot00000000000000package plumbing import ( "sync/atomic" ) // WriteCounter is an io.Writer that simply counts the number of bytes written // to it. type WriteCounter struct { count uint64 } func (wc *WriteCounter) Write(p []byte) (int, error) { n := len(p) atomic.AddUint64(&wc.count, uint64(n)) return n, nil } // Count returns the number of bytes written. func (wc *WriteCounter) Count() uint64 { return atomic.LoadUint64(&wc.count) } plumbing-1.3.0/count_test.go000066400000000000000000000006641433555231200160570ustar00rootroot00000000000000package plumbing_test import ( "testing" "github.com/bodgit/plumbing" "github.com/stretchr/testify/assert" ) func TestWriteCounter(t *testing.T) { t.Parallel() w := &plumbing.WriteCounter{} n, err := w.Write([]byte("abcd")) assert.Equal(t, 4, n) assert.Nil(t, err) assert.Equal(t, uint64(4), w.Count()) n, err = w.Write([]byte("efgh")) assert.Equal(t, 4, n) assert.Nil(t, err) assert.Equal(t, uint64(8), w.Count()) } plumbing-1.3.0/example_test.go000066400000000000000000000052021433555231200163530ustar00rootroot00000000000000package plumbing_test import ( "archive/zip" "bytes" "fmt" "io" "github.com/bodgit/plumbing" ) func ExampleWriteCounter() { in := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} writer := plumbing.WriteCounter{} reader := io.TeeReader(bytes.NewReader(in), &writer) if _, err := io.CopyN(io.Discard, reader, 4); err != nil { panic(err) } if _, err := io.Copy(io.Discard, reader); err != nil { panic(err) } fmt.Println(writer.Count()) // Output: 10 } func ExampleTeeReaderAt() { // Smallest valid zip archive in := []byte{80, 75, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} writer := plumbing.WriteCounter{} if _, err := zip.NewReader(plumbing.TeeReaderAt(bytes.NewReader(in), &writer), int64(len(in))); err != nil { panic(err) } fmt.Println(writer.Count()) // Output: 44 } func ExampleTeeReadCloser() { in := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} writer := plumbing.WriteCounter{} reader := plumbing.TeeReadCloser(io.NopCloser(bytes.NewReader(in)), &writer) defer reader.Close() if _, err := io.Copy(io.Discard, reader); err != nil { panic(err) } fmt.Println(writer.Count()) // Output: 10 } func ExamplePaddedReader() { in := []byte{1, 2, 3, 4} reader := plumbing.PaddedReader(bytes.NewReader(in), 8, 0) writer := new(bytes.Buffer) if _, err := io.Copy(writer, reader); err != nil { panic(err) } fmt.Println(writer.Bytes()) // Output: [1 2 3 4 0 0 0 0] } func ExampleNopWriteCloser() { writer := plumbing.NopWriteCloser(new(bytes.Buffer)) fmt.Println(writer.Close()) // Output: } func ExampleMultiWriteCloser() { in := []byte{0, 1, 2, 3} b1, b2 := new(bytes.Buffer), new(bytes.Buffer) writer := plumbing.MultiWriteCloser(plumbing.NopWriteCloser(b1), plumbing.NopWriteCloser(b2)) if _, err := writer.Write(in); err != nil { panic(err) } if err := writer.Close(); err != nil { panic(err) } fmt.Println(b1.Bytes(), b2.Bytes()) // Output: [0 1 2 3] [0 1 2 3] } func ExampleMultiReadCloser() { b1, b2 := bytes.NewReader([]byte{0, 1, 2, 3}), bytes.NewReader([]byte{4, 5, 6, 7}) r := plumbing.MultiReadCloser(io.NopCloser(b1), io.NopCloser(b2)) w := new(bytes.Buffer) if _, err := io.Copy(w, r); err != nil { panic(err) } if err := r.Close(); err != nil { panic(err) } fmt.Println(w.Bytes()) // Output: [0 1 2 3 4 5 6 7] } func ExampleLimitReadCloser() { in := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9} reader := plumbing.LimitReadCloser(io.NopCloser(bytes.NewReader(in)), 5) writer := new(bytes.Buffer) if _, err := io.Copy(writer, reader); err != nil { panic(err) } if err := reader.Close(); err != nil { panic(err) } fmt.Println(writer.Bytes()) // Output: [0 1 2 3 4] } plumbing-1.3.0/fill.go000066400000000000000000000005211433555231200146060ustar00rootroot00000000000000package plumbing import "io" type fillReader struct { b byte } func (r *fillReader) Read(p []byte) (int, error) { for i := range p { p[i] = r.b } return len(p), nil } // FillReader returns an io.Reader such that Read calls return an unlimited // stream of b bytes. func FillReader(b byte) io.Reader { return &fillReader{b} } plumbing-1.3.0/go.mod000066400000000000000000000001271433555231200144410ustar00rootroot00000000000000module github.com/bodgit/plumbing go 1.13 require github.com/stretchr/testify v1.8.1 plumbing-1.3.0/go.sum000066400000000000000000000027561433555231200145000ustar00rootroot00000000000000github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 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.0-20200313102051-9f266ea9e77c/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= plumbing-1.3.0/limit.go000066400000000000000000000016231433555231200150020ustar00rootroot00000000000000package plumbing import "io" // A LimitedReadCloser reads from R but limits the amount of // data returned to just N bytes. Each call to Read // updates N to reflect the new amount remaining. // Read returns EOF when N <= 0 or when the underlying R returns EOF. type LimitedReadCloser struct { R io.ReadCloser N int64 } func (l *LimitedReadCloser) Read(p []byte) (n int, err error) { if l.N <= 0 { return 0, io.EOF } if int64(len(p)) > l.N { p = p[0:l.N] } n, err = l.R.Read(p) l.N -= int64(n) return } // Close closes the LimitedReadCloser, rendering it unusable for I/O. func (l *LimitedReadCloser) Close() error { return l.R.Close() } // LimitReadCloser returns an io.ReadCloser that reads from r // but stops with EOF after n bytes. // The underlying implementation is a *LimitedReadCloser. func LimitReadCloser(r io.ReadCloser, n int64) io.ReadCloser { return &LimitedReadCloser{r, n} } plumbing-1.3.0/limit_test.go000066400000000000000000000014151433555231200160400ustar00rootroot00000000000000package plumbing_test import ( "io" "strings" "testing" "github.com/bodgit/plumbing" "github.com/stretchr/testify/assert" ) func TestLimitReadCloser(t *testing.T) { t.Parallel() tables := []struct { name string reader io.ReadCloser limit int64 n int64 err error }{ { "success", io.NopCloser(strings.NewReader("abcdefghij")), 5, 5, nil, }, { "partial", io.NopCloser(strings.NewReader("abcde")), 10, 5, nil, }, } for _, table := range tables { table := table t.Run(table.name, func(t *testing.T) { t.Parallel() r := plumbing.LimitReadCloser(table.reader, table.limit) defer r.Close() n, err := io.Copy(io.Discard, r) assert.Equal(t, table.n, n) assert.Equal(t, table.err, err) }) } } plumbing-1.3.0/multi.go000066400000000000000000000046271433555231200150250ustar00rootroot00000000000000package plumbing import ( "io" ) type multiWriteCloser struct { writeClosers []io.WriteCloser } func (t *multiWriteCloser) Write(p []byte) (n int, err error) { for _, wc := range t.writeClosers { n, err = wc.Write(p) if err != nil { return } if n != len(p) { err = io.ErrShortWrite return } } return len(p), nil } func (t *multiWriteCloser) Close() (err error) { for _, wc := range t.writeClosers { err = wc.Close() if err != nil { return } } return } // MultiWriteCloser creates a writer that duplicates its writes to all the // provided writers, similar to the Unix tee(1) command. // // Each write is written to each listed writer, one at a time. // If a listed writer returns an error, that overall write operation // stops and returns the error; it does not continue down the list. func MultiWriteCloser(writeClosers ...io.WriteCloser) io.WriteCloser { allWriteClosers := make([]io.WriteCloser, 0, len(writeClosers)) for _, wc := range writeClosers { if mwc, ok := wc.(*multiWriteCloser); ok { allWriteClosers = append(allWriteClosers, mwc.writeClosers...) } else { allWriteClosers = append(allWriteClosers, wc) } } return &multiWriteCloser{allWriteClosers} } type multiReadCloser struct { readClosers []io.ReadCloser i int } func (mrc *multiReadCloser) Read(p []byte) (n int, err error) { for mrc.i < len(mrc.readClosers) { if len(mrc.readClosers) == 1 { if rc, ok := mrc.readClosers[0].(*multiReadCloser); ok { mrc.readClosers = rc.readClosers continue } } n, err = mrc.readClosers[mrc.i].Read(p) if err == io.EOF { //nolint:errorlint mrc.i++ } if n > 0 || err != io.EOF { //nolint:errorlint if err == io.EOF && mrc.i < len(mrc.readClosers) { //nolint:errorlint err = nil } return } } return 0, io.EOF } func (mrc *multiReadCloser) Close() (err error) { for _, rc := range mrc.readClosers { err = rc.Close() if err != nil { return } } return } // MultiReadCloser returns an io.ReadCloser that's the logical concatenation // of the provider input readers. They're read sequentially. Once all inputs // have returned io.EOF, Read will return EOF. If any of the readers return // a non-nil, non-EOF error, Read will return that error. func MultiReadCloser(readClosers ...io.ReadCloser) io.ReadCloser { rc := make([]io.ReadCloser, len(readClosers)) copy(rc, readClosers) return &multiReadCloser{rc, 0} } plumbing-1.3.0/multi_test.go000066400000000000000000000061561433555231200160630ustar00rootroot00000000000000package plumbing_test import ( "bytes" "errors" "io" "testing" "github.com/bodgit/plumbing" "github.com/stretchr/testify/assert" ) var errClose = errors.New("error closing") type errorWriteCloser struct { io.WriteCloser } func (errorWriteCloser) Write(p []byte) (n int, err error) { return len(p), nil } func (errorWriteCloser) Close() error { return errClose } type partialWriter struct { io.Writer } func (partialWriter) Write(p []byte) (n int, err error) { return len(p) - 1, nil } //nolint:funlen func TestMultiWriteCloser(t *testing.T) { t.Parallel() in := []byte("abcdefghij") tables := []struct { name string writer io.WriteCloser n int writeErr error closeErr error }{ { "success", plumbing.NopWriteCloser(new(bytes.Buffer)), 10, nil, nil, }, { "nested", plumbing.MultiWriteCloser(plumbing.NopWriteCloser(new(bytes.Buffer))), 10, nil, nil, }, { "write", plumbing.NopWriteCloser(errorWriter{}), 0, errWrite, nil, }, { "close", errorWriteCloser{}, 10, nil, errClose, }, { "partial", plumbing.NopWriteCloser(partialWriter{}), 9, io.ErrShortWrite, nil, }, } for _, table := range tables { table := table t.Run(table.name, func(t *testing.T) { t.Parallel() dst := plumbing.NopWriteCloser(new(bytes.Buffer)) w := plumbing.MultiWriteCloser(table.writer, dst) n, err := w.Write(in) assert.Equal(t, table.n, n) assert.Equal(t, table.writeErr, err) err = w.Close() assert.Equal(t, table.closeErr, err) }) } } type errorReadCloser struct { io.Reader } func (errorReadCloser) Close() error { return errClose } type earlyEOFBytesReader struct { r *bytes.Reader } func (r *earlyEOFBytesReader) Read(p []byte) (n int, err error) { n, err = r.r.Read(p) // Return an early EOF when bytes have been read and there's none left if n > 0 && err == nil && r.r.Len() == 0 { err = io.EOF } return } func TestMultiReadCloser(t *testing.T) { t.Parallel() tables := []struct { name string readers []io.ReadCloser n int64 expected []byte readErr error closeErr error }{ { "success", []io.ReadCloser{ io.NopCloser(&earlyEOFBytesReader{bytes.NewReader([]byte("abcde"))}), io.NopCloser(bytes.NewReader([]byte("fghij"))), }, 10, []byte("abcdefghij"), nil, nil, }, { "nested", []io.ReadCloser{ plumbing.MultiReadCloser(io.NopCloser(bytes.NewReader([]byte("abcdefghij")))), }, 10, []byte("abcdefghij"), nil, nil, }, { "close", []io.ReadCloser{ io.NopCloser(bytes.NewReader([]byte("abcde"))), &errorReadCloser{bytes.NewReader([]byte("fghij"))}, }, 10, []byte("abcdefghij"), nil, errClose, }, } for _, table := range tables { table := table t.Run(table.name, func(t *testing.T) { t.Parallel() r := plumbing.MultiReadCloser(table.readers...) b := new(bytes.Buffer) n, err := io.Copy(b, r) assert.Equal(t, table.n, n) assert.Equal(t, table.readErr, err) assert.Equal(t, table.expected, b.Bytes()) err = r.Close() assert.Equal(t, table.closeErr, err) }) } } plumbing-1.3.0/padded.go000066400000000000000000000005131433555231200151020ustar00rootroot00000000000000package plumbing import ( "io" ) // PaddedReader returns an io.Reader that reads at most n bytes from r. If // fewer than n bytes are available from r then any remaining bytes return // fill instead. func PaddedReader(r io.Reader, n int64, fill byte) io.Reader { return io.LimitReader(io.MultiReader(r, FillReader(fill)), n) } plumbing-1.3.0/padded_test.go000066400000000000000000000007041433555231200161430ustar00rootroot00000000000000package plumbing_test import ( "bytes" "io" "testing" "github.com/bodgit/plumbing" "github.com/stretchr/testify/assert" ) func TestPaddedReader(t *testing.T) { t.Parallel() src := []byte("abcdef") rb := bytes.NewReader(src) wb := new(bytes.Buffer) r := plumbing.PaddedReader(rb, 8, 0) n, err := io.Copy(wb, r) assert.Equal(t, int64(8), n) assert.Nil(t, err) assert.Equal(t, []byte{'a', 'b', 'c', 'd', 'e', 'f', 0, 0}, wb.Bytes()) } plumbing-1.3.0/plumbing.go000066400000000000000000000005661433555231200155060ustar00rootroot00000000000000// Package plumbing is a collection of assorted I/O helpers. package plumbing import "io" type nopWriteCloser struct { io.Writer } func (nopWriteCloser) Close() error { return nil } // NopWriteCloser returns an io.WriteCloser with a no-op Close method // wrapping the provided io.Writer w. func NopWriteCloser(w io.Writer) io.WriteCloser { return nopWriteCloser{w} } plumbing-1.3.0/tee.go000066400000000000000000000026041433555231200144410ustar00rootroot00000000000000package plumbing import "io" type teeReaderAt struct { r io.ReaderAt w io.Writer } func (t *teeReaderAt) ReadAt(p []byte, off int64) (n int, err error) { n, err = t.r.ReadAt(p, off) if n > 0 { if n, err := t.w.Write(p[:n]); err != nil { return n, err } } return } // TeeReaderAt returns an io.ReaderAt that writes to w what it reads from r. // All reads from r performed through it are matched with corresponding writes // to w. There is no internal buffering - the write must complete before the // read completes. Any error encountered while writing is reported as a read // error. func TeeReaderAt(r io.ReaderAt, w io.Writer) io.ReaderAt { return &teeReaderAt{r, w} } type teeReadCloser struct { r io.ReadCloser w io.Writer } func (t *teeReadCloser) Read(p []byte) (n int, err error) { n, err = t.r.Read(p) if n > 0 { if n, err := t.w.Write(p[:n]); err != nil { return n, err } } return } func (t *teeReadCloser) Close() error { return t.r.Close() } // TeeReadCloser returns an io.ReadCloser that writes to w what it reads from // r. All reads from r performed through it are matched with corresponding // writes to w. There is no internal buffering - the write must complete // before the read completes. Any error encountered while writing is reported // as a read error. func TeeReadCloser(r io.ReadCloser, w io.Writer) io.ReadCloser { return &teeReadCloser{r, w} } plumbing-1.3.0/tee_test.go000066400000000000000000000032541433555231200155020ustar00rootroot00000000000000package plumbing_test import ( "bytes" "errors" "io" "testing" "github.com/bodgit/plumbing" "github.com/stretchr/testify/assert" ) var errWrite = errors.New("error writing") type errorWriter struct { io.Writer } func (errorWriter) Write(p []byte) (n int, err error) { return 0, errWrite } func TestTeeReaderAt(t *testing.T) { t.Parallel() in := []byte("abcdefghij") tables := []struct { name string reader io.ReaderAt writer io.Writer length int offset int64 n int err error }{ { "success", bytes.NewReader(in), io.Discard, 3, 2, 3, nil, }, { "fail", bytes.NewReader(in), errorWriter{}, 3, 2, 0, errWrite, }, } for _, table := range tables { table := table t.Run(table.name, func(t *testing.T) { t.Parallel() r := plumbing.TeeReaderAt(table.reader, table.writer) dst := make([]byte, table.length) n, err := r.ReadAt(dst, table.offset) assert.Equal(t, table.n, n) assert.Equal(t, table.err, err) }) } } func TestTeeReadCloser(t *testing.T) { t.Parallel() in := []byte("abcdefghij") tables := []struct { name string reader io.ReadCloser writer io.Writer n int64 err error }{ { "success", io.NopCloser(bytes.NewReader(in)), io.Discard, 10, nil, }, { "fail", io.NopCloser(bytes.NewReader(in)), errorWriter{}, 0, errWrite, }, } for _, table := range tables { table := table t.Run(table.name, func(t *testing.T) { t.Parallel() r := plumbing.TeeReadCloser(table.reader, table.writer) defer r.Close() n, err := io.Copy(io.Discard, r) assert.Equal(t, table.n, n) assert.Equal(t, table.err, err) }) } } plumbing-1.3.0/zero.go000066400000000000000000000006041433555231200146410ustar00rootroot00000000000000package plumbing import "io" type devZero struct { io.Reader } func (w *devZero) Write(p []byte) (int, error) { return len(p), nil } // DevZero returns an io.ReadWriter that behaves like /dev/zero such that Read // calls return an unlimited stream of zero bytes and all Write calls succeed // without doing anything. func DevZero() io.ReadWriter { return &devZero{FillReader(0)} } plumbing-1.3.0/zero_test.go000066400000000000000000000011111433555231200156720ustar00rootroot00000000000000package plumbing_test import ( "bytes" "io" "testing" "github.com/bodgit/plumbing" "github.com/stretchr/testify/assert" ) const limit = 10 func TestDevZero(t *testing.T) { t.Parallel() rw := plumbing.DevZero() b := new(bytes.Buffer) n, err := io.Copy(b, io.LimitReader(rw, limit)) if err != nil { t.Fatal(err) } assert.Equal(t, limit, int(n)) assert.Equal(t, limit, b.Len()) assert.Equal(t, bytes.Repeat([]byte{0x00}, limit), b.Bytes()) n, err = io.Copy(rw, b) if err != nil { t.Fatal(err) } assert.Equal(t, limit, int(n)) assert.Equal(t, 0, b.Len()) }