""" Relation registry — declarative entity relationship definitions. Relations are defined as s-expressions using ``defrelation`` and stored in a global registry. All services load the same definitions at startup via ``load_relation_registry()``. """ from __future__ import annotations from shared.sx.types import RelationDef # --------------------------------------------------------------------------- # Registry # --------------------------------------------------------------------------- _RELATION_REGISTRY: dict[str, RelationDef] = {} def register_relation(defn: RelationDef) -> None: """Add a RelationDef to the global registry.""" _RELATION_REGISTRY[defn.name] = defn def get_relation(name: str) -> RelationDef | None: """Look up a relation by name (e.g. ``"page->market"``).""" return _RELATION_REGISTRY.get(name) def relations_from(entity_type: str) -> list[RelationDef]: """All relations where *entity_type* is the ``from`` side.""" return [d for d in _RELATION_REGISTRY.values() if d.from_type == entity_type] def relations_to(entity_type: str) -> list[RelationDef]: """All relations where *entity_type* is the ``to`` side.""" return [d for d in _RELATION_REGISTRY.values() if d.to_type == entity_type] def all_relations() -> list[RelationDef]: """Return all registered relations.""" return list(_RELATION_REGISTRY.values()) def clear_registry() -> None: """Clear all registered relations (for testing).""" _RELATION_REGISTRY.clear() # --------------------------------------------------------------------------- # Built-in relation definitions (s-expression source) # --------------------------------------------------------------------------- _BUILTIN_RELATIONS = ''' (begin (defrelation :page->market :from "page" :to "market" :cardinality :one-to-many :inverse :market->page :nav :submenu :nav-icon "fa fa-shopping-bag" :nav-label "markets") (defrelation :page->calendar :from "page" :to "calendar" :cardinality :one-to-many :inverse :calendar->page :nav :submenu :nav-icon "fa fa-calendar" :nav-label "calendars") (defrelation :post->calendar_entry :from "post" :to "calendar_entry" :cardinality :many-to-many :inverse :calendar_entry->post :nav :inline :nav-icon "fa fa-file-alt" :nav-label "events") (defrelation :page->menu_node :from "page" :to "menu_node" :cardinality :one-to-one :nav :hidden) ) ''' def load_relation_registry() -> None: """Parse built-in defrelation s-expressions and populate the registry.""" from shared.sx.evaluator import evaluate from shared.sx.parser import parse tree = parse(_BUILTIN_RELATIONS) evaluate(tree)