github.com/hugelgupf/u-root@v0.0.0-20191023214958-4807c632154c/pkg/uio/lazy.go (about)

     1  // Copyright 2018 the u-root Authors. All rights reserved
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package uio
     6  
     7  import (
     8  	"io"
     9  	"os"
    10  )
    11  
    12  // LazyOpener is a lazy io.Reader.
    13  //
    14  // LazyOpener will use a given open function to derive an io.Reader when Read
    15  // is first called on the LazyOpener.
    16  type LazyOpener struct {
    17  	r    io.Reader
    18  	err  error
    19  	open func() (io.Reader, error)
    20  }
    21  
    22  // NewLazyOpener returns a lazy io.Reader based on `open`.
    23  func NewLazyOpener(open func() (io.Reader, error)) io.ReadCloser {
    24  	return &LazyOpener{open: open}
    25  }
    26  
    27  // Read implements io.Reader.Read lazily.
    28  //
    29  // If called for the first time, the underlying reader will be obtained and
    30  // then used for the first and subsequent calls to Read.
    31  func (lr *LazyOpener) Read(p []byte) (int, error) {
    32  	if lr.r == nil && lr.err == nil {
    33  		lr.r, lr.err = lr.open()
    34  	}
    35  	if lr.err != nil {
    36  		return 0, lr.err
    37  	}
    38  	return lr.r.Read(p)
    39  }
    40  
    41  // Close implements io.Closer.Close.
    42  func (lr *LazyOpener) Close() error {
    43  	if c, ok := lr.r.(io.Closer); ok {
    44  		return c.Close()
    45  	}
    46  	return nil
    47  }
    48  
    49  // ReadAtCloser is an io.ReaderAt and an io.Closer.
    50  type ReadAtCloser interface {
    51  	io.ReaderAt
    52  	io.Closer
    53  }
    54  
    55  // LazyOpenerAt is a lazy io.ReaderAt.
    56  //
    57  // LazyOpenerAt will use a given open function to derive an io.ReaderAt when
    58  // ReadAt is first called.
    59  type LazyOpenerAt struct {
    60  	r    io.ReaderAt
    61  	err  error
    62  	open func() (io.ReaderAt, error)
    63  }
    64  
    65  // NewLazyFile returns a lazy ReaderAt opened from path.
    66  func NewLazyFile(path string) ReadAtCloser {
    67  	if len(path) == 0 {
    68  		return nil
    69  	}
    70  	return NewLazyOpenerAt(func() (io.ReaderAt, error) {
    71  		return os.Open(path)
    72  	})
    73  }
    74  
    75  // NewLazyOpenerAt returns a lazy io.ReaderAt based on `open`.
    76  func NewLazyOpenerAt(open func() (io.ReaderAt, error)) ReadAtCloser {
    77  	return &LazyOpenerAt{open: open}
    78  }
    79  
    80  // ReadAt implements io.ReaderAt.ReadAt.
    81  func (loa *LazyOpenerAt) ReadAt(p []byte, off int64) (int, error) {
    82  	if loa.r == nil && loa.err == nil {
    83  		loa.r, loa.err = loa.open()
    84  	}
    85  	if loa.err != nil {
    86  		return 0, loa.err
    87  	}
    88  	return loa.r.ReadAt(p, off)
    89  }
    90  
    91  // Close implements io.Closer.Close.
    92  func (loa *LazyOpenerAt) Close() error {
    93  	if c, ok := loa.r.(io.Closer); ok {
    94  		return c.Close()
    95  	}
    96  	return nil
    97  }