2026-01-05 13:09:24 -08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
2026-01-06 01:38:59 -08:00
|
|
|
import sys
|
|
|
|
|
from typing import Any, Dict, Iterable, Sequence
|
2026-01-05 13:09:24 -08:00
|
|
|
|
|
|
|
|
from . import _shared as sh
|
|
|
|
|
from SYS.logger import log, debug
|
|
|
|
|
from SYS import pipeline as ctx
|
|
|
|
|
|
|
|
|
|
from SYS.result_table_adapters import get_provider
|
|
|
|
|
from SYS.result_table_renderers import RichRenderer
|
|
|
|
|
|
|
|
|
|
Cmdlet = sh.Cmdlet
|
|
|
|
|
CmdletArg = sh.CmdletArg
|
|
|
|
|
parse_cmdlet_args = sh.parse_cmdlet_args
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CMDLET = Cmdlet(
|
|
|
|
|
name="provider-table",
|
|
|
|
|
summary="Render a provider's result set and optionally run a follow-up cmdlet using the selected row.",
|
|
|
|
|
usage="provider-table -provider <name> [-sample] [-select <n>] [-run-cmd <name>]",
|
|
|
|
|
arg=[
|
|
|
|
|
CmdletArg("provider", type="string", description="Provider name to render (default: example)"),
|
|
|
|
|
CmdletArg("sample", type="flag", description="Use provider sample/demo items when available."),
|
|
|
|
|
CmdletArg("select", type="int", description="1-based row index to select and use for follow-up command."),
|
|
|
|
|
CmdletArg("run-cmd", type="string", description="Cmdlet to invoke with the selected row's selector args."),
|
|
|
|
|
],
|
|
|
|
|
detail=[
|
|
|
|
|
"Use a registered provider to build a table and optionally run another cmdlet with selection args.",
|
|
|
|
|
"Emits pipeline-friendly dicts enriched with `_selection_args` so you can pipe into `select` and other cmdlets.",
|
|
|
|
|
"Example: provider-table -provider example -sample | select -select 1 | add-file -store default",
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def _run(result: Any, args: Sequence[str], config: Dict[str, Any]) -> int:
|
|
|
|
|
parsed = parse_cmdlet_args(args, CMDLET)
|
|
|
|
|
|
|
|
|
|
provider_name = parsed.get("provider") or "example"
|
|
|
|
|
use_sample = bool(parsed.get("sample", False))
|
|
|
|
|
run_cmd = parsed.get("run-cmd")
|
|
|
|
|
select_raw = parsed.get("select")
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
provider = get_provider(provider_name)
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
log(f"Unknown provider: {provider_name}", file=sys.stderr)
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
# Obtain items to feed to the adapter
|
|
|
|
|
items = None
|
|
|
|
|
if use_sample:
|
|
|
|
|
# Try to locate SAMPLE_ITEMS in the adapter's module (convention only)
|
|
|
|
|
try:
|
|
|
|
|
mod = __import__(provider.adapter.__module__, fromlist=["*"])
|
|
|
|
|
items = getattr(mod, "SAMPLE_ITEMS", None)
|
|
|
|
|
if items is None:
|
|
|
|
|
log("Provider does not expose SAMPLE_ITEMS; no sample available", file=sys.stderr)
|
|
|
|
|
return 1
|
|
|
|
|
except Exception:
|
|
|
|
|
log("Failed to load provider sample", file=sys.stderr)
|
|
|
|
|
return 1
|
|
|
|
|
else:
|
|
|
|
|
# Require input for non-sample runs
|
|
|
|
|
inputs = list(result) if isinstance(result, Iterable) else []
|
|
|
|
|
if not inputs:
|
|
|
|
|
log("No input provided. Use -sample for demo or pipe provider items in.", file=sys.stderr)
|
|
|
|
|
return 1
|
|
|
|
|
items = inputs
|
|
|
|
|
|
|
|
|
|
try:
|
2026-01-06 01:38:59 -08:00
|
|
|
table = provider.build_table(items)
|
2026-01-05 13:09:24 -08:00
|
|
|
except Exception as exc:
|
2026-01-06 01:38:59 -08:00
|
|
|
log(f"Provider '{provider.name}' failed: {exc}", file=sys.stderr)
|
2026-01-05 13:09:24 -08:00
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
# Emit rows for downstream pipeline consumption (pipable behavior).
|
|
|
|
|
try:
|
2026-01-06 01:38:59 -08:00
|
|
|
for item in provider.serialize_rows(table.rows):
|
2026-01-05 13:09:24 -08:00
|
|
|
try:
|
|
|
|
|
ctx.emit(item)
|
|
|
|
|
except Exception:
|
|
|
|
|
continue
|
|
|
|
|
except Exception:
|
2026-01-06 01:38:59 -08:00
|
|
|
# Non-fatal: rendering still happens
|
2026-01-05 13:09:24 -08:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
|
# Render using RichRenderer
|
|
|
|
|
try:
|
2026-01-06 01:38:59 -08:00
|
|
|
renderable = RichRenderer().render(table.rows, table.columns, table.meta)
|
2026-01-05 13:09:24 -08:00
|
|
|
try:
|
|
|
|
|
from rich.console import Console
|
|
|
|
|
|
2026-01-06 01:38:59 -08:00
|
|
|
Console().print(renderable)
|
2026-01-05 13:09:24 -08:00
|
|
|
except Exception:
|
|
|
|
|
# Fallback to simple printing
|
2026-01-06 01:38:59 -08:00
|
|
|
for r in table.rows:
|
|
|
|
|
print(" ".join(str((c.extractor(r) or "")) for c in table.columns))
|
2026-01-05 13:09:24 -08:00
|
|
|
except Exception as exc:
|
|
|
|
|
log(f"Rendering failed: {exc}", file=sys.stderr)
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
# If no selection requested, we're done
|
|
|
|
|
if not select_raw:
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
try:
|
|
|
|
|
select_idx = int(select_raw) - 1
|
|
|
|
|
except Exception:
|
|
|
|
|
log("Invalid -select value; must be an integer", file=sys.stderr)
|
|
|
|
|
return 1
|
|
|
|
|
|
2026-01-06 01:38:59 -08:00
|
|
|
if select_idx < 0 or select_idx >= len(table.rows):
|
2026-01-05 13:09:24 -08:00
|
|
|
log("-select out of range", file=sys.stderr)
|
|
|
|
|
return 1
|
|
|
|
|
|
2026-01-06 01:38:59 -08:00
|
|
|
selected = table.rows[select_idx]
|
2026-01-05 13:09:24 -08:00
|
|
|
sel_args = provider.selection_args(selected)
|
|
|
|
|
|
|
|
|
|
if not run_cmd:
|
|
|
|
|
# Print selection args for caller
|
|
|
|
|
log(f"Selection args: {sel_args}", file=sys.stderr)
|
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
# Run follow-up cmdlet
|
|
|
|
|
try:
|
|
|
|
|
from cmdlet import ensure_cmdlet_modules_loaded, get as get_cmdlet
|
|
|
|
|
|
|
|
|
|
ensure_cmdlet_modules_loaded()
|
|
|
|
|
cmd_fn = get_cmdlet(run_cmd)
|
|
|
|
|
if not cmd_fn:
|
|
|
|
|
log(f"Follow-up cmdlet not found: {run_cmd}", file=sys.stderr)
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
# Call the cmdlet with no upstream result, but with selection args
|
|
|
|
|
ret = cmd_fn(None, sel_args, config or {})
|
|
|
|
|
return ret
|
|
|
|
|
except Exception as exc:
|
|
|
|
|
log(f"Failed to invoke follow-up cmdlet: {exc}", file=sys.stderr)
|
|
|
|
|
return 1
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
CMDLET.exec = _run
|
|
|
|
|
CMDLET.register()
|