177 lines
4.4 KiB
Go
177 lines
4.4 KiB
Go
// Copyright 2024 Matthew Rich <matthewrich.conf@gmail.com>. 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
|
|
}
|