github.com/tetratelabs/wazero@v1.7.3-0.20240513003603-48f702e154b5/internal/filecache/file_cache.go (about)

     1  package filecache
     2  
     3  import (
     4  	"encoding/hex"
     5  	"errors"
     6  	"io"
     7  	"os"
     8  	"path"
     9  	"path/filepath"
    10  )
    11  
    12  // New returns a new Cache implemented by fileCache.
    13  func New(dir string) Cache {
    14  	return newFileCache(dir)
    15  }
    16  
    17  func newFileCache(dir string) *fileCache {
    18  	return &fileCache{dirPath: dir}
    19  }
    20  
    21  // fileCache persists compiled functions into dirPath.
    22  //
    23  // Note: this can be expanded to do binary signing/verification, set TTL on each entry, etc.
    24  type fileCache struct {
    25  	dirPath string
    26  }
    27  
    28  func (fc *fileCache) path(key Key) string {
    29  	return path.Join(fc.dirPath, hex.EncodeToString(key[:]))
    30  }
    31  
    32  func (fc *fileCache) Get(key Key) (content io.ReadCloser, ok bool, err error) {
    33  	f, err := os.Open(fc.path(key))
    34  	if errors.Is(err, os.ErrNotExist) {
    35  		return nil, false, nil
    36  	} else if err != nil {
    37  		return nil, false, err
    38  	} else {
    39  		return f, true, nil
    40  	}
    41  }
    42  
    43  func (fc *fileCache) Add(key Key, content io.Reader) (err error) {
    44  	path := fc.path(key)
    45  	dirPath, fileName := filepath.Split(path)
    46  
    47  	file, err := os.CreateTemp(dirPath, fileName+".*.tmp")
    48  	if err != nil {
    49  		return
    50  	}
    51  	defer func() {
    52  		file.Close()
    53  		if err != nil {
    54  			_ = os.Remove(file.Name())
    55  		}
    56  	}()
    57  	if _, err = io.Copy(file, content); err != nil {
    58  		return
    59  	}
    60  	if err = file.Sync(); err != nil {
    61  		return
    62  	}
    63  	if err = file.Close(); err != nil {
    64  		return
    65  	}
    66  	err = os.Rename(file.Name(), path)
    67  	return
    68  }
    69  
    70  func (fc *fileCache) Delete(key Key) (err error) {
    71  	err = os.Remove(fc.path(key))
    72  	if errors.Is(err, os.ErrNotExist) {
    73  		err = nil
    74  	}
    75  	return
    76  }