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