func (c *cacheWrapper) Attributes(ctx context.Context, path string, options *ReaderOptions) (*Attributes, error) {
	f, err := c.Open(ctx, path, options)
	if err != nil {
		return nil, err
	}
	return &f.Attributes, nil
}
Attributes implements FS.
package storage
import (
	"context"
	"io"
	"time"
)
type CacheOptions struct {
	MaxAge time.Duration
	DefaultExpired bool
	NoData bool
}
func NewCacheWrapper(src, cache FS, options *CacheOptions) FS {
	if options == nil {
		options = &CacheOptions{}
	}
	return &cacheWrapper{
		src:     src,
		cache:   cache,
		options: options,
	}
}
type openForwarder struct {
	src func() (io.ReadCloser, error)
	rc  io.ReadCloser
}
func (f *openForwarder) open() error {
	if f.rc != nil {
		return nil
	}
	rc, err := f.src()
	if err != nil {
		return err
	}
	f.rc = rc
	return nil
}
func (f *openForwarder) Read(p []byte) (n int, err error) {
	if err = f.open(); err != nil {
		return 0, err
	}
	return f.rc.Read(p)
}
func (f *openForwarder) Close() (err error) {
	if f.rc == nil {
		return nil
	}
	return f.rc.Close()
}
type cacheWrapper struct {
	src     FS
	cache   FS
	options *CacheOptions
}
func (c *cacheWrapper) isExpired(file *File) bool {
	if c.options.MaxAge == 0 {
		return false
	}
	creationTime := file.CreationTime
	if creationTime.IsZero() {
		creationTime = file.ModTime  
	}
	if creationTime.IsZero() {
		return c.options.DefaultExpired
	}
	return time.Since(creationTime) > c.options.MaxAge
}
func (c *cacheWrapper) openCache(ctx context.Context, path string, options *ReaderOptions) (*File, error) {
	f, err := c.cache.Open(ctx, path, options)
	if err != nil {
		return nil, err
	}
	if c.options.NoData {
		f.ReadCloser = &openForwarder{
			src: func() (io.ReadCloser, error) {
				return c.src.Open(ctx, path, options)
			},
		}
	}
	return f, nil
}
func (c *cacheWrapper) Open(ctx context.Context, path string, options *ReaderOptions) (*File, error) {
	f, err := c.openCache(ctx, path, options)
	if err == nil {
		if !c.isExpired(f) {
			return f, nil
		}
	} else if !IsNotExist(err) {
		return nil, err
	}
	sf, err := c.src.Open(ctx, path, options)
	if err != nil {
		return nil, err
	}
	defer sf.Close()
	cacheAttrs := sf.Attributes
	cacheAttrs.CreationTime = time.Now()  
	wc, err := c.cache.Create(ctx, path, &WriterOptions{
		Attributes: cacheAttrs,
	})
	if err != nil {
		return nil, err
	}
	if !c.options.NoData {
		if _, err := io.Copy(wc, sf); err != nil {
			wc.Close()
			return nil, err
		}
	}
	if err := wc.Close(); err != nil {
		return nil, err
	}
	ff, err := c.openCache(ctx, path, options)
	if err != nil {
		return nil, err
	}
	return ff, nil
}