Files
dirmd/internal/walker/walker.go
T
2026-09-15 11:16:27 +01:00

155 lines
3.2 KiB
Go

package walker
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"strings"
"git.flo.fo/FLO/dirmd/internal/config"
"git.flo.fo/FLO/dirmd/internal/filter"
"git.flo.fo/FLO/dirmd/internal/git"
"git.flo.fo/FLO/dirmd/internal/instructions"
)
type Entry struct {
RelPath string
Size int64
}
type Result struct {
Entries []Entry
AbsRoot string
SingleFile bool
OmitTest bool
Cfg *config.Config
Commit *git.CommitInfo
Instructions string
}
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,
OmitTest: cfg.OmitTest,
FrontendOnly: cfg.FrontendOnly,
OmitMd: cfg.OmitMd,
}
if flt.ShouldSkipFile(cfg.AbsRoot, filepath.Base(cfg.AbsRoot), info) {
return nil, fmt.Errorf("file %s filtered out", cfg.AbsRoot)
}
commitInfo := git.GetCommitInfo(filepath.Dir(cfg.AbsRoot))
var instContent string
if cfg.IncludeInstructions {
instContent, _ = instructions.Read(filepath.Dir(cfg.AbsRoot))
}
return &Result{
Entries: []Entry{{RelPath: filepath.Base(cfg.AbsRoot), Size: info.Size()}},
AbsRoot: filepath.Dir(cfg.AbsRoot),
SingleFile: true,
Cfg: cfg,
Commit: commitInfo,
Instructions: instContent,
}, nil
}
func walkDirectory(cfg *config.Config) (*Result, error) {
flt := &filter.Filter{
MaxSize: cfg.MaxSize,
Extensions: cfg.Extensions,
Ignores: cfg.Ignores,
OmitTest: cfg.OmitTest,
FrontendOnly: cfg.FrontendOnly,
OmitMd: cfg.OmitMd,
}
result := &Result{AbsRoot: cfg.AbsRoot, Cfg: cfg, OmitTest: cfg.OmitTest}
result.Commit = git.GetCommitInfo(cfg.AbsRoot)
if cfg.IncludeInstructions {
instContent, err := instructions.Read(cfg.AbsRoot)
if err != nil {
fmt.Fprintf(os.Stderr, "warning: could not read .dirmd: %v\n", err)
} else {
result.Instructions = instContent
}
}
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
}