from __future__ import annotations from typing import Any, Dict, List, Sequence import sys from SYS.logger import log from . import _shared as sh Cmdlet = sh.Cmdlet CmdletArg = sh.CmdletArg SharedArgs = sh.SharedArgs class Metadata(Cmdlet): """Unified metadata command with domain + action routing.""" _ACTION_FLAGS = { "add": {"-add", "--add"}, "delete": {"-delete", "--delete", "-del", "--del"}, "get": {"-get", "--get"}, "inspect": {"-inspect", "--inspect", "-info", "--info", "-file", "--file"}, } _DOMAIN_FLAGS = { "tag": {"-tag", "--tag", "-tags", "--tags"}, "url": {"-url", "--url", "-urls", "--urls"}, "relationship": { "-relationship", "--relationship", "-relationships", "--relationships", "-rel", "--rel", }, "note": {"-note", "--note", "-notes", "--notes"}, "file": {"-file", "--file"}, } def __init__(self) -> None: super().__init__( name="metadata", summary="Manage metadata domains with one command", usage='metadata [-tag|-url|-relationship|-note] (-add|-delete|-get) [args] OR metadata -inspect [args]', alias=["meta"], arg=[ SharedArgs.QUERY, SharedArgs.INSTANCE, CmdletArg("-tag", type="flag", required=False, description="Metadata tag domain (default if omitted)"), CmdletArg("-url", type="flag", required=False, description="URL metadata domain"), CmdletArg("-relationship", type="flag", required=False, description="Relationship metadata domain", alias="rel"), CmdletArg("-note", type="flag", required=False, description="Note metadata domain"), CmdletArg("-add", type="flag", required=False, description="Add metadata tag value(s)"), CmdletArg("-delete", type="flag", required=False, description="Delete metadata tag value(s)", alias="del"), CmdletArg("-get", type="flag", required=False, description="Read metadata values for selected domain"), CmdletArg("-inspect", type="flag", required=False, description="Inspect file metadata details", alias="info"), CmdletArg( "[,...]", type="string", required=False, variadic=True, description="Tag values for -add/-delete", ), ], detail=[ "- Use one action flag: -add, -delete, -get, or -inspect.", "- Domain flags: -tag (default), -url, -relationship, -note.", "- Examples:", " metadata -url -add ", " metadata -url -delete ", " metadata -relationship -get", " metadata -note -add -query \"title:lyric text:...\"", "- -inspect maps to get-metadata and prints rich metadata details.", ], exec=self.run, ) self.register() @classmethod def _extract_parts( cls, args: Sequence[str], ) -> tuple[str | None, str, List[str], List[str], List[str]]: matched_actions: List[str] = [] matched_domains: List[str] = [] passthrough: List[str] = [] for token in args or []: text = str(token or "") lower = text.strip().lower() matched = None for action_name, variants in cls._ACTION_FLAGS.items(): if lower in variants: matched = action_name break if matched: matched_actions.append(matched) continue matched_domain = None for domain_name, variants in cls._DOMAIN_FLAGS.items(): if lower in variants: matched_domain = domain_name break if matched_domain: matched_domains.append(matched_domain) continue passthrough.append(text) unique_actions: List[str] = [] for action in matched_actions: if action not in unique_actions: unique_actions.append(action) unique_domains: List[str] = [] for domain in matched_domains: if domain not in unique_domains: unique_domains.append(domain) action = unique_actions[0] if len(unique_actions) == 1 else None domain = unique_domains[0] if len(unique_domains) == 1 else "tag" return action, domain, passthrough, unique_actions, unique_domains def run(self, result: Any, args: Sequence[str], config: Dict[str, Any]) -> int: action, domain, passthrough_args, seen_actions, seen_domains = self._extract_parts(args) if action is None: if not seen_actions: log( "metadata: missing action flag; choose exactly one of -add, -delete, -get, -inspect", file=sys.stderr, ) else: rendered = ", ".join(f"-{name}" for name in seen_actions) log(f"metadata: conflicting actions ({rendered}); choose exactly one", file=sys.stderr) return 1 if len(seen_domains) > 1: rendered_domains = ", ".join(f"-{name}" for name in seen_domains) log(f"metadata: conflicting domains ({rendered_domains}); choose one domain", file=sys.stderr) return 1 if action == "inspect": from cmdlet.metadata.inspect import run_inspect_action return run_inspect_action(result, passthrough_args, config) if domain == "file": log("metadata: -file only supports -inspect; use metadata -inspect", file=sys.stderr) return 1 if domain == "tag": from cmdlet.metadata.tag import run_tag_action return run_tag_action(action, result, passthrough_args, config) if domain == "url": from cmdlet.metadata.url import run_url_action return run_url_action(action, result, passthrough_args, config) if domain == "relationship": from cmdlet.metadata.relationship import run_relationship_action return run_relationship_action(action, result, passthrough_args, config) if domain == "note": from cmdlet.metadata.note import run_note_action return run_note_action(action, result, passthrough_args, config) log(f"metadata: unsupported domain '{domain}'", file=sys.stderr) return 1 CMDLET = Metadata()