Files
kandev/apps/backend/internal/system/disk/walker.go
T

86 lines
2.6 KiB
Go

// Package disk computes a lazy, cached breakdown of the on-disk kandev data
// footprint (data dir, worktrees, repos, sessions, tasks, quick-chat, and
// backups) for the System -> Status page. The walk is tolerant of permission
// errors and surfaces them as Warnings on the returned Breakdown.
package disk
import (
"fmt"
"io/fs"
"os"
"path/filepath"
)
// subdir describes a logical bucket of the breakdown — its display name (the
// JSON / breakdown field) and the absolute path on disk it covers.
type subdir struct {
name string
path string
}
// subdirsFor returns the list of subdirectories that contribute to the
// breakdown for a given home directory. The backups subdir lives under
// <home>/data/backups; the rest are direct children of <home>.
func subdirsFor(homeDir string) []subdir {
return []subdir{
{name: "data_dir", path: filepath.Join(homeDir, "data")},
{name: "worktrees", path: filepath.Join(homeDir, "worktrees")},
{name: "repos", path: filepath.Join(homeDir, "repos")},
{name: "sessions", path: filepath.Join(homeDir, "sessions")},
{name: "tasks", path: filepath.Join(homeDir, "tasks")},
{name: "quick_chat", path: filepath.Join(homeDir, "quick-chat")},
{name: "backups", path: filepath.Join(homeDir, "data", "backups")},
}
}
// walkResult is the per-subdir output of walkSubdir.
type walkResult struct {
bytes int64
warnings []string
}
// walkSubdir recursively sums regular file sizes under root. Permission
// errors on subdirectories are recorded into warnings and the walk
// continues with the next sibling. A missing root (os.IsNotExist) yields a
// zero-byte result with no warning — empty installs are normal.
func walkSubdir(root string) walkResult {
info, err := os.Stat(root)
if err != nil {
if os.IsNotExist(err) {
return walkResult{}
}
return walkResult{warnings: []string{fmt.Sprintf("%s: %s", root, err)}}
}
if !info.IsDir() {
// Treat a regular file as its own size; uncommon but cheap to handle.
return walkResult{bytes: info.Size()}
}
var result walkResult
err = filepath.WalkDir(root, func(path string, d fs.DirEntry, walkErr error) error {
if walkErr != nil {
result.warnings = append(result.warnings, fmt.Sprintf("%s: %s", path, walkErr))
if d != nil && d.IsDir() {
return fs.SkipDir
}
return nil
}
if d.IsDir() {
return nil
}
fi, infoErr := d.Info()
if infoErr != nil {
result.warnings = append(result.warnings, fmt.Sprintf("%s: %s", path, infoErr))
return nil
}
if fi.Mode().IsRegular() {
result.bytes += fi.Size()
}
return nil
})
if err != nil {
result.warnings = append(result.warnings, fmt.Sprintf("%s: %s", root, err))
}
return result
}