2022-12-04 07:59:36 +00:00
|
|
|
package mfer
|
|
|
|
|
|
|
|
import (
|
2022-12-09 00:02:33 +00:00
|
|
|
"bytes"
|
|
|
|
"context"
|
|
|
|
"errors"
|
2022-12-04 07:59:36 +00:00
|
|
|
"fmt"
|
|
|
|
"io/fs"
|
2022-12-09 00:02:33 +00:00
|
|
|
"path"
|
2022-12-04 07:59:36 +00:00
|
|
|
"path/filepath"
|
|
|
|
"strings"
|
|
|
|
|
2022-12-09 00:02:33 +00:00
|
|
|
"git.eeqj.de/sneak/mfer/internal/log"
|
2022-12-04 07:59:36 +00:00
|
|
|
"github.com/spf13/afero"
|
|
|
|
)
|
|
|
|
|
2022-12-09 00:02:33 +00:00
|
|
|
type manifestFile struct {
|
|
|
|
path string
|
|
|
|
info fs.FileInfo
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
|
|
|
|
2022-12-09 00:02:33 +00:00
|
|
|
func (m *manifestFile) String() string {
|
|
|
|
return fmt.Sprintf("<File \"%s\">", m.path)
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
|
|
|
|
2022-12-09 00:02:33 +00:00
|
|
|
type manifest struct {
|
|
|
|
sourceFS []afero.Fs
|
|
|
|
files []*manifestFile
|
|
|
|
scanOptions *ManifestScanOptions
|
|
|
|
totalFileSize int64
|
|
|
|
pbInner *MFFile
|
|
|
|
pbOuter *MFFileOuter
|
|
|
|
output *bytes.Buffer
|
|
|
|
ctx context.Context
|
|
|
|
errors []*error
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
|
|
|
|
2022-12-09 00:02:33 +00:00
|
|
|
func (m *manifest) String() string {
|
|
|
|
return fmt.Sprintf("<Manifest count=%d totalSize=%d>", len(m.files), m.totalFileSize)
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
type ManifestScanOptions struct {
|
|
|
|
IgnoreDotfiles bool
|
|
|
|
FollowSymLinks bool
|
|
|
|
}
|
|
|
|
|
2022-12-09 00:02:33 +00:00
|
|
|
func (m *manifest) HasError() bool {
|
|
|
|
return len(m.errors) > 0
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *manifest) AddError(e error) *manifest {
|
|
|
|
m.errors = append(m.errors, &e)
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *manifest) WithContext(c context.Context) *manifest {
|
|
|
|
m.ctx = c
|
|
|
|
return m
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *manifest) addInputPath(inputPath string) error {
|
2022-12-04 07:59:36 +00:00
|
|
|
abs, err := filepath.Abs(inputPath)
|
|
|
|
if err != nil {
|
2022-12-09 00:02:33 +00:00
|
|
|
return err
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
2022-12-09 00:02:33 +00:00
|
|
|
// FIXME check to make sure inputPath/abs exists maybe
|
|
|
|
afs := afero.NewReadOnlyFs(afero.NewBasePathFs(afero.NewOsFs(), abs))
|
|
|
|
return m.addInputFS(afs)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *manifest) addInputFS(f afero.Fs) error {
|
|
|
|
if m.sourceFS == nil {
|
|
|
|
m.sourceFS = make([]afero.Fs, 0)
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
2022-12-09 00:02:33 +00:00
|
|
|
m.sourceFS = append(m.sourceFS, f)
|
|
|
|
// FIXME do some sort of check on f here?
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func New() *manifest {
|
|
|
|
m := &manifest{}
|
|
|
|
return m
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
|
|
|
|
2022-12-09 00:02:33 +00:00
|
|
|
func NewFromPaths(options *ManifestScanOptions, inputPaths ...string) (*manifest, error) {
|
|
|
|
log.Dump(inputPaths)
|
|
|
|
m := New()
|
|
|
|
m.scanOptions = options
|
|
|
|
for _, p := range inputPaths {
|
|
|
|
err := m.addInputPath(p)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
2022-12-09 00:02:33 +00:00
|
|
|
return m, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewFromFS(options *ManifestScanOptions, fs afero.Fs) (*manifest, error) {
|
|
|
|
m := New()
|
|
|
|
m.scanOptions = options
|
|
|
|
err := m.addInputFS(fs)
|
2022-12-04 07:59:36 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
return m, nil
|
|
|
|
}
|
|
|
|
|
2022-12-09 00:02:33 +00:00
|
|
|
func (m *manifest) GetFileCount() int64 {
|
|
|
|
return int64(len(m.files))
|
|
|
|
}
|
2022-12-04 07:59:36 +00:00
|
|
|
|
2022-12-09 00:02:33 +00:00
|
|
|
func (m *manifest) GetTotalFileSize() int64 {
|
|
|
|
return m.totalFileSize
|
|
|
|
}
|
2022-12-04 07:59:36 +00:00
|
|
|
|
2022-12-09 00:02:33 +00:00
|
|
|
func pathIsHidden(p string) bool {
|
|
|
|
tp := path.Clean(p)
|
|
|
|
if strings.HasPrefix(tp, ".") {
|
|
|
|
return true
|
|
|
|
}
|
|
|
|
for {
|
|
|
|
d, f := path.Split(tp)
|
|
|
|
if strings.HasPrefix(f, ".") {
|
|
|
|
return true
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
2022-12-09 00:02:33 +00:00
|
|
|
if d == "" {
|
|
|
|
return false
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
2022-12-09 00:02:33 +00:00
|
|
|
tp = d[0 : len(d)-1] // trim trailing slash from dir
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-12-09 00:02:33 +00:00
|
|
|
func (m *manifest) addFile(p string, fi fs.FileInfo, sfsIndex int) error {
|
|
|
|
if m.scanOptions.IgnoreDotfiles && pathIsHidden(p) {
|
|
|
|
return nil
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
2022-12-09 00:02:33 +00:00
|
|
|
if fi != nil && fi.IsDir() {
|
|
|
|
// manifests contain only files, directories are implied.
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
// FIXME test if 'fi' is already result of stat
|
|
|
|
fileinfo, staterr := m.sourceFS[sfsIndex].Stat(p)
|
|
|
|
if staterr != nil {
|
|
|
|
return staterr
|
|
|
|
}
|
|
|
|
cleanPath := p
|
|
|
|
if cleanPath[0:1] == "/" {
|
|
|
|
cleanPath = cleanPath[1:]
|
|
|
|
}
|
|
|
|
nf := &manifestFile{
|
|
|
|
path: cleanPath,
|
|
|
|
info: fileinfo,
|
|
|
|
}
|
|
|
|
m.files = append(m.files, nf)
|
|
|
|
m.totalFileSize = m.totalFileSize + fi.Size()
|
|
|
|
return nil
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|
|
|
|
|
2022-12-09 00:02:33 +00:00
|
|
|
func (m *manifest) Scan() error {
|
|
|
|
// FIXME scan and whatever function does the hashing should take ctx
|
|
|
|
for idx, sfs := range m.sourceFS {
|
|
|
|
if sfs == nil {
|
|
|
|
return errors.New("invalid source fs")
|
|
|
|
}
|
|
|
|
e := afero.Walk(sfs, "/", func(p string, info fs.FileInfo, err error) error {
|
|
|
|
return m.addFile(p, info, idx)
|
|
|
|
})
|
|
|
|
if e != nil {
|
|
|
|
return e
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
2022-12-04 07:59:36 +00:00
|
|
|
}
|