99 lines
1.7 KiB
Go
99 lines
1.7 KiB
Go
// Copyright Earl Warren <contact@earl-warren.org>
|
|
// Copyright Loïc Dachary <loic@dachary.org>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
package objects
|
|
|
|
import (
|
|
"crypto/sha256"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"runtime"
|
|
)
|
|
|
|
type Interface interface {
|
|
Save(rc io.ReadCloser) (string, string)
|
|
}
|
|
|
|
type helper struct {
|
|
dir *string
|
|
}
|
|
|
|
func (o *helper) getDir() string {
|
|
if o.dir == nil {
|
|
dir, err := os.MkdirTemp("", "objectHelper")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
runtime.SetFinalizer(o, func(o *helper) {
|
|
err := os.RemoveAll(dir)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
})
|
|
o.dir = &dir
|
|
}
|
|
return *o.dir
|
|
}
|
|
|
|
func (o *helper) getPath(sha string) string {
|
|
return filepath.Join(o.getDir(), sha[0:2], sha[2:4], sha)
|
|
}
|
|
|
|
func (o *helper) Save(rc io.ReadCloser) (string, string) {
|
|
tempFile, err := os.CreateTemp("", "object")
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
tempRemoved := false
|
|
defer func() {
|
|
if !tempRemoved {
|
|
_ = os.Remove(tempFile.Name())
|
|
}
|
|
}()
|
|
|
|
// reader
|
|
defer rc.Close()
|
|
|
|
// writer to file
|
|
f, err := os.OpenFile(tempFile.Name(), os.O_CREATE|os.O_RDWR, 0o644)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
defer f.Close()
|
|
|
|
// writer to sha256
|
|
h := sha256.New()
|
|
|
|
// copy reader to file & sha256
|
|
w := io.MultiWriter(f, h)
|
|
if _, err := io.Copy(w, rc); err != nil {
|
|
panic(fmt.Errorf("while copying object: %w", err))
|
|
}
|
|
|
|
// finalize writer to sha256
|
|
sha := hex.EncodeToString(h.Sum(nil))
|
|
|
|
// finalize writer to file
|
|
if err := tempFile.Close(); err != nil {
|
|
panic(err)
|
|
}
|
|
path := o.getPath(sha)
|
|
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
|
|
panic(err)
|
|
}
|
|
if err := os.Rename(tempFile.Name(), path); err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
tempRemoved = true
|
|
|
|
return sha, path
|
|
}
|
|
|
|
func NewObjectsHelper() Interface {
|
|
return &helper{}
|
|
}
|