github.com/haraldrudell/parl@v0.4.176/pio/context-reader.go (about)

     1  /*
     2  © 2023–present Harald Rudell <harald.rudell@gmail.com> (https://haraldrudell.github.io/haraldrudell/)
     3  ISC License
     4  */
     5  
     6  package pio
     7  
     8  import (
     9  	"context"
    10  	"io"
    11  )
    12  
    13  // ContextReader is an [io.ReadCloser] that aborts on context cancel
    14  //   - on context cancel, Read returns error [context.Canceled]
    15  //   - If the runtime type of reader implements [io.Close], it ContextReader can close it
    16  type ContextReader struct {
    17  	reader io.Reader // Read()
    18  	// idempotent pannic-free closer if reader implemented [io.Closer]
    19  	//	- Close() IsClosable()
    20  	ContextCloser
    21  	ctx context.Context
    22  }
    23  
    24  var _ io.ReadCloser = &ContextReader{}
    25  
    26  // NewContextReader returns an [io.ReadCloser] that aborts on context cancel
    27  //   - on context cancel, Read returns error [context.Canceled]
    28  //   - If the runtime type of reader implements [io.Close], it can be closed
    29  func NewContextReader(reader io.Reader, ctx context.Context) (contextReader *ContextReader) {
    30  	var closer, _ = reader.(io.Closer)
    31  	return &ContextReader{
    32  		reader:        reader,
    33  		ContextCloser: *NewContextCloser(closer),
    34  		ctx:           ctx,
    35  	}
    36  }
    37  
    38  var _ = context.Canceled
    39  
    40  // Read is like [io.Reader.Read] but cancels if the context is canceled
    41  //   - on context cancel, the error returned is [context.Canceled]
    42  func (c *ContextReader) Read(p []byte) (n int, err error) {
    43  	if err = c.ctx.Err(); err != nil {
    44  		return
    45  	}
    46  	return c.reader.Read(p)
    47  }