lure/internal/dl/dl.go

132 lines
2.2 KiB
Go

package dl
import (
"context"
"io"
"os"
"path/filepath"
"go.arsenm.dev/logger/log"
"go.arsenm.dev/lure/internal/dlcache"
)
var Downloaders = []Downloader{
FileDownloader{},
}
type Type uint8
const (
TypeFile Type = iota
TypeDir
)
func (t Type) String() string {
switch t {
case TypeFile:
return "file"
case TypeDir:
return "dir"
}
return "<unknown>"
}
type Options struct {
ID string
Name string
URL string
Destination string
Progress io.Writer
}
type Downloader interface {
Name() string
Type() Type
MatchURL(string) bool
Download(Options) error
}
type UpdatingDownloader interface {
Downloader
Update(Options) error
}
func Download(ctx context.Context, opts Options) error {
d := getDownloader(opts.URL)
cacheDir, ok := dlcache.Get(opts.ID)
if ok {
ok, err := handleCache(cacheDir, opts.Destination, d.Type())
if err != nil {
return err
}
if ok {
log.Info("Source found in cache, linked to destination").Str("source", opts.Name).Stringer("type", d.Type()).Send()
return nil
}
}
log.Info("Downloading source").Str("source", opts.Name).Str("downloader", d.Name()).Send()
cacheDir, err := dlcache.New(opts.ID)
if err != nil {
return err
}
err = d.Download(Options{
Name: opts.Name,
URL: opts.URL,
Destination: cacheDir,
Progress: opts.Progress,
})
if err != nil {
return err
}
_, err = handleCache(cacheDir, opts.Destination, d.Type())
return err
}
func handleCache(cacheDir, dest string, t Type) (bool, error) {
switch t {
case TypeFile:
cd, err := os.Open(cacheDir)
if err != nil {
return false, err
}
names, err := cd.Readdirnames(1)
if err != nil && err != io.EOF {
return false, err
}
// If the cache dir contains no files,
// assume there is no cache entry
if len(names) == 0 {
break
}
err = os.Link(filepath.Join(cacheDir, names[0]), filepath.Join(dest, filepath.Base(names[0])))
if err != nil {
return false, err
}
return true, nil
case TypeDir:
err := os.Link(cacheDir, dest)
if err != nil {
return false, err
}
return true, nil
}
return false, nil
}
func getDownloader(u string) Downloader {
for _, d := range Downloaders {
if d.MatchURL(u) {
return d
}
}
return nil
}