131 lines
3.0 KiB
Go
131 lines
3.0 KiB
Go
package tree
|
|
|
|
import (
|
|
"fmt"
|
|
"io/fs"
|
|
"path/filepath"
|
|
"slices"
|
|
"strings"
|
|
|
|
"git.flo.fo/FLO/dirmd/internal/config"
|
|
"git.flo.fo/FLO/dirmd/internal/filter"
|
|
)
|
|
|
|
func BuildFullTree(absRoot string, cfg *config.Config) (*Node, error) {
|
|
root := &Node{IsDir: true, Children: make(map[string]*Node)}
|
|
|
|
err := filepath.WalkDir(absRoot, func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil {
|
|
return nil
|
|
}
|
|
|
|
rel, rerr := filepath.Rel(absRoot, path)
|
|
if rerr != nil || rel == "." {
|
|
return nil
|
|
}
|
|
|
|
if filter.IsSymlink(path) {
|
|
return nil
|
|
}
|
|
|
|
depth := strings.Count(rel, string(filepath.Separator)) + 1
|
|
if depth > cfg.MaxDepth {
|
|
node := addPath(root, rel, d.IsDir())
|
|
node.Annotation = "exceeded --max-depth"
|
|
if d.IsDir() {
|
|
return filepath.SkipDir
|
|
}
|
|
return nil
|
|
}
|
|
|
|
name := filepath.Base(path)
|
|
|
|
if d.IsDir() {
|
|
if strings.HasPrefix(name, ".") {
|
|
node := addPath(root, rel, true)
|
|
node.Annotation = "ignored (default)"
|
|
return filepath.SkipDir
|
|
}
|
|
if annotation, ok := ignoreReason(name, cfg.Ignores); ok {
|
|
node := addPath(root, rel, true)
|
|
node.Annotation = annotation
|
|
return filepath.SkipDir
|
|
}
|
|
if cfg.OmitTest && filter.IsTestDir(rel) {
|
|
node := addPath(root, rel, true)
|
|
node.Annotation = "tests omitted (--omit-test)"
|
|
return filepath.SkipDir
|
|
}
|
|
addPath(root, rel, true)
|
|
return nil
|
|
}
|
|
|
|
if annotation, ok := fileAnnotation(rel, name, cfg); ok {
|
|
node := addPath(root, rel, false)
|
|
node.Annotation = annotation
|
|
return nil
|
|
}
|
|
|
|
addPath(root, rel, false)
|
|
return nil
|
|
})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("cannot walk %s: %w", absRoot, err)
|
|
}
|
|
|
|
return root, nil
|
|
}
|
|
|
|
func ignoreReason(name string, ignores []string) (string, bool) {
|
|
for _, pattern := range ignores {
|
|
if matched, _ := filepath.Match(pattern, name); matched {
|
|
if slices.Contains(config.DefaultIgnores, pattern) {
|
|
return "ignored (default)", true
|
|
}
|
|
return "ignored (--ignore)", true
|
|
}
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
func fileAnnotation(rel, name string, cfg *config.Config) (string, bool) {
|
|
if strings.HasPrefix(name, ".") {
|
|
return "", false
|
|
}
|
|
if annotation, ok := ignoreReason(name, cfg.Ignores); ok {
|
|
return annotation, true
|
|
}
|
|
if cfg.OmitTest && filter.IsTestFile(rel, name) {
|
|
return "tests omitted (--omit-test)", true
|
|
}
|
|
if cfg.OmitMd && filter.IsNonReadmeMarkdown(rel, name) {
|
|
return "omitted (--omit-md)", true
|
|
}
|
|
if cfg.FrontendOnly && !filter.IsFrontendOrReadme(rel, name) {
|
|
return "omitted (--frontend-only)", true
|
|
}
|
|
if cfg.SkipFrontend && filter.IsFrontendExt(rel) {
|
|
return "omitted (--skip-frontend)", true
|
|
}
|
|
return "", false
|
|
}
|
|
|
|
func RenderBreadcrumb(absRoot, relPath string) string {
|
|
var sb strings.Builder
|
|
rootName := filepath.Base(absRoot)
|
|
dir := filepath.Dir(relPath)
|
|
if dir == "." {
|
|
return rootName + "/" + filepath.Base(relPath)
|
|
}
|
|
|
|
parts := strings.Split(filepath.ToSlash(dir), "/")
|
|
sb.WriteString(rootName)
|
|
for _, part := range parts {
|
|
sb.WriteString(" / ")
|
|
sb.WriteString(part)
|
|
}
|
|
sb.WriteString(" / ")
|
|
sb.WriteString(filepath.Base(relPath))
|
|
return sb.String()
|
|
}
|