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 }