from __future__ import annotations from pathlib import Path from typing import Any, Dict, Optional, Sequence import sys from SYS.logger import log import pipeline as ctx from ._shared import ( Cmdlet, CmdletArg, SharedArgs, normalize_hash, parse_cmdlet_args, normalize_result_input, get_field, should_show_help, ) from Store import Store from SYS.utils import sha256_file class Delete_Note(Cmdlet): def __init__(self) -> None: super().__init__( name="delete-note", summary="Delete a named note from a file in a store.", usage="delete-note -store [-hash ] ", alias=["del-note"], arg=[ SharedArgs.STORE, SharedArgs.HASH, CmdletArg("name", type="string", required=True, description="The note name/key to delete."), ], detail=[ "- Deletes the named note from the selected store backend.", ], exec=self.run, ) try: SharedArgs.STORE.choices = SharedArgs.get_store_choices(None) except Exception: pass self.register() def _resolve_hash(self, raw_hash: Optional[str], raw_path: Optional[str], override_hash: Optional[str]) -> Optional[str]: resolved = normalize_hash(override_hash) if override_hash else normalize_hash(raw_hash) if resolved: return resolved if raw_path: try: p = Path(str(raw_path)) stem = p.stem if len(stem) == 64 and all(c in "0123456789abcdef" for c in stem.lower()): return stem.lower() if p.exists() and p.is_file(): return sha256_file(p) except Exception: return None return None def run(self, result: Any, args: Sequence[str], config: Dict[str, Any]) -> int: if should_show_help(args): log(f"Cmdlet: {self.name}\nSummary: {self.summary}\nUsage: {self.usage}") return 0 parsed = parse_cmdlet_args(args, self) store_override = parsed.get("store") hash_override = parsed.get("hash") note_name_override = str(parsed.get("name") or "").strip() # Allow piping note rows from get-note: the selected item carries note_name. inferred_note_name = str(get_field(result, "note_name") or "").strip() if not note_name_override and not inferred_note_name: log("[delete_note] Error: Requires (or pipe a note row that provides note_name)", file=sys.stderr) return 1 results = normalize_result_input(result) if not results: if store_override and normalize_hash(hash_override): results = [{"store": str(store_override), "hash": normalize_hash(hash_override)}] else: log("[delete_note] Error: Requires piped item(s) or -store and -hash", file=sys.stderr) return 1 store_registry = Store(config) deleted = 0 for res in results: if not isinstance(res, dict): ctx.emit(res) continue # Resolve which note name to delete for this item. note_name = note_name_override or str(res.get("note_name") or "").strip() or inferred_note_name if not note_name: log("[delete_note] Error: Missing note name (pass or pipe a note row)", file=sys.stderr) return 1 store_name = str(store_override or res.get("store") or "").strip() raw_hash = res.get("hash") raw_path = res.get("path") if not store_name: log("[delete_note] Error: Missing -store and item has no store field", file=sys.stderr) return 1 resolved_hash = self._resolve_hash( raw_hash=str(raw_hash) if raw_hash else None, raw_path=str(raw_path) if raw_path else None, override_hash=str(hash_override) if hash_override else None, ) if not resolved_hash: ctx.emit(res) continue try: backend = store_registry[store_name] except Exception as exc: log(f"[delete_note] Error: Unknown store '{store_name}': {exc}", file=sys.stderr) return 1 ok = False try: ok = bool(backend.delete_note(resolved_hash, note_name, config=config)) except Exception as exc: log(f"[delete_note] Error: Failed to delete note: {exc}", file=sys.stderr) ok = False if ok: deleted += 1 ctx.emit(res) log(f"[delete_note] Deleted note on {deleted} item(s)", file=sys.stderr) return 0 if deleted > 0 else 1 CMDLET = Delete_Note()