swadhinbiswas feat: initial release of VEET (Universal Linux App Uninstaller & Deep-Clean Residual Purger) · 8/15/2026
5e0888a
71 lines · 1.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package detector

import (
	"context"
	"os"
	"strings"

	"github.com/swadhinbiswas/veet/internal/model"
)

type cargoDetector struct {
	ex   Execer
	path string
}

// NewCargoDetector detects binaries installed via `cargo install`.
func NewCargoDetector(ex Execer) Detector {
	path, _ := ex.LookPath("cargo")
	return &cargoDetector{ex: ex, path: path}
}

func (d *cargoDetector) Source() string  { return "cargo" }
func (d *cargoDetector) Available() bool { return d.path != "" }

func (d *cargoDetector) Detect(ctx context.Context) ([]model.AppInfo, error) {
	out, err := Exec(ctx, d.ex, "cargo", "install", "--list")
	if err != nil {
		return nil, err
	}
	var apps []model.AppInfo
	curIdx := -1
	for _, line := range strings.Split(out, "\n") {
		trimmed := strings.TrimSpace(line)
		if trimmed == "" {
			curIdx = -1
			continue
		}
		indented := line != trimmed
		if !indented {
			name, ver, ok := strings.Cut(trimmed, " v")
			if !ok {
				continue
			}
			apps = append(apps, model.AppInfo{
				Name:    strings.TrimSpace(name),
				Version: strings.TrimSpace(ver),
				Source:  "cargo",
				Status:  "Installed",
			})
			curIdx = len(apps) - 1
		} else if strings.HasPrefix(trimmed, "/") {
			if curIdx < 0 {
				continue
			}
			if size, err := binSizeKB(trimmed); err == nil {
				apps[curIdx].InstallSizeKB += size
			}
		}
	}
	return apps, nil
}

// binSizeKB measures a single regular file in KiB.
func binSizeKB(p string) (int64, error) {
	info, err := os.Stat(p)
	if err != nil {
		return 0, err
	}
	return (info.Size() + 1023) / 1024, nil
}