package mount import ( "os" "path/filepath" "sync" ) /* BlobCache provides persistent storage for downloaded blobs. * Uses ~/.cache/larc/blobs/{prefix}/{hash} structure. * Thread-safe for concurrent access. */ type BlobCache struct { dir string mu sync.RWMutex } // NewBlobCache creates a cache in ~/.cache/larc/blobs func NewBlobCache() (*BlobCache, error) { home, err := os.UserHomeDir() if err != nil { return nil, err } dir := filepath.Join(home, ".cache", "larc", "blobs") if err := os.MkdirAll(dir, 0755); err != nil { return nil, err } return &BlobCache{dir: dir}, nil } // NewBlobCacheAt creates a cache at a specific path func NewBlobCacheAt(dir string) (*BlobCache, error) { if err := os.MkdirAll(dir, 0755); err != nil { return nil, err } return &BlobCache{dir: dir}, nil } func (c *BlobCache) path(hash string) string { if len(hash) < 2 { return filepath.Join(c.dir, hash) } return filepath.Join(c.dir, hash[:2], hash) } // Get retrieves blob from cache, returns nil if not found func (c *BlobCache) Get(hash string) ([]byte, bool) { c.mu.RLock() defer c.mu.RUnlock() data, err := os.ReadFile(c.path(hash)) if err != nil { return nil, false } return data, true } // Put stores blob in cache func (c *BlobCache) Put(hash string, data []byte) error { c.mu.Lock() defer c.mu.Unlock() p := c.path(hash) if err := os.MkdirAll(filepath.Dir(p), 0755); err != nil { return err } return os.WriteFile(p, data, 0644) } // Exists checks if blob is cached func (c *BlobCache) Exists(hash string) bool { c.mu.RLock() defer c.mu.RUnlock() _, err := os.Stat(c.path(hash)) return err == nil } // Dir returns cache directory path func (c *BlobCache) Dir() string { return c.dir }