package store import ( "bytes" "crypto/sha256" "encoding/hex" "fmt" "io" "os" "path/filepath" ) type Record struct { Hash string Path string Size int64 } type ContentStore struct { root string } func New(root string) (*ContentStore, error) { if err := os.MkdirAll(root, 0o755); err != nil { return nil, fmt.Errorf("create content store root: %w", err) } return &ContentStore{root: root}, nil } func (s *ContentStore) PutBytes(content []byte) (Record, error) { return s.put(bytes.NewReader(content)) } func (s *ContentStore) PutReader(reader io.Reader) (Record, error) { return s.put(reader) } func (s *ContentStore) Read(hash string) ([]byte, error) { return os.ReadFile(s.PathForHash(hash)) } func (s *ContentStore) PathForHash(hash string) string { return filepath.Join(s.root, hash[0:2], hash[2:4], hash) } func (s *ContentStore) put(reader io.Reader) (Record, error) { content, err := io.ReadAll(reader) if err != nil { return Record{}, fmt.Errorf("read content: %w", err) } sum := sha256.Sum256(content) hash := hex.EncodeToString(sum[:]) target := s.PathForHash(hash) if err := os.MkdirAll(filepath.Dir(target), 0o755); err != nil { return Record{}, fmt.Errorf("create content directory: %w", err) } if _, err := os.Stat(target); err == nil { return Record{Hash: hash, Path: target, Size: int64(len(content))}, nil } if err := os.WriteFile(target, content, 0o644); err != nil { return Record{}, fmt.Errorf("write content file: %w", err) } return Record{Hash: hash, Path: target, Size: int64(len(content))}, nil }