1// Package ioutil implements some I/O utility functions. 2package ioutil 3 4import ( 5 "bufio" 6 "context" 7 "errors" 8 "io" 9 10 "github.com/jbenet/go-context/io" 11) 12 13type readPeeker interface { 14 io.Reader 15 Peek(int) ([]byte, error) 16} 17 18var ( 19 ErrEmptyReader = errors.New("reader is empty") 20) 21 22// NonEmptyReader takes a reader and returns it if it is not empty, or 23// `ErrEmptyReader` if it is empty. If there is an error when reading the first 24// byte of the given reader, it will be propagated. 25func NonEmptyReader(r io.Reader) (io.Reader, error) { 26 pr, ok := r.(readPeeker) 27 if !ok { 28 pr = bufio.NewReader(r) 29 } 30 31 _, err := pr.Peek(1) 32 if err == io.EOF { 33 return nil, ErrEmptyReader 34 } 35 36 if err != nil { 37 return nil, err 38 } 39 40 return pr, nil 41} 42 43type readCloser struct { 44 io.Reader 45 closer io.Closer 46} 47 48func (r *readCloser) Close() error { 49 return r.closer.Close() 50} 51 52// NewReadCloser creates an `io.ReadCloser` with the given `io.Reader` and 53// `io.Closer`. 54func NewReadCloser(r io.Reader, c io.Closer) io.ReadCloser { 55 return &readCloser{Reader: r, closer: c} 56} 57 58type writeCloser struct { 59 io.Writer 60 closer io.Closer 61} 62 63func (r *writeCloser) Close() error { 64 return r.closer.Close() 65} 66 67// NewWriteCloser creates an `io.WriteCloser` with the given `io.Writer` and 68// `io.Closer`. 69func NewWriteCloser(w io.Writer, c io.Closer) io.WriteCloser { 70 return &writeCloser{Writer: w, closer: c} 71} 72 73type writeNopCloser struct { 74 io.Writer 75} 76 77func (writeNopCloser) Close() error { return nil } 78 79// WriteNopCloser returns a WriteCloser with a no-op Close method wrapping 80// the provided Writer w. 81func WriteNopCloser(w io.Writer) io.WriteCloser { 82 return writeNopCloser{w} 83} 84 85// CheckClose calls Close on the given io.Closer. If the given *error points to 86// nil, it will be assigned the error returned by Close. Otherwise, any error 87// returned by Close will be ignored. CheckClose is usually called with defer. 88func CheckClose(c io.Closer, err *error) { 89 if cerr := c.Close(); cerr != nil && *err == nil { 90 *err = cerr 91 } 92} 93 94// NewContextWriter wraps a writer to make it respect given Context. 95// If there is a blocking write, the returned Writer will return whenever the 96// context is cancelled (the return values are n=0 and err=ctx.Err()). 97func NewContextWriter(ctx context.Context, w io.Writer) io.Writer { 98 return ctxio.NewWriter(ctx, w) 99} 100 101// NewContextReader wraps a reader to make it respect given Context. 102// If there is a blocking read, the returned Reader will return whenever the 103// context is cancelled (the return values are n=0 and err=ctx.Err()). 104func NewContextReader(ctx context.Context, r io.Reader) io.Reader { 105 return ctxio.NewReader(ctx, r) 106} 107 108// NewContextWriteCloser as NewContextWriter but with io.Closer interface. 109func NewContextWriteCloser(ctx context.Context, w io.WriteCloser) io.WriteCloser { 110 ctxw := ctxio.NewWriter(ctx, w) 111 return NewWriteCloser(ctxw, w) 112} 113 114// NewContextReadCloser as NewContextReader but with io.Closer interface. 115func NewContextReadCloser(ctx context.Context, r io.ReadCloser) io.ReadCloser { 116 ctxr := ctxio.NewReader(ctx, r) 117 return NewReadCloser(ctxr, r) 118} 119 120type readerOnError struct { 121 io.Reader 122 notify func(error) 123} 124 125// NewReaderOnError returns a io.Reader that call the notify function when an 126// unexpected (!io.EOF) error happens, after call Read function. 127func NewReaderOnError(r io.Reader, notify func(error)) io.Reader { 128 return &readerOnError{r, notify} 129} 130 131// NewReadCloserOnError returns a io.ReadCloser that call the notify function 132// when an unexpected (!io.EOF) error happens, after call Read function. 133func NewReadCloserOnError(r io.ReadCloser, notify func(error)) io.ReadCloser { 134 return NewReadCloser(NewReaderOnError(r, notify), r) 135} 136 137func (r *readerOnError) Read(buf []byte) (n int, err error) { 138 n, err = r.Reader.Read(buf) 139 if err != nil && err != io.EOF { 140 r.notify(err) 141 } 142 143 return 144} 145 146type writerOnError struct { 147 io.Writer 148 notify func(error) 149} 150 151// NewWriterOnError returns a io.Writer that call the notify function when an 152// unexpected (!io.EOF) error happens, after call Write function. 153func NewWriterOnError(w io.Writer, notify func(error)) io.Writer { 154 return &writerOnError{w, notify} 155} 156 157// NewWriteCloserOnError returns a io.WriteCloser that call the notify function 158//when an unexpected (!io.EOF) error happens, after call Write function. 159func NewWriteCloserOnError(w io.WriteCloser, notify func(error)) io.WriteCloser { 160 return NewWriteCloser(NewWriterOnError(w, notify), w) 161} 162 163func (r *writerOnError) Write(p []byte) (n int, err error) { 164 n, err = r.Writer.Write(p) 165 if err != nil && err != io.EOF { 166 r.notify(err) 167 } 168 169 return 170} 171