drop storage interface (only one implementation)
1 file changed, 79 insertions(+), 0 deletions(-)
changed files
M shared/storage/file.go → shared/storage/file.go
@@ -2,10 +2,17 @@ package storage import ( "errors" + "fmt" + "hash/fnv" + "io" "mime" "os" "path/filepath" + "strings" "time" + + "github.com/Southclaws/fault" + "github.com/Southclaws/fault/fmsg" ) type File struct {@@ -21,6 +28,12 @@ } var ( ErrEncodingNotFound = errors.New("encoding not found") + + encodings = map[string]string{ + "br": ".br", + "gzip": ".gz", + "zstd": ".zstd", + } ) func NewFile(urlPath string, filename string) (*File, error) {@@ -68,3 +81,69 @@ f.ContentType = mime.TypeByExtension(ext) return f.ContentType } + +func (r *Reader) OpenFile(path string, filename string) (*File, error) { + file, err := NewFile(path, filename) + if err != nil { + return nil, fault.Wrap(err, fmsg.With("could not init file")) + } + + file.Etag, err = etag(file.Encodings["identity"]) + if err != nil { + return nil, err + } + + for enc, suffix := range encodings { + _, err := os.Stat(filename + suffix) + if err != nil { + if errors.Is(err, os.ErrNotExist) { + continue + } + + return nil, fault.Wrap( + err, + fmsg.With(fmt.Sprintf("could not stat file %s", filename+suffix)), + ) + } + f, err := os.Open(filename + suffix) + if err != nil { + return nil, fault.Wrap( + err, + fmsg.With(fmt.Sprintf("could not read file %s", filename+suffix)), + ) + } + file.Encodings[enc] = f + } + + return file, nil +} + +func etag(f io.Reader) (string, error) { + hash := fnv.New64a() + if _, err := io.Copy(hash, f); err != nil { + return "", fault.Wrap(err, fmsg.With("could not hash file")) + } + + return fmt.Sprintf(`W/"%x"`, hash.Sum(nil)), nil +} + +func pathNameToFileName(pathname string) string { + if strings.HasSuffix(pathname, "/") { + pathname += "index.html" + } + + return strings.TrimPrefix(pathname, "/") +} + +func cutSuffix(s, suffix string) string { + out, _ := strings.CutSuffix(s, suffix) + + return out +} + +func fileNameToPathName(filename string) string { + return cutSuffix( + cutSuffix(filename, "index.html"), + ".html", + ) +}