1
0
mirror of https://github.com/tw93/Mole.git synced 2026-02-04 19:09:43 +00:00
Files
Mole/cmd/analyze/delete.go

127 lines
2.9 KiB
Go

package main
import (
"io/fs"
"os"
"path/filepath"
"sort"
"strings"
"sync/atomic"
tea "github.com/charmbracelet/bubbletea"
)
func deletePathCmd(path string, counter *int64) tea.Cmd {
return func() tea.Msg {
count, err := deletePathWithProgress(path, counter)
return deleteProgressMsg{
done: true,
err: err,
count: count,
path: path,
}
}
}
// deleteMultiplePathsCmd deletes multiple paths and returns combined results
func deleteMultiplePathsCmd(paths []string, counter *int64) tea.Cmd {
return func() tea.Msg {
var totalCount int64
var errors []string
// Delete deeper paths first to avoid parent removal triggering child not-exist errors
pathsToDelete := append([]string(nil), paths...)
sort.Slice(pathsToDelete, func(i, j int) bool {
return strings.Count(pathsToDelete[i], string(filepath.Separator)) > strings.Count(pathsToDelete[j], string(filepath.Separator))
})
for _, path := range pathsToDelete {
count, err := deletePathWithProgress(path, counter)
totalCount += count
if err != nil {
if os.IsNotExist(err) {
continue // Parent already removed - not an actionable error
}
errors = append(errors, err.Error())
}
}
var resultErr error
if len(errors) > 0 {
resultErr = &multiDeleteError{errors: errors}
}
// Return empty path to trigger full refresh since multiple items were deleted
return deleteProgressMsg{
done: true,
err: resultErr,
count: totalCount,
path: "", // Empty path signals multiple deletions
}
}
}
// multiDeleteError holds multiple deletion errors
type multiDeleteError struct {
errors []string
}
func (e *multiDeleteError) Error() string {
if len(e.errors) == 1 {
return e.errors[0]
}
return strings.Join(e.errors[:min(3, len(e.errors))], "; ")
}
func deletePathWithProgress(root string, counter *int64) (int64, error) {
var count int64
var firstErr error
err := filepath.WalkDir(root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
// Skip permission errors but continue walking
if os.IsPermission(err) {
if firstErr == nil {
firstErr = err
}
return filepath.SkipDir
}
// For other errors, record and continue
if firstErr == nil {
firstErr = err
}
return nil
}
if !d.IsDir() {
if removeErr := os.Remove(path); removeErr == nil {
count++
if counter != nil {
atomic.StoreInt64(counter, count)
}
} else if firstErr == nil {
// Record first deletion error
firstErr = removeErr
}
}
return nil
})
// Track walk error separately
if err != nil && firstErr == nil {
firstErr = err
}
// Try to remove remaining directory structure
// Even if this fails, we still report files deleted
if removeErr := os.RemoveAll(root); removeErr != nil {
if firstErr == nil {
firstErr = removeErr
}
}
// Always return count (even if there were errors), along with first error
return count, firstErr
}