diff --git a/main.go b/main.go index 7b1be0a..5c1ecc2 100644 --- a/main.go +++ b/main.go @@ -41,6 +41,8 @@ func main() { err = unlinkCmd(args[1:]) case "remove": err = removeCmd(args[1:]) + case "status": + err = statusCmd() case "help", "-h", "--help": usage() return @@ -59,8 +61,35 @@ func usage() { fmt.Println("usage:") fmt.Println(" sigil apply [--prune]") fmt.Println(" sigil add ") - fmt.Println(" sigil unlink ") - fmt.Println(" sigil remove ") + fmt.Println(" sigil unlink [--dry-run]") + fmt.Println(" sigil remove [--dry-run]") + fmt.Println(" sigil status") +} + +type packageFlags struct { + dryRun bool +} + +func parsePackageFlags(args []string) (packageFlags, string, error) { + flags := packageFlags{} + var pkg string + for _, arg := range args { + if arg == "--dry-run" { + flags.dryRun = true + continue + } + if strings.HasPrefix(arg, "-") { + return flags, "", fmt.Errorf("unknown flag %q", arg) + } + if pkg != "" { + return flags, "", errors.New("too many arguments") + } + pkg = arg + } + if pkg == "" { + return flags, "", errors.New("missing package name") + } + return flags, pkg, nil } func applyCmd(args []string) error { @@ -133,7 +162,7 @@ func applyCmd(args []string) error { } if prune { - return removeLinks(stales) + return removeLinks(stales, false) } fmt.Printf("Stale links found: %d\n", len(stales)) @@ -142,7 +171,7 @@ func applyCmd(args []string) error { return err } if ok { - return removeLinks(stales) + return removeLinks(stales, false) } return nil @@ -246,11 +275,11 @@ func addCmd(args []string) error { } func unlinkCmd(args []string) error { - if len(args) < 1 { - return errors.New("missing package name") + flags, pkgName, err := parsePackageFlags(args) + if err != nil { + return err } - pkgName := args[0] repo, err := repoPath() if err != nil { return err @@ -273,15 +302,15 @@ func unlinkCmd(args []string) error { return err } - return restorePackage(filesDir, targetRoot) + return restorePackage(filesDir, targetRoot, flags.dryRun) } func removeCmd(args []string) error { - if len(args) < 1 { - return errors.New("missing package name") + flags, pkgName, err := parsePackageFlags(args) + if err != nil { + return err } - pkgName := args[0] repo, err := repoPath() if err != nil { return err @@ -304,10 +333,14 @@ func removeCmd(args []string) error { return err } - if err := restorePackage(filesDir, targetRoot); err != nil { + if err := restorePackage(filesDir, targetRoot, flags.dryRun); err != nil { return err } + if flags.dryRun { + return nil + } + return os.RemoveAll(pkgDir) } @@ -420,6 +453,53 @@ func loadConfig(path string) (*packageConfig, error) { return &packageConfig{targets: targets}, nil } +func statusCmd() error { + repo, err := repoPath() + if err != nil { + return err + } + + entries, err := os.ReadDir(repo) + if err != nil { + return err + } + + for _, entry := range entries { + if !entry.IsDir() || strings.HasPrefix(entry.Name(), ".") { + continue + } + + pkgDir := filepath.Join(repo, entry.Name()) + configPath := filepath.Join(pkgDir, configFileName) + cfg, err := loadConfig(configPath) + if err != nil { + return fmt.Errorf("%s: %w", entry.Name(), err) + } + + targetRoot, err := selectTarget(cfg.targets) + if err != nil { + return fmt.Errorf("%s: %w", entry.Name(), err) + } + + filesDir := filepath.Join(pkgDir, filesDirName) + stale, err := findStaleLinks(filesDir, targetRoot) + if err != nil { + return fmt.Errorf("%s: %w", entry.Name(), err) + } + if len(stale) == 0 { + fmt.Printf("%s: ok\n", entry.Name()) + continue + } + + fmt.Printf("%s: stale links (%d)\n", entry.Name(), len(stale)) + for _, path := range stale { + fmt.Printf(" %s\n", path) + } + } + + return nil +} + func selectTarget(targets map[string]string) (string, error) { osKey := runtime.GOOS if osKey == "darwin" { @@ -593,8 +673,12 @@ func findStaleLinks(filesDir, targetRoot string) ([]string, error) { return stale, nil } -func removeLinks(paths []string) error { +func removeLinks(paths []string, dryRun bool) error { for _, path := range paths { + if dryRun { + fmt.Printf("dry-run: remove %s\n", path) + continue + } if err := os.Remove(path); err != nil { return err } @@ -602,7 +686,7 @@ func removeLinks(paths []string) error { return nil } -func restorePackage(filesDir, targetRoot string) error { +func restorePackage(filesDir, targetRoot string, dryRun bool) error { filesAbs, err := filepath.Abs(filesDir) if err != nil { return err @@ -649,6 +733,11 @@ func restorePackage(filesDir, targetRoot string) error { return nil } + if dryRun { + fmt.Printf("dry-run: restore %s\n", targetPath) + return nil + } + if err := os.Remove(targetPath); err != nil { return err }