github.com/u-root/u-root@v7.0.1-0.20200915234505-ad7babab0a8e+incompatible/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  	"fmt"
     9  	"io"
    10  	"os"
    11  )
    12  
    13  // LazyOpener is a lazy io.Reader.
    14  //
    15  // LazyOpener will use a given open function to derive an io.Reader when Read
    16  // is first called on the LazyOpener.
    17  type LazyOpener struct {
    18  	r    io.Reader
    19  	err  error
    20  	open func() (io.Reader, error)
    21  }
    22  
    23  // NewLazyOpener returns a lazy io.Reader based on `open`.
    24  func NewLazyOpener(open func() (io.Reader, error)) io.ReadCloser {
    25  	return &LazyOpener{open: open}
    26  }
    27  
    28  // Read implements io.Reader.Read lazily.
    29  //
    30  // If called for the first time, the underlying reader will be obtained and
    31  // then used for the first and subsequent calls to Read.
    32  func (lr *LazyOpener) Read(p []byte) (int, error) {
    33  	if lr.r == nil && lr.err == nil {
    34  		lr.r, lr.err = lr.open()
    35  	}
    36  	if lr.err != nil {
    37  		return 0, lr.err
    38  	}
    39  	return lr.r.Read(p)
    40  }
    41  
    42  // Close implements io.Closer.Close.
    43  func (lr *LazyOpener) Close() error {
    44  	if c, ok := lr.r.(io.Closer); ok {
    45  		return c.Close()
    46  	}
    47  	return nil
    48  }
    49  
    50  // LazyOpenerAt is a lazy io.ReaderAt.
    51  //
    52  // LazyOpenerAt will use a given open function to derive an io.ReaderAt when
    53  // ReadAt is first called.
    54  type LazyOpenerAt struct {
    55  	r    io.ReaderAt
    56  	s    string
    57  	err  error
    58  	open func() (io.ReaderAt, error)
    59  }
    60  
    61  // NewLazyFile returns a lazy ReaderAt opened from path.
    62  func NewLazyFile(path string) *LazyOpenerAt {
    63  	if len(path) == 0 {
    64  		return nil
    65  	}
    66  	return NewLazyOpenerAt(path, func() (io.ReaderAt, error) {
    67  		return os.Open(path)
    68  	})
    69  }
    70  
    71  // NewLazyOpenerAt returns a lazy io.ReaderAt based on `open`.
    72  func NewLazyOpenerAt(filename string, open func() (io.ReaderAt, error)) *LazyOpenerAt {
    73  	return &LazyOpenerAt{s: filename, open: open}
    74  }
    75  
    76  // String implements fmt.Stringer.
    77  func (loa *LazyOpenerAt) String() string {
    78  	if len(loa.s) > 0 {
    79  		return loa.s
    80  	}
    81  	if loa.r != nil {
    82  		return fmt.Sprintf("%v", loa.r)
    83  	}
    84  	return "unopened mystery file"
    85  }
    86  
    87  // ReadAt implements io.ReaderAt.ReadAt.
    88  func (loa *LazyOpenerAt) ReadAt(p []byte, off int64) (int, error) {
    89  	if loa.r == nil && loa.err == nil {
    90  		loa.r, loa.err = loa.open()
    91  	}
    92  	if loa.err != nil {
    93  		return 0, loa.err
    94  	}
    95  	return loa.r.ReadAt(p, off)
    96  }
    97  
    98  // Close implements io.Closer.Close.
    99  func (loa *LazyOpenerAt) Close() error {
   100  	if c, ok := loa.r.(io.Closer); ok {
   101  		return c.Close()
   102  	}
   103  	return nil
   104  }