Optimize walkdir for pages
This commit is contained in:
@@ -1,8 +1,8 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/fs"
|
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -44,20 +44,21 @@ func (h *handler) handleMove(w http.ResponseWriter, r *http.Request, srcURL, src
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Phase 1: walk the tree and rewrite every index.md whose content changes.
|
// Phase 1: walk the tree and rewrite every index.md that references the
|
||||||
// Keep the pre-rewrite bytes in memory so we can revert on failure.
|
// moved path. Keep the pre-rewrite bytes in memory so we can revert on
|
||||||
|
// failure. The walker only reads directory listings and files literally
|
||||||
|
// named index.md; hidden directories are pruned. A cheap substring check
|
||||||
|
// skips parsing files that cannot contain a relevant link.
|
||||||
rewritten := map[string][]byte{}
|
rewritten := map[string][]byte{}
|
||||||
walkErr := filepath.WalkDir(h.root, func(fsPath string, d fs.DirEntry, err error) error {
|
needle := []byte("[[" + oldPath)
|
||||||
if err != nil {
|
walkErr := walkIndexFiles(h.root, func(fsPath string) error {
|
||||||
return err
|
|
||||||
}
|
|
||||||
if d.IsDir() || d.Name() != "index.md" {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
orig, err := os.ReadFile(fsPath)
|
orig, err := os.ReadFile(fsPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
if !bytes.Contains(orig, needle) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
updated, changed := rewriteWikiLinks(orig, oldPath, newPath)
|
updated, changed := rewriteWikiLinks(orig, oldPath, newPath)
|
||||||
if !changed {
|
if !changed {
|
||||||
return nil
|
return nil
|
||||||
@@ -186,6 +187,36 @@ func rollbackRewrites(rewritten map[string][]byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// walkIndexFiles visits every `index.md` under root, skipping hidden
|
||||||
|
// directories (names beginning with `.`). Unlike filepath.WalkDir this does
|
||||||
|
// not stat each regular file — on spinning disks that saves the bulk of the
|
||||||
|
// traversal cost when folders contain many non-page files (photos, archives).
|
||||||
|
func walkIndexFiles(root string, visit func(fsPath string) error) error {
|
||||||
|
entries, err := os.ReadDir(root)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, e := range entries {
|
||||||
|
name := e.Name()
|
||||||
|
if strings.HasPrefix(name, ".") {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
full := filepath.Join(root, name)
|
||||||
|
if e.IsDir() {
|
||||||
|
if err := walkIndexFiles(full, visit); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if name == "index.md" {
|
||||||
|
if err := visit(full); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
// writeFileAtomic writes data to a temp file in the same directory as path
|
// writeFileAtomic writes data to a temp file in the same directory as path
|
||||||
// and renames it into place so readers never observe a partial file.
|
// and renames it into place so readers never observe a partial file.
|
||||||
func writeFileAtomic(path string, data []byte, perm os.FileMode) error {
|
func writeFileAtomic(path string, data []byte, perm os.FileMode) error {
|
||||||
|
|||||||
Reference in New Issue
Block a user