diff --git a/pyproject.toml b/pyproject.toml index ec97fa5..76ce6dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "resrm" -version = "0.2.0" +version = "0.2.1" description = "drop-in replacement for rm with undo/restore built-in." authors = ["Marco D'Aleo "] license = "GPL-3.0-or-later" diff --git a/src/resrm/core.py b/src/resrm/core.py index a34a47b..5201a9a 100644 --- a/src/resrm/core.py +++ b/src/resrm/core.py @@ -7,7 +7,7 @@ Basic usage: resrm -r dir # recursive remove (moves dir to trash) resrm -f file # ignore nonexistent, no prompt resrm -i file # interactive prompt before removal - resrm --perma file # permanent delete (bypass trash) + resrm --skip-trash file # permanent delete (bypass trash) resrm -l # list trash entries (neat table) resrm --restore # restore by short-id (8 chars) or exact basename resrm --empty # empty trash entries (permanent) @@ -116,6 +116,8 @@ def find_candidates(identifier: str) -> List[Dict]: if id_matches: return id_matches + return [] + def restore_many(identifiers: List[str]): """Restore multiple files, prompting when needed.""" for identifier in identifiers: @@ -216,7 +218,7 @@ def empty_trash(): save_meta(meta) print(f"Trash emptied ({count} entries removed).") -def move_to_trash(path: Path, interactive: bool, force: bool, recursive: bool, perma: bool): +def move_to_trash(path: Path, interactive: bool, force: bool, skip_trash: bool): if not path.exists(): if force: return @@ -224,7 +226,7 @@ def move_to_trash(path: Path, interactive: bool, force: bool, recursive: bool, p return # Interactive prompt - if interactive: + if interactive and not force: try: yn = input(f"remove '{path}'? [y/N] ").strip().lower() except KeyboardInterrupt: @@ -234,7 +236,7 @@ def move_to_trash(path: Path, interactive: bool, force: bool, recursive: bool, p return # Permanent delete path - if perma: + if skip_trash: try: if path.is_dir() and not path.is_symlink(): shutil.rmtree(path) @@ -305,20 +307,30 @@ def main(argv: Optional[List[str]] = None): parser = argparse.ArgumentParser(add_help=False) parser.add_argument("paths", nargs="*", help="files to remove") parser.add_argument("-r", action="store_true", help="recursive") - parser.add_argument("-f", action="store_true", help="force") + parser.add_argument("-f", "--force", action="store_true", help="force") parser.add_argument("-i", action="store_true", help="interactive") - parser.add_argument("--perma", action="store_true", help="permanent delete") - parser.add_argument("--restore", nargs="+", help="restore by id or basename") + parser.add_argument("--skip-trash", action="store_true", help="permanent delete") + parser.add_argument("--restore", nargs="+", metavar="item", help="restore by id or basename") parser.add_argument("-l", action="store_true", help="list trash") parser.add_argument("--empty", action="store_true", help="empty the trash permanently") - parser.add_argument("--help", action="store_true", help="show help") + parser.add_argument("-h", "--help", action="store_true", help="show help") + parser.add_argument("-V", "--version", action="store_true", help="show version") args = parser.parse_args(argv) - # Always print docstring if --help or no args - if args.help or not argv: + # Always print docstring if -h or --help + if args.help: print(__doc__) return + if args.version: + print("resrm 0.2.1") + return + + if not args.paths and not (args.l or args.empty or args.restore): + print("resrm: missing operand") + print("Try 'resrm --help' for more information.") + return + if args.l: list_trash() return @@ -344,4 +356,4 @@ def main(argv: Optional[List[str]] = None): continue print(f"resrm: cannot remove '{pth}': Is a directory") continue - move_to_trash(pth, interactive=args.i, force=args.f, recursive=args.r, perma=args.perma) + move_to_trash(pth, interactive=args.i, force=args.force, skip_trash=args.skip_trash)