aboutsummaryrefslogtreecommitdiffstats
path: root/utils/ioutil/common.go
blob: 66044e24ecd9b3ba7446931e6b6b2223e85d95b5 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// Package ioutil implements some I/O utility functions.
package ioutil

import (
	"bufio"
	"context"
	"errors"
	"io"

	"github.com/jbenet/go-context/io"
)

type readPeeker interface {
	io.Reader
	Peek(int) ([]byte, error)
}

var (
	ErrEmptyReader = errors.New("reader is empty")
)

// NonEmptyReader takes a reader and returns it if it is not empty, or
// `ErrEmptyReader` if it is empty. If there is an error when reading the first
// byte of the given reader, it will be propagated.
func NonEmptyReader(r io.Reader) (io.Reader, error) {
	pr, ok := r.(readPeeker)
	if !ok {
		pr = bufio.NewReader(r)
	}

	_, err := pr.Peek(1)
	if err == io.EOF {
		return nil, ErrEmptyReader
	}

	if err != nil {
		return nil, err
	}

	return pr, nil
}

type readCloser struct {
	io.Reader
	closer io.Closer
}

func (r *readCloser) Close() error {
	return r.closer.Close()
}

// NewReadCloser creates an `io.ReadCloser` with the given `io.Reader` and
// `io.Closer`.
func NewReadCloser(r io.Reader, c io.Closer) io.ReadCloser {
	return &readCloser{Reader: r, closer: c}
}

type writeCloser struct {
	io.Writer
	closer io.Closer
}

func (r *writeCloser) Close() error {
	return r.closer.Close()
}

// NewWriteCloser creates an `io.WriteCloser` with the given `io.Writer` and
// `io.Closer`.
func NewWriteCloser(w io.Writer, c io.Closer) io.WriteCloser {
	return &writeCloser{Writer: w, closer: c}
}

type writeNopCloser struct {
	io.Writer
}

func (writeNopCloser) Close() error { return nil }

// WriteNopCloser returns a WriteCloser with a no-op Close method wrapping
// the provided Writer w.
func WriteNopCloser(w io.Writer) io.WriteCloser {
	return writeNopCloser{w}
}

// CheckClose calls Close on the given io.Closer. If the given *error points to
// nil, it will be assigned the error returned by Close. Otherwise, any error
// returned by Close will be ignored. CheckClose is usually called with defer.
func CheckClose(c io.Closer, err *error) {
	if cerr := c.Close(); cerr != nil && *err == nil {
		*err = cerr
	}
}

// NewContextWriter wraps a writer to make it respect given Context.
// If there is a blocking write, the returned Writer will return whenever the
// context is cancelled (the return values are n=0 and err=ctx.Err()).
func NewContextWriter(ctx context.Context, w io.Writer) io.Writer {
	return ctxio.NewWriter(ctx, w)
}

// NewContextReader wraps a reader to make it respect given Context.
// If there is a blocking read, the returned Reader will return whenever the
// context is cancelled (the return values are n=0 and err=ctx.Err()).
func NewContextReader(ctx context.Context, r io.Reader) io.Reader {
	return ctxio.NewReader(ctx, r)
}

// NewContextWriteCloser as NewContextWriter but with io.Closer interface.
func NewContextWriteCloser(ctx context.Context, w io.WriteCloser) io.WriteCloser {
	ctxw := ctxio.NewWriter(ctx, w)
	return NewWriteCloser(ctxw, w)
}

// NewContextReadCloser as NewContextReader but with io.Closer interface.
func NewContextReadCloser(ctx context.Context, r io.ReadCloser) io.ReadCloser {
	ctxr := ctxio.NewReader(ctx, r)
	return NewReadCloser(ctxr, r)
}

type readerOnError struct {
	io.Reader
	notify func(error)
}

// NewReaderOnError returns a io.Reader that call the notify function when an
// unexpected (!io.EOF) error happends, after call Read function.
func NewReaderOnError(r io.Reader, notify func(error)) io.Reader {
	return &readerOnError{r, notify}
}

// NewReadCloserOnError returns a io.ReadCloser that call the notify function
// when an unexpected (!io.EOF) error happends, after call Read function.
func NewReadCloserOnError(r io.ReadCloser, notify func(error)) io.ReadCloser {
	return NewReadCloser(NewReaderOnError(r, notify), r)
}

func (r *readerOnError) Read(buf []byte) (n int, err error) {
	n, err = r.Reader.Read(buf)
	if err != nil && err != io.EOF {
		r.notify(err)
	}

	return
}

type writerOnError struct {
	io.Writer
	notify func(error)
}

// NewWriterOnError returns a io.Writer that call the notify function when an
// unexpected (!io.EOF) error happends, after call Write function.
func NewWriterOnError(w io.Writer, notify func(error)) io.Writer {
	return &writerOnError{w, notify}
}

// NewWriteCloserOnError returns a io.WriteCloser that call the notify function
//when an unexpected (!io.EOF) error happends, after call Write function.
func NewWriteCloserOnError(w io.WriteCloser, notify func(error)) io.WriteCloser {
	return NewWriteCloser(NewWriterOnError(w, notify), w)
}

func (r *writerOnError) Write(p []byte) (n int, err error) {
	n, err = r.Writer.Write(p)
	if err != nil && err != io.EOF {
		r.notify(err)
	}

	return
}