From 38d7935b315bc2be064edb1d81c1781f6b5afd7c Mon Sep 17 00:00:00 2001 From: "Thomas G. Lopes" Date: Thu, 19 Feb 2026 20:16:13 +0000 Subject: [PATCH] Allow unlink/remove by path --- main.go | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 2 deletions(-) diff --git a/main.go b/main.go index 5810932..c6581fa 100644 --- a/main.go +++ b/main.go @@ -114,6 +114,87 @@ func splitPackageSpec(spec string) (string, string, error) { return pkg, rel, nil } +func resolvePackageSpec(spec string) (string, string, error) { + repo, err := repoPath() + if err != nil { + return "", "", err + } + + repoAbs, err := filepath.Abs(repo) + if err != nil { + return "", "", err + } + + spec = expandHome(spec) + if filepath.IsAbs(spec) { + return resolvePathSpec(spec, repoAbs) + } + + clean := filepath.Clean(spec) + if strings.HasPrefix(clean, ".") || strings.HasPrefix(clean, string(os.PathSeparator)) { + return resolvePathSpec(clean, repoAbs) + } + + return splitPackageSpec(spec) +} + +func resolvePathSpec(pathSpec, repoAbs string) (string, string, error) { + absPath, err := filepath.Abs(pathSpec) + if err != nil { + return "", "", err + } + + if strings.HasPrefix(absPath, repoAbs+string(os.PathSeparator)) || absPath == repoAbs { + rel, err := filepath.Rel(repoAbs, absPath) + if err != nil { + return "", "", err + } + parts := strings.Split(rel, string(os.PathSeparator)) + if len(parts) >= 1 { + pkg := parts[0] + if len(parts) >= 2 && parts[1] == filesDirName { + relPath := filepath.Join(parts[2:]...) + return pkg, relPath, nil + } + return pkg, "", nil + } + } + + entries, err := os.ReadDir(repoAbs) + if err != nil { + return "", "", err + } + + for _, entry := range entries { + if !entry.IsDir() || strings.HasPrefix(entry.Name(), ".") { + continue + } + pkgDir := filepath.Join(repoAbs, entry.Name()) + configPath := filepath.Join(pkgDir, configFileName) + cfg, err := loadConfig(configPath) + if err != nil { + continue + } + targetRoot, err := selectTarget(cfg.targets) + if err != nil { + continue + } + absTarget, err := filepath.Abs(expandHome(targetRoot)) + if err != nil { + continue + } + if strings.HasPrefix(absPath, absTarget+string(os.PathSeparator)) || absPath == absTarget { + rel, err := filepath.Rel(absTarget, absPath) + if err != nil { + return "", "", err + } + return entry.Name(), rel, nil + } + } + + return "", "", fmt.Errorf("could not resolve %s to a package", pathSpec) +} + func applyCmd(args []string) error { prune := false for _, arg := range args { @@ -325,7 +406,7 @@ func unlinkCmd(args []string) error { return err } - pkgName, relPath, err := splitPackageSpec(pkgSpec) + pkgName, relPath, err := resolvePackageSpec(pkgSpec) if err != nil { return err } @@ -385,7 +466,7 @@ func removeCmd(args []string) error { return err } - pkgName, relPath, err := splitPackageSpec(pkgSpec) + pkgName, relPath, err := resolvePackageSpec(pkgSpec) if err != nil { return err }