"""Resolve postgres-extension library install recipes. Usage: {{ lookup('postgres_libraries', ['vector', 'bloom']) }} Takes a list of extension names and returns the subset that needs a custom library install on top of the official postgres base image (the contrib extensions bloom, pg_trgm and unaccent are already in the base — those are filtered out). Each returned entry is a dict with: - extension: the extension name (matches the SQL identifier) - name: the library/source name (used in the Dockerfile context) - apt_package: the PGDG package suffix, installed as postgresql-- """ from __future__ import annotations from typing import TYPE_CHECKING, Any from ansible.errors import AnsibleError from ansible.plugins.lookup import LookupBase if TYPE_CHECKING: from collections.abc import Sequence _REGISTRY: dict[str, dict[str, str]] = { "postgis": { "name": "postgis", "apt_package": "postgis-3", }, "vector": { "name": "pgvector", "apt_package": "pgvector", }, } class LookupModule(LookupBase): def run( self, terms: Sequence[Any] | None, variables: dict[str, Any] | None = None, **kwargs: Any, ) -> list[Any]: terms = list(terms or []) if len(terms) != 1: raise AnsibleError( "postgres_libraries: expected exactly one term — a list " "of extension names." ) extensions = terms[0] if not isinstance(extensions, (list, tuple)): raise AnsibleError( "postgres_libraries: first term must be a list of extension names." ) recipes: list[dict[str, str]] = [] seen: set[str] = set() for ext in extensions: ext_name = str(ext).strip() if not ext_name or ext_name in seen: continue seen.add(ext_name) recipe = _REGISTRY.get(ext_name) if recipe is not None: recipes.append({"extension": ext_name, **recipe}) return [recipes]