124 lines
2.2 KiB
Go
124 lines
2.2 KiB
Go
package walker
|
|
|
|
import (
|
|
"fmt"
|
|
"io/fs"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"dirmd/internal/config"
|
|
"dirmd/internal/filter"
|
|
)
|
|
|
|
type Entry struct {
|
|
RelPath string
|
|
Size int64
|
|
}
|
|
|
|
type Result struct {
|
|
Entries []Entry
|
|
AbsRoot string
|
|
SingleFile bool
|
|
}
|
|
|
|
func Run(cfg *config.Config) (*Result, error) {
|
|
if cfg.SingleFile {
|
|
return processSingleFile(cfg)
|
|
}
|
|
return walkDirectory(cfg)
|
|
}
|
|
|
|
func processSingleFile(cfg *config.Config) (*Result, error) {
|
|
info, err := os.Stat(cfg.AbsRoot)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot access %s: %w", cfg.AbsRoot, err)
|
|
}
|
|
|
|
if info.IsDir() {
|
|
return walkDirectory(cfg)
|
|
}
|
|
|
|
flt := &filter.Filter{
|
|
MaxSize: cfg.MaxSize,
|
|
Extensions: cfg.Extensions,
|
|
Ignores: cfg.Ignores,
|
|
}
|
|
|
|
if flt.ShouldSkipFile(cfg.AbsRoot, filepath.Base(cfg.AbsRoot), info) {
|
|
return nil, fmt.Errorf("file %s filtered out", cfg.AbsRoot)
|
|
}
|
|
|
|
return &Result{
|
|
Entries: []Entry{{RelPath: filepath.Base(cfg.AbsRoot), Size: info.Size()}},
|
|
AbsRoot: filepath.Dir(cfg.AbsRoot),
|
|
SingleFile: true,
|
|
}, nil
|
|
|
|
}
|
|
|
|
func walkDirectory(cfg *config.Config) (*Result, error) {
|
|
flt := &filter.Filter{
|
|
MaxSize: cfg.MaxSize,
|
|
Extensions: cfg.Extensions,
|
|
Ignores: cfg.Ignores,
|
|
}
|
|
|
|
result := &Result{AbsRoot: cfg.AbsRoot}
|
|
|
|
err := filepath.WalkDir(cfg.AbsRoot, func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "warning: %v\n", err)
|
|
return nil
|
|
}
|
|
|
|
rel, err := filepath.Rel(cfg.AbsRoot, path)
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
depth := strings.Count(rel, string(filepath.Separator))
|
|
if rel != "." {
|
|
depth++
|
|
}
|
|
|
|
if depth > cfg.MaxDepth {
|
|
if d.IsDir() {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if filter.IsSymlink(path) {
|
|
if d.IsDir() {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
|
|
base := filepath.Base(path)
|
|
|
|
if d.IsDir() {
|
|
if rel != "." && flt.ShouldSkipDir(base) {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
|
|
info, err := d.Info()
|
|
if err != nil {
|
|
fmt.Fprintf(os.Stderr, "warning: cannot stat %s: %v\n", rel, err)
|
|
return nil
|
|
}
|
|
|
|
if flt.ShouldSkipFile(path, base, info) {
|
|
return nil
|
|
}
|
|
|
|
result.Entries = append(result.Entries, Entry{RelPath: rel, Size: info.Size()})
|
|
return nil
|
|
})
|
|
|
|
return result, err
|
|
}
|