// Copyright 2024 Matthew Rich . All rights reserved. package fan import ( "context" _ "encoding/json" "fmt" _ "gopkg.in/yaml.v3" "net/url" "path/filepath" "decl/internal/data" "decl/internal/folio" "os" _ "io" "log/slog" "decl/internal/fs" ) type Dir struct { Path string `yaml:"path" json:"path"` Relative bool `yaml:"relative" json:"relative"` subDirsStack []string `yaml:"-" json:"-"` fs *fs.WalkDir `yaml:"-" json:"-"` } func NewDir() *Dir { return &Dir{ subDirsStack: make([]string, 0, 100), } } func init() { folio.DocumentRegistry.ConverterTypes.Register([]string{"file"}, func(u *url.URL) data.Converter { t := NewDir() t.Path,_ = filepath.Abs(filepath.Join(u.Hostname(), u.Path)) t.Relative = false return t }) } func (d *Dir) SetRelative(flag bool) { d.Relative = flag } func (d *Dir) Type() data.TypeName { return "dir" } func (d *Dir) ExtractDirectory(path string, document data.Document) (err error) { ctx := context.Background() files, readDirErr := os.ReadDir(path) slog.Info("fan.Dir.ExtractDirectory()", "path", path, "error", readDirErr) if readDirErr != nil { return readDirErr } for _,file := range files { filePath := filepath.Join(path, file.Name()) u := fmt.Sprintf("file://%s", filePath) var f data.Resource if f, err = document.NewResource(u); err != nil { return } if _, err = f.Read(ctx); err != nil { return } if file.IsDir() { d.subDirsStack = append(d.subDirsStack, filePath) } } return nil } func (d *Dir) isParent(m *map[string]int, path string, containingDirectoryPath string) (newCDP string, cdpCount int) { newCDP = containingDirectoryPath cdpCount = (*m)[containingDirectoryPath] pathLen := len(path) for i, p := range path { if p == '/' || i == pathLen { sPath := path[:i] if len(sPath) > 0 { (*m)[sPath]++ superDirCount := (*m)[sPath] if superDirCount >= cdpCount { newCDP = sPath cdpCount = superDirCount } } } } return } func (d *Dir) LCPath(files []string) (lcPath string) { parentPaths := make(map[string]int) var containingDirectoryPath string for _,filePath := range files { containingDirectoryPath, _ = d.isParent(&parentPaths, filePath, containingDirectoryPath) } lcPath = containingDirectoryPath return } func (d *Dir) Emit(document data.Document, filter data.ElementSelector) (resourceTarget data.Resource, err error) { if document == nil || document.Len() <= 0 { return nil, ErrEmptyDocument } dirFileDeclaration := folio.NewDeclaration() dirFileDeclaration.Type = "file" if err = dirFileDeclaration.NewResource(nil); err != nil { return } parentPaths := make(map[string]int) var containingDirectoryPath string for _,res := range document.Filter(func(d data.Declaration) bool { return d.ResourceType() == "file" }) { var f data.FileResource = res.(*folio.Declaration).Attributes.(data.FileResource) var parent string if f.FileInfo().IsDir() { parent, err = filepath.Abs(f.FilePath()) } else { parent, err = filepath.Abs(filepath.Dir(f.FilePath())) } if err != nil { return } containingDirectoryPath, _ = d.isParent(&parentPaths, parent, containingDirectoryPath) } uri := fmt.Sprintf("file://%s", containingDirectoryPath) if err = dirFileDeclaration.SetURI(uri); err != nil { return } resourceTarget = dirFileDeclaration.Attributes return } func (d *Dir) Extract(resourceSource data.Resource, filter data.ElementSelector) (document data.Document, err error) { ctx := context.Background() if resourceSource.Type() != "file" { return nil, fmt.Errorf("%w", ErrInvalidResource) } slog.Info("fan.Dir.Extract()", "path", d.Path, "resource", resourceSource) d.Path = resourceSource.(data.FileResource).FilePath() document = folio.DocumentRegistry.NewDocument("") d.fs = fs.NewWalkDir(os.DirFS(d.Path), d.Path, func(fsys fs.FS, path string, file fs.DirEntry) (err error) { u := fmt.Sprintf("file://%s", path) slog.Info("Fan.Dir.Extract() WalkDir", "file", u, "root", d.Path) if path != "" { var f data.Resource if f, err = document.NewResource(u); err != nil { return } if d.Relative { f.(data.FileResource).SetBasePath(len(d.Path) + 1) slog.Info("Fan.Dir.Extract() WalkDir Relative", "file", f, "path", path) } slog.Info("Fan.Dir.Extract() WalkDir Resource.Read", "file", f) _, err = f.Read(ctx) } return }) slog.Info("Fan.Dir.Extract()", "fs", d.fs) err = d.fs.Walk(nil) return } func (d *Dir) Close() error { return nil }