# Copyright 2025 Softwell S.r.l.
# Licensed under the Apache License, Version 2.0
"""AsgiConfigRenderer — object renderer for the server configuration dialect.
Unlike the markup dialects (HTML, SVG) whose render emits a string, this is an
``object`` renderer: a node renders to a ConfigNode carrying its tag, its
already-resolved attributes (the walk resolves ``^pointers`` and data logic)
and its rendered children. The render result is the list of top-level
ConfigNodes, delivered to the server, which configures itself from them.
"""
from __future__ import annotations
from typing import Any
from genro_builders.renderer import RendererBase
[docs]
class ConfigNode:
"""A rendered configuration node: tag, label, resolved attrs, children."""
__slots__ = ("tag", "label", "attrs", "children")
def __init__(
self, tag: str, label: str, attrs: dict[str, Any], children: list[ConfigNode]
) -> None:
self.tag = tag
self.label = label
self.attrs = attrs
self.children = children
[docs]
class AsgiConfigRenderer(RendererBase):
"""Object renderer: a node renders to a ConfigNode with resolved attrs."""
render_type = "object"
mode = "asgi"
[docs]
def rendered_item(
self,
node: Any,
item: Any,
runtime_attrs: dict[str, Any],
*,
tag: str,
**opts: Any,
) -> Any:
"""Build a ConfigNode from the resolved tag/attrs and the children.
``runtime_attrs`` are resolved by the walk (pointers, data logic);
``item`` is the list of child ConfigNodes when the node has children.
"""
children = [c for c in item if isinstance(c, ConfigNode)] if isinstance(item, list) else []
attrs = {k: v for k, v in runtime_attrs.items() if not k.startswith("_")}
return ConfigNode(tag, node.label, attrs, children)
[docs]
def finalize(self, result: Any, target: Any, **_opts: Any) -> Any:
"""Deliver the list of top-level ConfigNodes to the target.
An object dialect does not join fragments into text. ``target`` is the
server: it receives the ConfigNodes via ``apply_configuration`` and
configures itself. ``target=None`` returns the list as-is.
"""
nodes = result if isinstance(result, list) else [result]
if target is None:
return nodes
target.apply_configuration(nodes)
return None
if __name__ == "__main__":
pass