Files
Medios-Macina/SYS/result_table_adapters.py

81 lines
2.7 KiB
Python

"""Provider registry for ResultTable API (breaking, strict API).
Providers register themselves here with an adapter and optional column factory
and selection function. Consumers (cmdlets) can look up providers by name and
obtain the columns and selection behavior for building tables and for selection
args used by subsequent cmdlets.
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Callable, Dict, Iterable, List, Optional, Union
from SYS.result_table_api import ColumnSpec, ProviderAdapter, ResultModel
ColumnFactory = Callable[[Iterable[ResultModel]], List[ColumnSpec]]
SelectionFn = Callable[[ResultModel], List[str]]
@dataclass
class Provider:
name: str
adapter: ProviderAdapter
# columns can be a static list or a factory that derives columns from sample rows
columns: Optional[Union[List[ColumnSpec], ColumnFactory]] = None
selection_fn: Optional[SelectionFn] = None
metadata: Optional[Dict[str, Any]] = None
def get_columns(self, rows: Optional[Iterable[ResultModel]] = None) -> List[ColumnSpec]:
if callable(self.columns):
try:
rows_list = list(rows) if rows is not None else []
return list(self.columns(rows_list))
except Exception:
# Fall back to a minimal Title column on errors
return [ColumnSpec("title", "Title", lambda r: r.title)]
if self.columns is not None:
return list(self.columns)
# Default minimal column set
return [ColumnSpec("title", "Title", lambda r: r.title)]
def selection_args(self, row: ResultModel) -> List[str]:
if callable(self.selection_fn):
try:
return list(self.selection_fn(row))
except Exception:
return []
# Default selector: prefer path flag, then title
if getattr(row, "path", None):
return ["-path", str(row.path)]
return ["-title", str(row.title)]
_PROVIDERS: Dict[str, Provider] = {}
def register_provider(
name: str,
adapter: ProviderAdapter,
*,
columns: Optional[Union[List[ColumnSpec], ColumnFactory]] = None,
selection_fn: Optional[SelectionFn] = None,
metadata: Optional[Dict[str, Any]] = None,
) -> Provider:
name = str(name or "").strip().lower()
if not name:
raise ValueError("provider name required")
if name in _PROVIDERS:
raise ValueError(f"provider already registered: {name}")
p = Provider(name=name, adapter=adapter, columns=columns, selection_fn=selection_fn, metadata=metadata)
_PROVIDERS[name] = p
return p
def get_provider(name: str) -> Provider:
return _PROVIDERS[name.lower()]
def list_providers() -> List[str]:
return list(_PROVIDERS.keys())