package cas import ( "crypto/sha256" "encoding/hex" "errors" "io" "os" "path/filepath" ) type Store struct{ dir string } func New(dir string) *Store { os.MkdirAll(dir, 0o755); return &Store{dir: dir} } func (s *Store) pathOf(hash string) string { if len(hash) < 2 { return filepath.Join(s.dir, hash) } return filepath.Join(s.dir, hash[:2], hash) } func (s *Store) Has(hash string) bool { _, err := os.Stat(s.pathOf(hash)); return err == nil } func (s *Store) PutStream(r io.Reader) (string, error) { h := sha256.New() tmp, err := os.CreateTemp(s.dir, "put-*.tmp") if err != nil { return "", err } defer os.Remove(tmp.Name()) mw := io.MultiWriter(h, tmp) if _, err := io.Copy(mw, r); err != nil { tmp.Close() return "", err } tmp.Close() sum := hex.EncodeToString(h.Sum(nil)) dst := s.pathOf(sum) if err := os.MkdirAll(filepath.Dir(dst), 0o755); err != nil { return "", err } if err := os.Rename(tmp.Name(), dst); err != nil { return "", err } return sum, nil } func (s *Store) Get(hash string, w io.Writer) error { f, err := os.Open(s.pathOf(hash)) if err != nil { return errors.New("not found") } defer f.Close() _, err = io.Copy(w, f) return err }